-
Notifications
You must be signed in to change notification settings - Fork 404
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Antonio Russo <[email protected]>
- Loading branch information
Showing
33 changed files
with
863 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -81,3 +81,7 @@ sut: | |
settings: | ||
- *new-node-old-grpc | ||
latest: *composer-latest | ||
ethereum: | ||
1.2.1: ðereum-latest | ||
packages: ['[email protected]'] | ||
latest: *ethereum-latest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
# | ||
# 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. | ||
# | ||
|
||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 4 | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[*.md] | ||
trim_trailing_whitespace = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
# | ||
|
||
coverage | ||
node_modules |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
env: | ||
es6: true | ||
node: true | ||
mocha: true | ||
extends: 'eslint:recommended' | ||
parserOptions: | ||
ecmaVersion: 8 | ||
sourceType: | ||
- script | ||
rules: | ||
indent: | ||
- error | ||
- 4 | ||
linebreak-style: | ||
- error | ||
- unix | ||
quotes: | ||
- error | ||
- single | ||
semi: | ||
- error | ||
- always | ||
no-unused-vars: | ||
- error | ||
- args: none | ||
no-console: warn | ||
curly: error | ||
eqeqeq: error | ||
no-throw-literal: error | ||
strict: error | ||
no-var: error | ||
dot-notation: error | ||
no-tabs: error | ||
no-trailing-spaces: error | ||
no-use-before-define: error | ||
no-useless-call: error | ||
no-with: error | ||
operator-linebreak: error | ||
require-jsdoc: | ||
- error | ||
- require: | ||
ClassDeclaration: true | ||
MethodDefinition: true | ||
FunctionDeclaration: true | ||
valid-jsdoc: | ||
- error | ||
- requireReturn: false | ||
yoda: error |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* | ||
* 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. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
module.exports.AdminClient = require('./lib/ethereum'); | ||
module.exports.ClientFactory = require('./lib/ethereumClientFactory'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,244 @@ | ||
/** | ||
* 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. | ||
* | ||
* @file, definition of the Ethereum class, which implements the Caliper's NBI for Ethereum Web3 interface. | ||
*/ | ||
|
||
'use strict'; | ||
|
||
const Web3 = require('web3'); | ||
const {BlockchainInterface, CaliperUtils, TxStatus} = require('@hyperledger/caliper-core'); | ||
const logger = CaliperUtils.getLogger('ethereum.js'); | ||
|
||
/** | ||
* @typedef {Object} EthereumInvoke | ||
* | ||
* @property {string} verb Required. The name of the smart contract function | ||
* @property {string} args Required. Arguments of the smart contract function in the order in which they are defined | ||
* @property {boolean} isView Optional. If method to call is a view. | ||
*/ | ||
|
||
/** | ||
* Implements {BlockchainInterface} for a web3 Ethereum backend. | ||
*/ | ||
class Ethereum extends BlockchainInterface { | ||
|
||
/** | ||
* Create a new instance of the {Ethereum} class. | ||
* @param {string} config_path The path of the network configuration file. | ||
* @param {string} workspace_root The absolute path to the root location for the application configuration files. | ||
*/ | ||
constructor(config_path, workspace_root) { | ||
super(config_path); | ||
this.bcType = 'ethereum'; | ||
this.workspaceRoot = workspace_root; | ||
this.ethereumConfig = require(config_path).ethereum; | ||
this.web3 = new Web3(this.ethereumConfig.url); | ||
this.web3.transactionConfirmationBlocks = this.ethereumConfig.transactionConfirmationBlocks; | ||
} | ||
|
||
/** | ||
* Initialize the {Ethereum} object. | ||
* @return {object} Promise<boolean> True if the account got unlocked successful otherwise false. | ||
*/ | ||
init() { | ||
return this.web3.eth.personal.unlockAccount(this.ethereumConfig.contractDeployerAddress, this.ethereumConfig.contractDeployerAddressPassword, 1000); | ||
} | ||
|
||
/** | ||
* Deploy smart contracts specified in the network configuration file. | ||
* @return {object} Promise execution for all the contract creations. | ||
*/ | ||
async installSmartContract() { | ||
let promises = []; | ||
let self = this; | ||
logger.info('Creating contracts...'); | ||
for (const key of Object.keys(this.ethereumConfig.contracts)) { | ||
let contractData = require(CaliperUtils.resolvePath(this.ethereumConfig.contracts[key].path, this.workspaceRoot)); // TODO remove path property | ||
this.ethereumConfig.contracts[key].abi = contractData.abi; | ||
promises.push(new Promise(async function(resolve, reject) { | ||
let contractInstance = await self.deployContract(contractData); | ||
logger.info('Deployed contract ' + contractData.name + ' at ' + contractInstance.options.address); | ||
self.ethereumConfig.contracts[key].address = contractInstance.options.address; | ||
resolve(contractInstance); | ||
})); | ||
} | ||
return Promise.all(promises); | ||
} | ||
|
||
/** | ||
* Return the Ethereum context associated with the given callback module name. | ||
* @param {string} name The name of the callback module as defined in the configuration files. | ||
* @param {object} args Unused. | ||
* @return {object} The assembled Ethereum context. | ||
* @async | ||
*/ | ||
async getContext(name, args) { | ||
let context = {fromAddress: this.ethereumConfig.fromAddress}; | ||
context.web3 = this.web3; | ||
context.contracts = {}; | ||
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); | ||
return context; | ||
} | ||
|
||
/** | ||
* Release the given Ethereum context. | ||
* @param {object} context The Ethereum context to release. | ||
* @async | ||
*/ | ||
async releaseContext(context) { | ||
// nothing to do | ||
} | ||
|
||
/** | ||
* Invoke a smart contract. | ||
* @param {Object} context Context object. | ||
* @param {String} contractID Identity of the contract. | ||
* @param {String} contractVer Version of the contract. | ||
* @param {EthereumInvoke|EthereumInvoke[]} invokeData Smart contract methods calls. | ||
* @param {Number} timeout Request timeout, in seconds. | ||
* @return {Promise<object>} The promise for the result of the execution. | ||
*/ | ||
async invokeSmartContract(context, contractID, contractVer, invokeData, timeout) { | ||
let invocations; | ||
if (!Array.isArray(invokeData)) { | ||
invocations = [invokeData]; | ||
} else { | ||
invocations = invokeData; | ||
} | ||
let promises = []; | ||
invocations.forEach((item, index) => { | ||
promises.push(this.sendTransaction(context, contractID, contractVer, item, timeout)); | ||
}); | ||
return Promise.all(promises); | ||
} | ||
|
||
/** | ||
* Query a smart contract. | ||
* @param {Object} context Context object. | ||
* @param {String} contractID Identity of the contract. | ||
* @param {String} contractVer Version of the contract. | ||
* @param {EthereumInvoke|EthereumInvoke[]} invokeData Smart contract methods calls. | ||
* @param {Number} timeout Request timeout, in seconds. | ||
* @return {Promise<object>} The promise for the result of the execution. | ||
*/ | ||
async querySmartContract(context, contractID, contractVer, invokeData, timeout) { | ||
let invocations; | ||
if (!Array.isArray(invokeData)) { | ||
invocations = [invokeData]; | ||
} else { | ||
invocations = invokeData; | ||
} | ||
let promises = []; | ||
invocations.forEach((item, index) => { | ||
item.isView = true; | ||
promises.push(this.sendTransaction(context, contractID, contractVer, item, timeout)); | ||
}); | ||
return Promise.all(promises); | ||
} | ||
|
||
/** | ||
* Submit a transaction to the ethereum context. | ||
* @param {Object} context Context object. | ||
* @param {String} contractID Identity of the contract. | ||
* @param {String} contractVer Version of the contract. | ||
* @param {EthereumInvoke} methodCall Methods call data. | ||
* @param {Number} timeout Request timeout, in seconds. | ||
* @return {Promise<TxStatus>} Result and stats of the transaction invocation. | ||
*/ | ||
async sendTransaction(context, contractID, contractVer, methodCall, timeout) { | ||
let status = new TxStatus(); | ||
try { | ||
context.engine.submitCallback(1); | ||
let receipt = null; | ||
let methodType = 'send'; | ||
if (methodCall.isView) { | ||
methodType = 'call'; | ||
} | ||
if (methodCall.args) { | ||
receipt = await context.contracts[contractID].methods[methodCall.verb](...methodCall.args)[methodType]({from: context.fromAddress}); | ||
} else { | ||
receipt = await context.contracts[contractID].methods[methodCall.verb]()[methodType]({from: context.fromAddress}); | ||
} | ||
status.SetID(receipt.transactionHash); | ||
status.SetResult(receipt); | ||
status.SetVerification(true); | ||
status.SetStatusSuccess(); | ||
} catch (err) { | ||
status.SetStatusFail(); | ||
logger.error('Failed tx on ' + contractID + ' calling method ' + methodCall.verb); | ||
logger.error(err); | ||
} | ||
return Promise.resolve(status); | ||
} | ||
|
||
/** | ||
* Query the given smart contract according to the specified options. | ||
* @param {object} context The Ethereum context returned by {getContext}. | ||
* @param {string} contractID The name of the contract. | ||
* @param {string} contractVer The version of the contract. | ||
* @param {string} key The argument to pass to the smart contract query. | ||
* @param {string} [fcn=query] The contract query function name. | ||
* @return {Promise<object>} The promise for the result of the execution. | ||
*/ | ||
async queryState(context, contractID, contractVer, key, fcn = 'query') { | ||
let methodCall = { | ||
verb: fcn, | ||
args: [key], | ||
isView: true | ||
}; | ||
return this.sendTransaction(context, contractID, contractVer, methodCall, 60); | ||
} | ||
|
||
/** | ||
* Deploys a new contract using the given web3 instance | ||
* @param {JSON} contractData Contract data with abi, bytecode and gas properties | ||
* @returns {Promise<web3.eth.Contract>} The deployed contract instance | ||
*/ | ||
deployContract(contractData) { | ||
let web3 = this.web3; | ||
let contractDeployerAddress = this.ethereumConfig.contractDeployerAddress; | ||
return new Promise(function(resolve, reject) { | ||
let contract = new web3.eth.Contract(contractData.abi); | ||
let contractDeploy = contract.deploy({ | ||
data: contractData.bytecode | ||
}); | ||
contractDeploy.send({ | ||
from: contractDeployerAddress, | ||
gas: contractData.gas | ||
}).on('error', (error) => { | ||
reject(error); | ||
}).then((newContractInstance) => { | ||
resolve(newContractInstance); | ||
}); | ||
}); | ||
} | ||
|
||
/** | ||
* It passes deployed contracts addresses to all clients | ||
* @param {Number} number of clients to prepare | ||
* @returns {Array} client args | ||
*/ | ||
async prepareClients(number) { | ||
let result = []; | ||
for (let i = 0 ; i< number ; i++) { | ||
result[i] = {contracts: this.ethereumConfig.contracts}; | ||
} | ||
return result; | ||
} | ||
} | ||
|
||
module.exports = Ethereum; |
Oops, something went wrong.