-
Notifications
You must be signed in to change notification settings - Fork 14
JavaCode
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 | - |
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.
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.
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());
}
}
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.