Skip to content

Commit 095bd0b

Browse files
emizzle0x-r4bbit
authored andcommitted
feat(@embark/quorum): Add support for Quorum blockchains
Add support for *connecting to* Quorum blockchains. This plugin will not start a Quorum node or nodes automatically as Embark does with other chains. This plugins supports deploying contracts publically and privately using the Tessera private transaction manager. This plugin supports sending of public and private transactions using the Tessera private transaction manager. Add ability to skip bytecode checking as part of the contract deployment process. Instruct the deployer to skip checking if the contract bytecode exists on-chain before deploying the contract. This is important in the case of having many private nodes in a network because if a contract is deployed privately to node 1 and 7, running Embark on node 2 should skip the bytecode check as the contract *is not* deployed on node 2, nor do we want it deployed on node 2. If the bytecode check was in place, Embark would have deployed it to node 2 and therefore not adhered to the privacy needs. Add Ethereum contract deployer for Quorum, allowing for deploying of public and private contracts using `privateFor` and `privateFrom` (see Contract config updates below). Add web3 extensions enabling specific functionality for Quorum. Extensions includes those provided by [`quorum-js`](https://github.com/jpmorganchase/quorum.js), as well as some custom monkeypatches that override web3 method output formatting, including: - web3.eth.getBlock - web3.eth.getTransaction - web3.eth.getTransactionReceipt - web3.eth.decodeParameters DApps wishing to take advantage of these overrides will need to patch web3 as follows: ``` import {patchWeb3} from "embark-quorum"; import Web3 from "web3"; let web3 = new Web3(...); web3 = patchWeb3(web3); ``` Add support for sending a raw private transaction in the Quorum network. This includes running actions from the proxy after an `eth_sendTransaction` RPC request has been transformed in to `eth_sendRawTransaction` after being signed. fix(@embark/transaction-logger): Fix bug when sending a 0-value transaction. Add `originalRequest` to the proxy when modifying `eth_sendTransaction` to `eth_sendRawTransaction`, so that the original transaction parameters (including `privateFor` and `privateFrom`) can be used to sign a raw private transaction in the `eth_sendRawTransaction` action. Added the following properties on to blockchain config: - *`client`* `{boolean}` - Allows `quorum` to be specified as the blockchain client - *`clientConfig/tesseraPrivateUrl`* `{string}` - URL of the Tessera private transaction manager ``` client: "quorum", clientConfig: { tesseraPrivateUrl: "http://localhost:9081" // URL of the Tessera private transaction manager } ``` Added the following properties to the contracts config: - *`skipBytecodeCheck`* `{boolean}` - Instructs the deployer to skip checking if the bytecode of the contract exists on the chain before deploying the contract. This is important in the case of having many private nodes in a network because if a contract is deployed privately to node 1 and 7, running Embark on node 2 should skip the bytecode check as the contract *is not* deployed on node 2, nor do we want it deployed on node 2. If the bytecode check was in place, Embark would have deployed it to node 2 and therefore not adhered to the privacy needs. - *`privateFor`* `{string[]}` - When sending a private transaction, an array of the recipient nodes' base64-encoded public keys. - *`privateFrom`* `{string}` - When sending a private transaction, the sending party's base64-encoded public key to use ``` environment: { deploy: { SimpleStorage: { skipBytecodeCheck: true, privateFor: ["ROAZBWtSacxXQrOe3FGAqJDyJjFePR5ce4TSIzmJ0Bc"], privateFrom: "BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo=" } } }, ``` - *`proxy:endpoint:http:get`* - get the HTTP endpoint of the proxy regardless of blockchain settings - *`proxy:endpoint:ws:get`* - get the WS endpoint of the proxy regardless of blockchain settings - *`runcode:register:<variable>`* - when variables are registered in the console using `runcode:register`, actions with the name of the variable (ie `runcode:register:web3`) will be run *before* the variable is actually registered in the console. This allows a variable to be modified by plugins before being registered in the console.
1 parent 0c9b917 commit 095bd0b

File tree

30 files changed

+1075
-44
lines changed

30 files changed

+1075
-44
lines changed

packages/core/code-runner/src/index.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class CodeRunner {
1111
private events: EmbarkEvents;
1212
private vm: VM;
1313

14-
constructor(embark: Embark, _options: any) {
14+
constructor(private embark: Embark, _options: any) {
1515
this.logger = embark.logger;
1616
this.events = embark.events;
1717

@@ -49,8 +49,13 @@ class CodeRunner {
4949
cb();
5050
}
5151

52-
private registerVar(varName: string, code: any, cb = () => { }) {
53-
this.vm.registerVar(varName, code, cb);
52+
private registerVar(varName: string, code: any, cb = (...args) => { }) {
53+
this.embark.config.plugins.emitAndRunActionsForEvent<any>(`runcode:register:${varName}`, code, (err, updated) => {
54+
if (err) {
55+
return cb(err);
56+
}
57+
this.vm.registerVar(varName, updated, cb);
58+
});
5459
}
5560

5661
private evalCode(code: string, cb: Callback<any>, tolerateError = false, logCode = true, logError = true) {

packages/core/core/constants.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@
4141
"clients": {
4242
"geth": "geth",
4343
"parity": "parity",
44-
"ganache": "ganache-cli"
44+
"ganache": "ganache-cli",
45+
"nethermind": "nethermind",
46+
"quorum": "quorum"
4547
},
4648
"defaultMnemonic": "example exile argue silk regular smile grass bomb merge arm assist farm",
4749
"blockchainReady": "blockchainReady",
@@ -131,4 +133,4 @@
131133
},
132134
"generationDir": "embarkArtifacts"
133135
}
134-
}
136+
}

packages/core/core/src/index.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@ export interface Contract {
1515

1616
export interface ContractConfig {
1717
address?: string;
18-
args?: any[];
18+
args?: any;
1919
instanceOf?: string;
2020
gas?: number;
2121
gasPrice?: number;
2222
silent?: boolean;
2323
track?: boolean;
2424
deploy?: boolean;
25+
skipBytecodeCheck?: boolean;
2526
}
2627

2728
export interface Plugin {
@@ -35,6 +36,11 @@ export interface EmbarkPlugins {
3536
getPluginsProperty(pluginType: string, property: string, sub_property?: string): any[];
3637
plugins: Plugin[];
3738
runActionsForEvent(event: string, args: any, cb: Callback<any>): void;
39+
emitAndRunActionsForEvent<T>(
40+
name: string,
41+
params: any,
42+
cb: Callback<T>
43+
): void;
3844
}
3945

4046
export interface CompilerPluginObject {
@@ -69,6 +75,14 @@ export interface EmbarkEvents {
6975
): void;
7076
}
7177

78+
export interface ClientConfig {
79+
miningMode?: "dev" | "auto" | "always" | "off";
80+
}
81+
82+
export interface ContractsConfig {
83+
[key: string]: ContractConfig;
84+
}
85+
7286
export interface Configuration {
7387
contractsFiles: any[];
7488
embarkConfig: _EmbarkConfig;
@@ -86,9 +100,7 @@ export interface Configuration {
86100
isDev: boolean;
87101
client: string;
88102
enabled: boolean;
89-
clientConfig: {
90-
miningMode: string
91-
}
103+
clientConfig?: ClientConfig;
92104
};
93105
webServerConfig: {
94106
certOptions: {
@@ -98,6 +110,7 @@ export interface Configuration {
98110
};
99111
contractsConfig: {
100112
tracking?: boolean | string;
113+
contracts: ContractsConfig;
101114
};
102115
plugins: EmbarkPlugins;
103116
reloadConfig(): void;

packages/plugins/deploy-tracker/src/deploymentChecks.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,12 @@ export default class DeploymentChecks {
9292
catch (err) {
9393
return cb(err);
9494
}
95-
if (codeInChain.length > 3 && codeInChain.substring(2) === contract.runtimeBytecode) { // it is "0x" or "0x0" for empty code, depending on web3 version
95+
const skipBytecodeCheck = (this.contractsConfig?.contracts && this.contractsConfig.contracts[params.contract.className]?.skipBytecodeCheck) ?? false;
96+
if (skipBytecodeCheck) {
97+
this.logger.warn(__("WARNING: Skipping bytecode check for %s deployment. Performing an embark reset may cause the contract to be re-deployed to the current node regardless if it was already deployed on another node in the network.", params.contract.className));
98+
}
99+
if (skipBytecodeCheck ||
100+
(codeInChain.length > 3 && codeInChain.substring(2) === contract.runtimeBytecode)) { // it is "0x" or "0x0" for empty code, depending on web3 version
96101
contract.deployedAddress = trackedContract.address;
97102
contract.log(contract.className.bold.cyan + __(" already deployed at ").green + contract.deployedAddress.bold.cyan);
98103
params.shouldDeploy = false;

packages/plugins/deploy-tracker/src/test/deploymentChecksSpec.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ describe('embark.deploymentChecks', function () {
2222
let readJSON;
2323
let writeJSON;
2424
let _web3;
25+
let contractsConfig;
2526

2627
beforeEach(() => {
2728
params = {
@@ -77,7 +78,8 @@ describe('embark.deploymentChecks', function () {
7778
}
7879
};
7980
trackingFunctions._web3 = _web3;
80-
deploymentChecks = new DeploymentChecks({trackingFunctions, events, logger, contractsConfig: {}});
81+
contractsConfig = {};
82+
deploymentChecks = new DeploymentChecks({ trackingFunctions, events, logger, contractsConfig });
8183
deploymentChecks._web3 = _web3;
8284
});
8385
afterEach(() => {
@@ -175,6 +177,20 @@ describe('embark.deploymentChecks', function () {
175177
expect(params.contract.deployedAddress).to.be("0xbe474fb88709f99Ee83901eE09927005388Ab2F1");
176178
});
177179
});
180+
it("should not deploy if contract is tracked and bytecode check is skipped", async function () {
181+
deploymentChecks.contractsConfig = {
182+
contracts: {
183+
TestContract: {
184+
skipBytecodeCheck: true
185+
}
186+
}
187+
};
188+
return deploymentChecks.checkIfAlreadyDeployed(params, (err, params) => {
189+
expect(err).to.be(null);
190+
expect(params.shouldDeploy).to.be(false);
191+
expect(params.contract.deployedAddress).to.be("0xbe474fb88709f99Ee83901eE09927005388Ab2F1");
192+
});
193+
});
178194
it("should deploy if contract is tracked, but bytecode doesn't exist on chain", async function () {
179195
trackingFunctions._web3.eth.getCode = () => "0x0";
180196
return deploymentChecks.checkIfAlreadyDeployed(params, (err, params) => {

packages/plugins/ens/src/index.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,11 +320,21 @@ class ENS {
320320
const registration = this.config.namesystemConfig.register;
321321
const doRegister = registration && registration.rootDomain;
322322

323-
await this.events.request2('deployment:contract:deploy', this.ensConfig.ENSRegistry);
323+
try {
324+
await this.events.request2('deployment:contract:deploy', this.ensConfig.ENSRegistry);
325+
} catch (err) {
326+
this.logger.error(__(`Error deploying the ENS Registry contract: ${err.message}`));
327+
this.logger.debug(err.stack);
328+
}
324329
// Add Resolver to contract manager again but this time with correct arguments (Registry address)
325330
this.ensConfig.Resolver.args = [this.ensConfig.ENSRegistry.deployedAddress];
326331
this.ensConfig.Resolver = await this.events.request2('contracts:add', this.ensConfig.Resolver);
327-
await this.events.request2('deployment:contract:deploy', this.ensConfig.Resolver);
332+
try {
333+
await this.events.request2('deployment:contract:deploy', this.ensConfig.Resolver);
334+
} catch (err) {
335+
this.logger.error(__(`Error deploying the ENS Resolver contract: ${err.message}`));
336+
this.logger.debug(err.stack);
337+
}
328338

329339
const config = {
330340
registryAbi: self.ensConfig.ENSRegistry.abiDefinition,

packages/plugins/ethereum-blockchain-client/src/index.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ class EthereumBlockchainClient {
6060
return web3.currentProvider;
6161
}
6262

63-
async deployer(contract, done) {
63+
async deployer(contract, additionalDeployParams, done) {
6464
try {
6565
const web3 = await this.web3;
6666
const [account] = await web3.eth.getAccounts();
@@ -88,7 +88,7 @@ class EthereumBlockchainClient {
8888
}
8989

9090
embarkJsUtils.secureSend(web3, contractObject, {
91-
from: account, gas: contract.gas
91+
from: account, gas: contract.gas, ...additionalDeployParams
9292
}, true, (err, receipt) => {
9393
if (err) {
9494
return done(err);
@@ -199,7 +199,7 @@ class EthereumBlockchainClient {
199199
if (Array.isArray(arg)) {
200200
return checkArgs(arg, nextEachCb);
201201
}
202-
if (arg[0] === "$") {
202+
if (arg && arg[0] === "$") {
203203
return parseArg(arg, nextEachCb);
204204
}
205205
nextEachCb(null, arg);

packages/plugins/nethermind/src/index.js

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ import { __ } from 'embark-i18n';
22
import {BlockchainClient} from "./blockchain";
33
const {normalizeInput, testRpcWithEndpoint, testWsEndpoint} = require('embark-utils');
44
import {BlockchainProcessLauncher} from './blockchainProcessLauncher';
5-
6-
export const NETHERMIND_NAME = 'nethermind';
5+
import constants from "embark-core/constants";
76

87
class Nethermind {
98
constructor(embark) {
@@ -20,7 +19,7 @@ class Nethermind {
2019
return;
2120
}
2221

23-
this.events.request("blockchain:node:register", NETHERMIND_NAME, {
22+
this.events.request("blockchain:node:register", constants.blockchain.clients.nethermind, {
2423
isStartedFn: (isStartedCb) => {
2524
this._doCheck((state) => {
2625
console.log('Started?', JSON.stringify(state));
@@ -53,15 +52,15 @@ class Nethermind {
5352

5453
shouldInit() {
5554
return (
56-
this.blockchainConfig.client === NETHERMIND_NAME &&
55+
this.blockchainConfig.client === constants.blockchain.clients.nethermind &&
5756
this.blockchainConfig.enabled
5857
);
5958
}
6059

6160
_getNodeState(err, version, cb) {
6261
if (err) return cb({ name: "Ethereum node not found", status: 'off' });
6362

64-
return cb({ name: `${NETHERMIND_NAME} (Ethereum)`, status: 'on' });
63+
return cb({ name: `${constants.blockchain.clients.nethermind} (Ethereum)`, status: 'on' });
6564
}
6665

6766
_doCheck(cb) {
@@ -78,7 +77,7 @@ class Nethermind {
7877
startBlockchainNode(callback) {
7978
if (this.blockchainConfig.isStandalone) {
8079
return new BlockchainClient(this.blockchainConfig, {
81-
clientName: NETHERMIND_NAME,
80+
clientName: constants.blockchain.clients.nethermind,
8281
env: this.embark.config.env,
8382
certOptions: this.embark.config.webServerConfig.certOptions,
8483
logger: this.logger,

packages/plugins/quorum/.npmrc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
engine-strict = true
2+
package-lock = false
3+
save-exact = true
4+
scripts-prepend-node-path = true

0 commit comments

Comments
 (0)