Skip to content

JavaCode

UrsZeidler edited this page Jun 11, 2017 · 13 revisions

Generated Java Code

Start the code generation per launch configuration.

The java codes uses the eth-contract-api to access and interact with the blockchain. So it supports native ethereumj blockchains and also rcp (throu web3j) based access. So checkout his wiki also. Change or configure the used access implementation in the EthereumInstance.

With the 1.1.2 release the generated java code changed to the eth-propeller-core which uses both implementations eth-propeller-ethj and eth-propeller-web3j . As this is the follow-up project for eth-contract-api most of the code stays the same.

The generated code fall in two parts, the model/deployer and the test code. To use the solidity code in the deployer place the solidity code and or the compiled json in the src/resource folder.

compatibility matrix

uml2solidity eth-contract-api ethereum-propeller
1.1.2 - 0.12
1.1.1 0.16 -
1.1.0 0.16 -
1.0.10 0.16 -
1.0.9 0.16 -
1.0.8 0.9 -

model code

For each contract an a package the corresponding interface is created, it contains the public methods and the enums defined in that contract. For each function in a contract returning more than one value a dataholder class is generated. The naming schema is as following Return_FunctionName_+ the list of in parameter types. See https://github.com/adridadou/eth-contract-api/wiki/Complex-type-mapping for details. The same is true for events defined by the contract. The naming schema for the events is similar but started with Event_eventName.

    public interface ChecksumDatabase{
	/**
	* The name of the tracked product.
	**/
	String name();
	
	String url();
	
	String description();
	
	org.adridadou.ethereum.propeller.values.EthAddress owner();
	
	Integer count();

	/**
	* Add an entry to the database.
	* 
	* @param _version -The version the checksum belongs to.
	* @param _checksum -The checksum of the version.
	**/
	java.util.concurrent.CompletableFuture<Void> addEntry(String _version,String _checksum);
	
	java.util.concurrent.CompletableFuture<Void> changeOwner(org.adridadou.ethereum.propeller.values.EthAddress newOwner);
	
	ReturnGetEntry_string_string_uint getEntry(Integer id);

	//Start of user code additional_methods

	//End of user code
    }

All contracts of an uml package will be generated in the corresponding java package.

contract deployer

For each package a deployer class `[packageName.toUpperFirst()/]Deployer´ is generated containing methods to deploy or instantiate all smart contract defined in this package.

The deployer will be initialized with an EthereumFacade and optional with the contract source or a compiled combined json file. When having several sol files the combined json option is the only working option by now as the way the compiler is used it does not support includes.

Methods for each contract:

  • deploy[contractName] - Deploys the contract on the chain.
  • create[contractName] - Deploys the contract on the chain and create a proxy instance for it.
  • create[contractName]Proxy - Creates a proxy instance for a given address.
  • compiledContract[contractName] - Returns the compiled contract.
  • for each event defined by the contract
    • observeEvent[eventName] - To receive contract events you need to register with these methods.

EthereumInstance

The EthereumInstance is a simple singleton which configured and instantiate the EthereumFacade. You could change the used blockchain implementation via system property EthereumFacadeProvider, adding via -D when starting the testing process for example. Currently these blockchains are supported :

  • main - the main net
  • ropsten - the new testnet
  • test - for the old testnet
  • private - for a private net
  • rcp - to connect to a running rcp instance
    • url - this additional property defines the url of this instance
    • network id (0 for mainnet and 3 for the rposten testnet)
  • InfuraMain - rcp via Infura for the main net
  • InfuraRopsten - rcp via Infura for the ropsten testnet
    • apiKey - the api key
  • custom - a freely configurable chain

Instance matrix:

instance support events customizable access tec
main yes yes ethereumj
ropsten yes yes ethereumj
test yes yes ethereumj
private yes yes ethereumj
rpc ?? no(chain id) web3j
rpc-light no no(chain id) web3j
InfuraMain/Ropsten ?? no web3j
custom yes yes ethereumj
stand alone no yes ethereumj

When setting no provider, the standalone blockchain will be used which is the best case for junit testing as it is so fast. Customize the parameter in the setup() method and use the protected user code parts:

	private void setup() throws Exception {
		String property = System.getProperty("EthereumFacadeProvider");
		if(property!=null){
			if (property.equalsIgnoreCase("main")) {
				BlockchainConfig.Builder mainNet = EthereumJConfigs.mainNet();				
				//Start of user code for setup the main chain
				//End of user code
				ethereum = EthereumFacadeProvider.forNetwork(mainNet).create();
			}else if (property.equalsIgnoreCase("test")) {
				BlockchainConfig.Builder testnet = EthereumJConfigs.etherCampTestnet();
				//Start of user code for setup the test chain
				//End of user code
				ethereum = EthereumFacadeProvider.forNetwork(testnet).create();
			}else if (property.equalsIgnoreCase("ropsten")) {
				BlockchainConfig.Builder ropsten = EthereumJConfigs.ropsten();
				//Start of user code for setup the ropsten chain
				//End of user code
				ethereum = EthereumFacadeProvider.forNetwork(ropsten).create();
			}else if (property.equalsIgnoreCase("InfuraRopsten")) {
				String apiKey = System.getProperty("apiKey");
				ethereum = InfuraRopstenEthereumFacadeProvider.create(new InfuraKey(apiKey));
			}else if (property.equalsIgnoreCase("InfuraMain")) {
				String apiKey = System.getProperty("apiKey");
				ethereum = new InfuraMainEthereumFacadeProvider().create(new InfuraKey(apiKey));
			}else if (property.equalsIgnoreCase("rpc")) {
				EthereumFacadeRpcProvider rcp = new EthereumFacadeRpcProvider();
				String url = System.getProperty("rpc-url");
				String chainId = System.getProperty("chain-id");
				ethereum = rcp.create(url, new ChainId((byte) Integer.parseInt(chainId)));
			}else if (property.equalsIgnoreCase("private")){
				PrivateNetworkConfig config = PrivateNetworkConfig.config();
				//Start of user code for setup the private chain
				config
                //
                .initialBalance(new EthAccount(ECKey.fromPrivate(BigInteger.valueOf(100000L))), EthValue.ether(10L))
                ;

				//End of user code
				ethereum = new PrivateEthereumFacadeProvider().create(config);
			}else if (property.equalsIgnoreCase("custom")){
				BlockchainConfig.Builder config = BlockchainConfig.builder();
				//Start of user code for setup the custom chain
				//End of user code
				ethereum = EthereumFacadeProvider.forNetwork(config).create();
			}
		}
		if(ethereum==null){
			TestConfig.Builder builder = TestConfig.builder();
			//Start of user code for setup the standalone chain
			//End of user code
			ethereum = EthereumFacadeProvider.forTest(builder.build());
		}
	}

junit code

For each contract a Junit Test class is generated and for each public function a test method. When a contract extends an other contract, the test will also extends the other contract test.

For each contract a junit test is created named [aClass.name/]Test, it contains a test method for each public function in the contract. For each package a TestSuite called All[packageName.toUpperFirst()/]TestSuite is created containing all tests of this package.

The naming schema for the test are test[operation.name.toUpperFirst()/][operation.operationParameters()/]. For example testAddEntry_string_string(). All the test functions contains protected code parts where you add your test code. The contract is available in the field fixture.

	/**
	 * Test method for  addEntry.
	 * @throws Exception
	 */
	@Test
	public void testAddEntry_string_string() throws Exception {
		//Start of user code testAddEntry_string_string
        assertEquals(0, fixture.count().intValue());
        fixture.addEntry("aa","jj");
        assertEquals(1, fixture.count().intValue());
        fixture.addEntry("aa","jj");
        assertEquals(2, fixture.count().intValue());
        fixture.addEntry("aa","jj");
        assertEquals(3, fixture.count().intValue());

        //End of user code
	}

The tested contract instance is created in createFixture().

	/**
	 * Create a new fixture by deploying the contract source.
	 * @throws Exception
	 */
	protected void createFixture() throws Exception {
		//Start of user code createFixture
		//CompiledContract compiledContract = ethereum.compile(contractSource, getContractName()).get();
		CompiledContract compiledContract = getCompiledContract("/combined.json");
		String _name = "_name";
		String _url = "_url";
		String _description = "_description";
		CompletableFuture<EthAddress> address = ethereum.publishContract(compiledContract, sender, _name, _url,
				_description);
		fixtureAddress = address.get();
		setFixture(ethereum.createContractProxy(compiledContract, fixtureAddress, sender, ChecksumDatabase.class));
		// End of user code
	}

Where we store the contract address as fixtureAddress and call the polymorph setFixture() method.

All test classes inherit from the AbstractContractTest which supplies some basic methods like loading the contract from a combined json file, it also centralize the initializing of the tests. Loading the combined json (the compiled code) will speed up the test execution a bit, as the code get not compiled each test.

In the initTestMethod you can setup you accounts by loading key files or set accounts. The important account is the sender which is used to create the contracts.

	protected static void initTest() throws Exception {
		// Start of user code AbstractContractTest.initTest

		String property = System.getProperty("EthereumFacadeProvider");
		if (property != null)
			if (property.equalsIgnoreCase("ropsten") ||property.equalsIgnoreCase("rpc")|| property.equalsIgnoreCase("InfuraRopsten")) {
				SecureKey a = AccountProvider.fromKeystore(new File("/home/urs/.ethereum/testnet/keystore/UTC--2015-12-15T13-55-38.006995319Z--ba7b29b63c00dff8614f8d8a6bf34e94e853b2d3"));
				sender = a.decode(System.getProperty("keyPass"));
				senderAddress = sender.getAddress();

				
				
			} else if (property.equalsIgnoreCase("private")) {
				sender = new EthAccount(ECKey.fromPrivate(BigInteger.valueOf(100000L)));
			}

		if (sender == null){// the account for the standalone blockchain
			sender = new EthAccount(
					ECKey.fromPrivate(Hex.decode("3ec771c31cac8c0dba77a69e503765701d3c2bb62435888d4ffa38fed60c445c")));
			senderAddress = sender.getAddress();
		}
		// End of user code
	}

A simple example can be found at https://github.com/UrsZeidler/checksumDatabase and the java example.

Clone this wiki locally