Skip to content

Commit

Permalink
feat(@embark/quorum): Add support for Quorum blockchains
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
emizzle authored and 0x-r4bbit committed Mar 23, 2020
1 parent 0c9b917 commit 095bd0b
Show file tree
Hide file tree
Showing 30 changed files with 1,075 additions and 44 deletions.
11 changes: 8 additions & 3 deletions packages/core/code-runner/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class CodeRunner {
private events: EmbarkEvents;
private vm: VM;

constructor(embark: Embark, _options: any) {
constructor(private embark: Embark, _options: any) {
this.logger = embark.logger;
this.events = embark.events;

Expand Down Expand Up @@ -49,8 +49,13 @@ class CodeRunner {
cb();
}

private registerVar(varName: string, code: any, cb = () => { }) {
this.vm.registerVar(varName, code, cb);
private registerVar(varName: string, code: any, cb = (...args) => { }) {
this.embark.config.plugins.emitAndRunActionsForEvent<any>(`runcode:register:${varName}`, code, (err, updated) => {
if (err) {
return cb(err);
}
this.vm.registerVar(varName, updated, cb);
});
}

private evalCode(code: string, cb: Callback<any>, tolerateError = false, logCode = true, logError = true) {
Expand Down
6 changes: 4 additions & 2 deletions packages/core/core/constants.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@
"clients": {
"geth": "geth",
"parity": "parity",
"ganache": "ganache-cli"
"ganache": "ganache-cli",
"nethermind": "nethermind",
"quorum": "quorum"
},
"defaultMnemonic": "example exile argue silk regular smile grass bomb merge arm assist farm",
"blockchainReady": "blockchainReady",
Expand Down Expand Up @@ -131,4 +133,4 @@
},
"generationDir": "embarkArtifacts"
}
}
}
21 changes: 17 additions & 4 deletions packages/core/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,14 @@ export interface Contract {

export interface ContractConfig {
address?: string;
args?: any[];
args?: any;
instanceOf?: string;
gas?: number;
gasPrice?: number;
silent?: boolean;
track?: boolean;
deploy?: boolean;
skipBytecodeCheck?: boolean;
}

export interface Plugin {
Expand All @@ -35,6 +36,11 @@ export interface EmbarkPlugins {
getPluginsProperty(pluginType: string, property: string, sub_property?: string): any[];
plugins: Plugin[];
runActionsForEvent(event: string, args: any, cb: Callback<any>): void;
emitAndRunActionsForEvent<T>(
name: string,
params: any,
cb: Callback<T>
): void;
}

export interface CompilerPluginObject {
Expand Down Expand Up @@ -69,6 +75,14 @@ export interface EmbarkEvents {
): void;
}

export interface ClientConfig {
miningMode?: "dev" | "auto" | "always" | "off";
}

export interface ContractsConfig {
[key: string]: ContractConfig;
}

export interface Configuration {
contractsFiles: any[];
embarkConfig: _EmbarkConfig;
Expand All @@ -86,9 +100,7 @@ export interface Configuration {
isDev: boolean;
client: string;
enabled: boolean;
clientConfig: {
miningMode: string
}
clientConfig?: ClientConfig;
};
webServerConfig: {
certOptions: {
Expand All @@ -98,6 +110,7 @@ export interface Configuration {
};
contractsConfig: {
tracking?: boolean | string;
contracts: ContractsConfig;
};
plugins: EmbarkPlugins;
reloadConfig(): void;
Expand Down
7 changes: 6 additions & 1 deletion packages/plugins/deploy-tracker/src/deploymentChecks.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,12 @@ export default class DeploymentChecks {
catch (err) {
return cb(err);
}
if (codeInChain.length > 3 && codeInChain.substring(2) === contract.runtimeBytecode) { // it is "0x" or "0x0" for empty code, depending on web3 version
const skipBytecodeCheck = (this.contractsConfig?.contracts && this.contractsConfig.contracts[params.contract.className]?.skipBytecodeCheck) ?? false;
if (skipBytecodeCheck) {
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));
}
if (skipBytecodeCheck ||
(codeInChain.length > 3 && codeInChain.substring(2) === contract.runtimeBytecode)) { // it is "0x" or "0x0" for empty code, depending on web3 version
contract.deployedAddress = trackedContract.address;
contract.log(contract.className.bold.cyan + __(" already deployed at ").green + contract.deployedAddress.bold.cyan);
params.shouldDeploy = false;
Expand Down
18 changes: 17 additions & 1 deletion packages/plugins/deploy-tracker/src/test/deploymentChecksSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ describe('embark.deploymentChecks', function () {
let readJSON;
let writeJSON;
let _web3;
let contractsConfig;

beforeEach(() => {
params = {
Expand Down Expand Up @@ -77,7 +78,8 @@ describe('embark.deploymentChecks', function () {
}
};
trackingFunctions._web3 = _web3;
deploymentChecks = new DeploymentChecks({trackingFunctions, events, logger, contractsConfig: {}});
contractsConfig = {};
deploymentChecks = new DeploymentChecks({ trackingFunctions, events, logger, contractsConfig });
deploymentChecks._web3 = _web3;
});
afterEach(() => {
Expand Down Expand Up @@ -175,6 +177,20 @@ describe('embark.deploymentChecks', function () {
expect(params.contract.deployedAddress).to.be("0xbe474fb88709f99Ee83901eE09927005388Ab2F1");
});
});
it("should not deploy if contract is tracked and bytecode check is skipped", async function () {
deploymentChecks.contractsConfig = {
contracts: {
TestContract: {
skipBytecodeCheck: true
}
}
};
return deploymentChecks.checkIfAlreadyDeployed(params, (err, params) => {
expect(err).to.be(null);
expect(params.shouldDeploy).to.be(false);
expect(params.contract.deployedAddress).to.be("0xbe474fb88709f99Ee83901eE09927005388Ab2F1");
});
});
it("should deploy if contract is tracked, but bytecode doesn't exist on chain", async function () {
trackingFunctions._web3.eth.getCode = () => "0x0";
return deploymentChecks.checkIfAlreadyDeployed(params, (err, params) => {
Expand Down
14 changes: 12 additions & 2 deletions packages/plugins/ens/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -320,11 +320,21 @@ class ENS {
const registration = this.config.namesystemConfig.register;
const doRegister = registration && registration.rootDomain;

await this.events.request2('deployment:contract:deploy', this.ensConfig.ENSRegistry);
try {
await this.events.request2('deployment:contract:deploy', this.ensConfig.ENSRegistry);
} catch (err) {
this.logger.error(__(`Error deploying the ENS Registry contract: ${err.message}`));
this.logger.debug(err.stack);
}
// Add Resolver to contract manager again but this time with correct arguments (Registry address)
this.ensConfig.Resolver.args = [this.ensConfig.ENSRegistry.deployedAddress];
this.ensConfig.Resolver = await this.events.request2('contracts:add', this.ensConfig.Resolver);
await this.events.request2('deployment:contract:deploy', this.ensConfig.Resolver);
try {
await this.events.request2('deployment:contract:deploy', this.ensConfig.Resolver);
} catch (err) {
this.logger.error(__(`Error deploying the ENS Resolver contract: ${err.message}`));
this.logger.debug(err.stack);
}

const config = {
registryAbi: self.ensConfig.ENSRegistry.abiDefinition,
Expand Down
6 changes: 3 additions & 3 deletions packages/plugins/ethereum-blockchain-client/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class EthereumBlockchainClient {
return web3.currentProvider;
}

async deployer(contract, done) {
async deployer(contract, additionalDeployParams, done) {
try {
const web3 = await this.web3;
const [account] = await web3.eth.getAccounts();
Expand Down Expand Up @@ -88,7 +88,7 @@ class EthereumBlockchainClient {
}

embarkJsUtils.secureSend(web3, contractObject, {
from: account, gas: contract.gas
from: account, gas: contract.gas, ...additionalDeployParams
}, true, (err, receipt) => {
if (err) {
return done(err);
Expand Down Expand Up @@ -199,7 +199,7 @@ class EthereumBlockchainClient {
if (Array.isArray(arg)) {
return checkArgs(arg, nextEachCb);
}
if (arg[0] === "$") {
if (arg && arg[0] === "$") {
return parseArg(arg, nextEachCb);
}
nextEachCb(null, arg);
Expand Down
11 changes: 5 additions & 6 deletions packages/plugins/nethermind/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ import { __ } from 'embark-i18n';
import {BlockchainClient} from "./blockchain";
const {normalizeInput, testRpcWithEndpoint, testWsEndpoint} = require('embark-utils');
import {BlockchainProcessLauncher} from './blockchainProcessLauncher';

export const NETHERMIND_NAME = 'nethermind';
import constants from "embark-core/constants";

class Nethermind {
constructor(embark) {
Expand All @@ -20,7 +19,7 @@ class Nethermind {
return;
}

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

shouldInit() {
return (
this.blockchainConfig.client === NETHERMIND_NAME &&
this.blockchainConfig.client === constants.blockchain.clients.nethermind &&
this.blockchainConfig.enabled
);
}

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

return cb({ name: `${NETHERMIND_NAME} (Ethereum)`, status: 'on' });
return cb({ name: `${constants.blockchain.clients.nethermind} (Ethereum)`, status: 'on' });
}

_doCheck(cb) {
Expand All @@ -78,7 +77,7 @@ class Nethermind {
startBlockchainNode(callback) {
if (this.blockchainConfig.isStandalone) {
return new BlockchainClient(this.blockchainConfig, {
clientName: NETHERMIND_NAME,
clientName: constants.blockchain.clients.nethermind,
env: this.embark.config.env,
certOptions: this.embark.config.webServerConfig.certOptions,
logger: this.logger,
Expand Down
4 changes: 4 additions & 0 deletions packages/plugins/quorum/.npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
engine-strict = true
package-lock = false
save-exact = true
scripts-prepend-node-path = true
Loading

0 comments on commit 095bd0b

Please sign in to comment.