From 1469dc3f8dfba7d993a8f5f0f5adf88055fcaf70 Mon Sep 17 00:00:00 2001 From: Danno Ferrin Date: Thu, 24 Oct 2019 05:07:51 -0600 Subject: [PATCH] Add Support for Hyperledger Besu Most of the existing ethereum adapter works fine for Besu except for two major issues: Besu does not have wallet support (and never will as an architectual choice) and hence the automatic management of nonces no longer works. First, we add two new config vars contractDeployerAddressPrivateKey and fromAddressPrivateKey and if present prefer the 'raw' transaciton APIs. Second in the main transaction loop we track the nonce of the current transaction so this can set the nonces appropriately. Finally we add in gas estimation rather than a hard coded gas number so that blocks can be as full as possible. Besu has a slightly different genesis config from geth, so a besu config directory in the samples is added. The samples network only uses Clique right now because PoW is not something enterprises should be deploying. Signed-off-by: Danno Ferrin --- packages/caliper-ethereum/lib/ethereum.js | 27 +++++++++++++--- .../network/besu/1node-clique/Dockerfile | 16 ++++++++++ .../besu/1node-clique/data/genesis.json | 31 +++++++++++++++++++ .../besu/1node-clique/docker-compose.yml | 25 +++++++++++++++ .../network/besu/1node-clique/ethereum.json | 22 +++++++++++++ .../network/besu/1node-clique/keys/key | 1 + packages/caliper-samples/package.json | 4 ++- 7 files changed, 120 insertions(+), 6 deletions(-) create mode 100644 packages/caliper-samples/network/besu/1node-clique/Dockerfile create mode 100644 packages/caliper-samples/network/besu/1node-clique/data/genesis.json create mode 100644 packages/caliper-samples/network/besu/1node-clique/docker-compose.yml create mode 100644 packages/caliper-samples/network/besu/1node-clique/ethereum.json create mode 100644 packages/caliper-samples/network/besu/1node-clique/keys/key diff --git a/packages/caliper-ethereum/lib/ethereum.js b/packages/caliper-ethereum/lib/ethereum.js index 50fb1e12d..738c9c69b 100644 --- a/packages/caliper-ethereum/lib/ethereum.js +++ b/packages/caliper-ethereum/lib/ethereum.js @@ -52,7 +52,11 @@ class Ethereum extends BlockchainInterface { * @return {object} Promise True if the account got unlocked successful otherwise false. */ init() { - return this.web3.eth.personal.unlockAccount(this.ethereumConfig.contractDeployerAddress, this.ethereumConfig.contractDeployerAddressPassword, 1000); + if (this.ethereumConfig.contractDeployerAddressPrivateKey) { + this.web3.eth.accounts.wallet.add(this.ethereumConfig.contractDeployerAddressPrivateKey); + } else if (this.ethereumConfig.contractDeployerAddressPassword) { + return this.web3.eth.personal.unlockAccount(this.ethereumConfig.contractDeployerAddress, this.ethereumConfig.contractDeployerAddressPassword, 1000); + } } /** @@ -90,7 +94,13 @@ class Ethereum extends BlockchainInterface { for (const key of Object.keys(args.contracts)) { context.contracts[key] = new this.web3.eth.Contract(args.contracts[key].abi, args.contracts[key].address); } - await context.web3.eth.personal.unlockAccount(this.ethereumConfig.fromAddress, this.ethereumConfig.fromAddressPassword, 1000); + context.nonces = {}; + context.nonces[this.ethereumConfig.fromAddress] = await this.web3.eth.getTransactionCount(this.ethereumConfig.fromAddress); + if (this.ethereumConfig.fromAddressPrivateKey) { + this.web3.eth.accounts.wallet.add(this.ethereumConfig.fromAddressPrivateKey); + } else if (this.ethereumConfig.fromAddressPassword) { + await context.web3.eth.personal.unlockAccount(this.ethereumConfig.fromAddress, this.ethereumConfig.fromAddressPassword, 1000); + } return context; } @@ -161,17 +171,24 @@ class Ethereum extends BlockchainInterface { */ async sendTransaction(context, contractID, contractVer, methodCall, timeout) { let status = new TxStatus(); + let params = {from: context.fromAddress}; try { context.engine.submitCallback(1); let receipt = null; let methodType = 'send'; if (methodCall.isView) { methodType = 'call'; + } else { + let nonce = context.nonces[context.fromAddress]; + context.nonces[context.fromAddress] = nonce + 1; + params.nonce = nonce; } if (methodCall.args) { - receipt = await context.contracts[contractID].methods[methodCall.verb](...methodCall.args)[methodType]({from: context.fromAddress}); + params.gas = 1000 + await context.contracts[contractID].methods[methodCall.verb](...methodCall.args).estimateGas(); + receipt = await context.contracts[contractID].methods[methodCall.verb](...methodCall.args)[methodType](params); } else { - receipt = await context.contracts[contractID].methods[methodCall.verb]()[methodType]({from: context.fromAddress}); + params.gas = 1000 + await context.contracts[contractID].methods[methodCall.verb].estimateGas(params); + receipt = await context.contracts[contractID].methods[methodCall.verb]()[methodType](params); } status.SetID(receipt.transactionHash); status.SetResult(receipt); @@ -179,7 +196,7 @@ class Ethereum extends BlockchainInterface { status.SetStatusSuccess(); } catch (err) { status.SetStatusFail(); - logger.error('Failed tx on ' + contractID + ' calling method ' + methodCall.verb); + logger.error('Failed tx on ' + contractID + ' calling method ' + methodCall.verb + ' nonce ' + params.nonce); logger.error(err); } return Promise.resolve(status); diff --git a/packages/caliper-samples/network/besu/1node-clique/Dockerfile b/packages/caliper-samples/network/besu/1node-clique/Dockerfile new file mode 100644 index 000000000..395046028 --- /dev/null +++ b/packages/caliper-samples/network/besu/1node-clique/Dockerfile @@ -0,0 +1,16 @@ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM hyperledger/besu:latest +COPY ./data/ /root/ +VOLUME [ "/root/.ethereum/keystore/" ] +ENTRYPOINT [ "/opt/besu/bin/besu", "--genesis-file=/root/genesis.json", "--node-private-key-file=/root/.ethereum/keystore/key" ] diff --git a/packages/caliper-samples/network/besu/1node-clique/data/genesis.json b/packages/caliper-samples/network/besu/1node-clique/data/genesis.json new file mode 100644 index 000000000..565f5b0d6 --- /dev/null +++ b/packages/caliper-samples/network/besu/1node-clique/data/genesis.json @@ -0,0 +1,31 @@ +{ + "config": { + "chainId": 48122, + "homesteadBlock": 1, + "eip150Block": 2, + "eip150Hash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "eip155Block": 3, + "eip158Block": 3, + "byzantiumBlock": 4, + "constantinopleBlock": 5, + "clique": { + "blockperiodseconds": 5, + "epoch": 30000 + } + }, + "nonce": "0x0", + "timestamp": "0x5ca916c6", + "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000c0A8e4D217eB85b812aeb1226fAb6F588943C2C20000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "gasLimit": "0x47b760", + "difficulty": "0x1", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0xc0A8e4D217eB85b812aeb1226fAb6F588943C2C2", + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "alloc": { + "0xc0A8e4D217eB85b812aeb1226fAb6F588943C2C2": { + "balance": "0x200000000000000000000000000000000000000000000000000000000000000" + } + } +} diff --git a/packages/caliper-samples/network/besu/1node-clique/docker-compose.yml b/packages/caliper-samples/network/besu/1node-clique/docker-compose.yml new file mode 100644 index 000000000..cc0fca116 --- /dev/null +++ b/packages/caliper-samples/network/besu/1node-clique/docker-compose.yml @@ -0,0 +1,25 @@ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +version: "3" +services: + node: + build: . + image: caliper-besu-clique + container_name: besu_clique + volumes: + - ./keys:/root/.ethereum/keystore + ports: + - 8545-8547:8545-8547 + command: --revert-reason-enabled --rpc-http-enabled --rpc-http-host 0.0.0.0 --host-whitelist=* --rpc-http-apis admin,eth,miner,web3,net --graphql-http-enabled --discovery-enabled=false \ No newline at end of file diff --git a/packages/caliper-samples/network/besu/1node-clique/ethereum.json b/packages/caliper-samples/network/besu/1node-clique/ethereum.json new file mode 100644 index 000000000..c65f1db10 --- /dev/null +++ b/packages/caliper-samples/network/besu/1node-clique/ethereum.json @@ -0,0 +1,22 @@ +{ + "caliper": { + "blockchain": "ethereum", + "command" : { + "start": "docker-compose -f network/besu/1node-clique/docker-compose.yml up -d && sleep 10", + "end" : "docker-compose -f network/besu/1node-clique/docker-compose.yml down" + } + }, + "ethereum": { + "url": "http://localhost:8545", + "contractDeployerAddress": "0xc0A8e4D217eB85b812aeb1226fAb6F588943C2C2", + "contractDeployerAddressPrivateKey": "0xa67bee5d6fb43acd42e307eb67547ab5006ad2fbb9567829e9b4b2ef3580acea", + "fromAddress": "0xc0A8e4D217eB85b812aeb1226fAb6F588943C2C2", + "fromAddressPrivateKey": "0xa67bee5d6fb43acd42e307eb67547ab5006ad2fbb9567829e9b4b2ef3580acea", + "transactionConfirmationBlocks": 2, + "contracts": { + "simple": { + "path": "src/contract/ethereum/simple/simple.json" + } + } + } +} \ No newline at end of file diff --git a/packages/caliper-samples/network/besu/1node-clique/keys/key b/packages/caliper-samples/network/besu/1node-clique/keys/key new file mode 100644 index 000000000..ba5d3af1a --- /dev/null +++ b/packages/caliper-samples/network/besu/1node-clique/keys/key @@ -0,0 +1 @@ +0xa67bee5d6fb43acd42e307eb67547ab5006ad2fbb9567829e9b4b2ef3580acea \ No newline at end of file diff --git a/packages/caliper-samples/package.json b/packages/caliper-samples/package.json index c1a50381a..2eba07fb1 100644 --- a/packages/caliper-samples/package.json +++ b/packages/caliper-samples/package.json @@ -30,8 +30,9 @@ "coverage", "log", "benchmark/smallbank/.Rhistory", + "network/besu/1node-clique/keys", + "network/besu/1node-clique/Dockerfile", "network/burrow/simple/chain/keys/names", - "network/sawtooth/simplenetwork/Dockerfile", "network/ethereum/1node/keys", "network/ethereum/1node/Dockerfile", "network/ethereum/1node-clique/keys", @@ -44,6 +45,7 @@ "network/fabric-v1.4/kafka/config/.gitignore", "network/fabric-v1.4.1/config/.gitignore", "network/fabric-v1.4.1/raft/config/.gitignore", + "network/sawtooth/simplenetwork/Dockerfile", "src/contract/sawtooth/simple/simple_python/simple-tp-python", "src/contract/sawtooth/simple/simple_python/packaging/systemd/sawtooth-simple-tp-python", "src/contract/sawtooth/docker/sawtooth-int-simple-tp-python"