├── .circleci ├── config.yml └── organise_doc.js ├── .editorconfig ├── .eslintrc ├── .github └── ISSUE_TEMPLATE │ └── custom.md ├── .gitignore ├── .nycrc.json ├── 7nodes-test ├── deployContractViaHttp-externalSigningTemplate.js ├── deployContractViaHttp-pe.js ├── deployContractViaHttp.js ├── deployContractViaIpc.js ├── extension.js └── multi-tenant.js ├── CLA.md ├── CODE-OF-CONDUCT.md ├── CONTRIBUTING.md ├── GOVERNANCE.md ├── LICENSE ├── README.md ├── docker ├── .env ├── config │ ├── besu │ │ ├── .env │ │ ├── cliqueGenesis.json │ │ ├── config.toml │ │ ├── ibft2Genesis.json │ │ ├── log-config.xml │ │ ├── networkFiles │ │ │ ├── member1 │ │ │ │ └── keys │ │ │ │ │ ├── key │ │ │ │ │ └── key.pub │ │ │ ├── member2 │ │ │ │ └── keys │ │ │ │ │ ├── key │ │ │ │ │ └── key.pub │ │ │ ├── member3 │ │ │ │ └── keys │ │ │ │ │ ├── key │ │ │ │ │ └── key.pub │ │ │ ├── rpcnode │ │ │ │ └── keys │ │ │ │ │ ├── key │ │ │ │ │ └── key.pub │ │ │ ├── validator1 │ │ │ │ └── keys │ │ │ │ │ ├── key │ │ │ │ │ └── key.pub │ │ │ ├── validator2 │ │ │ │ └── keys │ │ │ │ │ ├── key │ │ │ │ │ └── key.pub │ │ │ ├── validator3 │ │ │ │ └── keys │ │ │ │ │ ├── key │ │ │ │ │ └── key.pub │ │ │ └── validator4 │ │ │ │ └── keys │ │ │ │ ├── key │ │ │ │ └── key.pub │ │ ├── permissions_config.toml │ │ └── static-nodes.json │ ├── ethsigner │ │ ├── createKey.js │ │ ├── key │ │ └── password │ └── tessera │ │ └── networkFiles │ │ ├── member1 │ │ ├── tm.key │ │ └── tm.pub │ │ ├── member2 │ │ ├── tm.key │ │ └── tm.pub │ │ └── member3 │ │ ├── tm.key │ │ └── tm.pub ├── docker-compose.yml ├── remove.sh ├── run.sh └── stop.sh ├── docs ├── .circleci │ └── config.yml ├── README.md ├── TARGET_README.md ├── community │ └── community_membership.md ├── index.html └── migrations │ ├── Migrate from quorum.js.md │ └── Migrate from web3js-eea.md ├── example ├── accessPublicState │ ├── deployContract.js │ └── storeValueFromNode1.js ├── concurrentPrivateTransactions │ └── concurrentPrivateTransactions.js ├── config │ ├── httpHeaders.js │ └── webSocket.js ├── erc20.js ├── eventEmitter.js ├── eventLogs │ ├── README.md │ ├── getPastLogs.js │ ├── sendTransaction.js │ ├── setup.js │ ├── subscribe.js │ └── subscribeWebSockets.js ├── helpers.js ├── keys.js ├── multiNodeExample │ ├── deployContract.js │ ├── storeValueFromNode1.js │ └── storeValueFromNode2.js ├── multiNodeExamplePrivacyGroup │ ├── deployContract.js │ ├── distributePrivacyGroup.js │ ├── storeValueFromNode1.js │ └── storeValueFromNode2.js ├── multiNodeExamplePrivateCall │ ├── deployContract.js │ ├── storeValueFromNode1.js │ └── storeValueFromNode2.js ├── multiTenancy │ ├── README.md │ ├── createPrivacyGroupMultiTenancy.js │ ├── onchainPrivacyAddRemoveExample.js │ ├── onchainPrivacyDeployContractExample.js │ └── onchainPrivacySimpleExample.js ├── multiTenancyExampleOnchainPrivacyEventLogs │ ├── README.md │ ├── addParticipantToGroup.js │ ├── getPastLogs.js │ ├── removeParticipantFromGroup.js │ ├── sendTransaction.js │ ├── setup.js │ ├── subscribe.js │ └── subscribeWebSockets.js ├── multiTenancyExamplePrivateCall │ ├── deployContract.js │ ├── storeValueFromNode1.js │ └── storeValueFromNode2.js ├── onChainPrivacy │ ├── addRemoveExample.js │ ├── deployContractExample.js │ └── simpleExample.js ├── privacyGroupManagement │ ├── createPrivacyGroup.js │ ├── createPrivacyGroupNode2.js │ ├── deletePrivacyGroup.js │ ├── findPrivacyGroup.js │ └── findPrivacyGroupNode2.js └── solidity │ ├── CrossContractReader │ ├── CrossContractReader.bin │ ├── CrossContractReader.json │ └── CrossContractReader.sol │ ├── EventEmitter │ ├── EventEmitter.bin │ ├── EventEmitter.json │ └── EventEmitter.sol │ └── Greeter │ ├── Greeter.sol │ ├── greeter.abi │ ├── greeter.bin │ ├── greeter_meta.json │ ├── mortal.abi │ ├── mortal.bin │ └── mortal_meta.json ├── integration-tests ├── errorFromNode1ForGroup23.test.js ├── getLogs.test.js ├── getPrivateTransaction.test.js ├── onchainPrivacy │ └── onChainPrivacy.test.js ├── privacyGroupManagement.test.js ├── quickstartExample.test.js ├── quickstartPrivacyGroupIdExample.test.js ├── quickstartPrivateCallExample.test.js ├── quorum-privacy │ ├── allowance.test.js │ ├── connection.test.js │ ├── greeter.test.js │ ├── helpers │ │ ├── httpConfig.js │ │ ├── ipcConfig.js │ │ └── quorumConfig.js │ └── transfer.test.js └── support │ ├── after.js │ ├── before.js │ ├── contractFactory.js │ ├── helpers.js │ └── keys.js ├── jsdoc.json ├── package-lock.json ├── package.json ├── solidity ├── Greeter.sol ├── HumanStandardToken │ ├── HumanStandardToken.json │ ├── HumanStandardToken.sol │ ├── StandardToken.sol │ └── Token.sol ├── PrivacyInterface.abi ├── PrivacyInterface.bin ├── PrivacyInterface.json ├── PrivacyInterface.sol ├── PrivacyProxy.abi ├── PrivacyProxy.bin ├── PrivacyProxy.json ├── PrivacyProxy.sol ├── greeter.json └── key ├── src ├── common.js ├── eth.js ├── flexiblePrivacyGroup.js ├── index.js ├── istanbul.js ├── permission.js ├── priv.js ├── privateSubscription.js ├── privateTransaction.js ├── privateTransaction.test.js ├── ptm.js ├── ptm.test.js ├── raft.js ├── test-utils │ ├── keySets.json │ └── txs.json ├── typedefs.doc.js ├── typescript │ └── index.d.ts ├── util │ ├── custom-ethjs-util.js │ └── index.js ├── utils.js └── utils.test.js └── tests ├── __snapshots__ └── web3Quorum.test.js.snap ├── tests-utils ├── constants.js └── httpMock.js ├── web3.eth.flexiblePrivacyGroup.test.js ├── web3.eth.test.js ├── web3.istanbul.test.js ├── web3.permission.test.js ├── web3.priv.test.js ├── web3.raft.test.js └── web3Quorum.test.js /.circleci/organise_doc.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | const fs = require('fs') 4 | const args = require('minimist')(process.argv.slice(2)) 5 | 6 | // get SDK name and version from package files 7 | // they are used by JSDoc to generate the output directories 8 | const packageJSON = require('../package.json') 9 | const packageVersion = packageJSON.version 10 | const packageName = packageJSON.name 11 | 12 | // get the output directory 13 | const jsdocJSON = require('../jsdoc.json') 14 | const jsdocOpts = jsdocJSON.opts 15 | const jsDocOutputDir = jsdocOpts.destination 16 | 17 | // root dir is the absolute path to use, provided by CI 18 | const rootDir = args.rootdir 19 | if (rootDir === '' || rootDir == null){ 20 | console.error('rootDir argument must be defined'); 21 | process.exit(1); 22 | } 23 | // the tag being built and empty or null if none 24 | const tag = args.tag 25 | 26 | // if no tag, doc will go in latest directory by default 27 | const targetDir = (tag === '' || tag == null) ? 'latest' : tag 28 | 29 | // source directories where JSdoc put the doc 30 | const sourceDirBase = `${rootDir}/${jsDocOutputDir}` 31 | const sourceDir = `${sourceDirBase}/${packageName}/${packageVersion}` 32 | 33 | // check if source exists and move files to latest or tag matching directory 34 | fs.rename(sourceDir, `${rootDir}/docs/${targetDir}`, (err) => { 35 | if (err) throw err 36 | fs.rmdirSync(sourceDirBase, {recursive:true}) 37 | console.log('Doc organisation success') 38 | }) 39 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | # Change these settings to your own preference 10 | indent_style = space 11 | indent_size = 2 12 | 13 | # We recommend you to keep these unchanged 14 | end_of_line = lf 15 | charset = utf-8 16 | trim_trailing_whitespace = true 17 | insert_final_newline = true 18 | 19 | [*.md] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "mocha": true, 4 | "chai": true, 5 | "expect": true, 6 | "assert": true 7 | }, 8 | "parser": "babel-eslint", 9 | "extends": [ 10 | "airbnb-base", 11 | "plugin:prettier/recommended", 12 | "plugin:jest/recommended", 13 | "prettier" 14 | ], 15 | "ignorePatterns": ["docs/out/**"], 16 | "plugins": [ 17 | "promise", 18 | "chai-expect", 19 | "jest" 20 | ], 21 | "env": { 22 | "mocha": true 23 | }, 24 | "rules": { 25 | "no-console": "off", 26 | "arrow-body-style": [ 27 | "error", 28 | "always" 29 | ], 30 | "class-methods-use-this": "warn", 31 | "no-underscore-dangle": "off", 32 | "chai-expect/missing-assertion": 2, 33 | "chai-expect/terminating-properties": 1, 34 | "promise/always-return": "error", 35 | "promise/no-return-wrap": "error", 36 | "promise/param-names": "error", 37 | "promise/catch-or-return": "error", 38 | "promise/no-native": "off", 39 | "promise/no-nesting": "warn", 40 | "promise/no-promise-in-callback": "warn", 41 | "promise/no-callback-in-promise": "warn", 42 | "promise/avoid-new": "warn", 43 | "promise/no-new-statics": "error", 44 | "promise/no-return-in-finally": "warn", 45 | "promise/valid-params": "warn", 46 | "import/no-extraneous-dependencies": ["error", {"devDependencies": ["integration-tests/**/*.js", "example/**/*.js", "tests/**/*.js", "test/**/*.js", "7nodes-test/**/*.js", "docker/**/*.js"]}], 47 | "jest/no-disabled-tests": "warn", 48 | "jest/no-focused-tests": "error", 49 | "jest/no-identical-title": "error", 50 | "jest/prefer-to-have-length": "warn", 51 | "jest/valid-expect": "error" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | #### System information 11 | 12 | Client type: GoQuorum, Besu 13 | 14 | Client version: v21.4.2 15 | 16 | OS & Version: Windows/Linux/OSX 17 | 18 | #### Expected behaviour 19 | 20 | 21 | #### Actual behaviour 22 | 23 | 24 | #### Steps to reproduce the behaviour 25 | 26 | 27 | #### Backtrace 28 | 29 | ```` 30 | [backtrace] 31 | ```` 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### JetBrains template 3 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 4 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 5 | 6 | # User-specific stuff 7 | .idea 8 | .vscode 9 | 10 | # File-based project format 11 | *.iws 12 | 13 | # IntelliJ 14 | out/ 15 | 16 | # mpeltonen/sbt-idea plugin 17 | .idea_modules/ 18 | 19 | # JIRA plugin 20 | atlassian-ide-plugin.xml 21 | 22 | ### JS 23 | /node_modules/ 24 | 25 | # MacOS 26 | .DS_Store 27 | .AppleDouble 28 | .LSOverride 29 | 30 | # testing 31 | coverage/ 32 | .nyc_output/ 33 | 34 | # temporary files 35 | **/params.json 36 | docker/.quickstart.lock 37 | 38 | # doc generation 39 | /.circleci/process.yml 40 | dist 41 | *.log 42 | -------------------------------------------------------------------------------- /.nycrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "all": true, 3 | "include": ["src/**/*.js"] 4 | } 5 | -------------------------------------------------------------------------------- /7nodes-test/deployContractViaHttp.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | 3 | const Web3Quorum = require("../src"); 4 | 5 | const web3 = new Web3Quorum( 6 | new Web3("http://localhost:22000"), 7 | { 8 | privateUrl: "http://localhost:9081", 9 | }, 10 | true 11 | ); 12 | const accAddress = "ed9d02e382b34818e88b88a309c7fe71e65f419d"; 13 | 14 | const signAcct = web3.eth.accounts.decrypt( 15 | { 16 | address: accAddress, 17 | crypto: { 18 | cipher: "aes-128-ctr", 19 | ciphertext: 20 | "4e77046ba3f699e744acb4a89c36a3ea1158a1bd90a076d36675f4c883864377", 21 | cipherparams: { iv: "a8932af2a3c0225ee8e872bc0e462c11" }, 22 | kdf: "scrypt", 23 | kdfparams: { 24 | dklen: 32, 25 | n: 262144, 26 | p: 1, 27 | r: 8, 28 | salt: 29 | "8ca49552b3e92f79c51f2cd3d38dfc723412c212e702bd337a3724e8937aff0f", 30 | }, 31 | mac: "6d1354fef5aa0418389b1a5d1f5ee0050d7273292a1171c51fd02f9ecff55264", 32 | }, 33 | id: "a65d1ac3-db7e-445d-a1cc-b6c5eeaa05e0", 34 | version: 3, 35 | }, 36 | "" 37 | ); 38 | 39 | const abi = [ 40 | { 41 | constant: true, 42 | inputs: [], 43 | name: "storedData", 44 | outputs: [{ name: "", type: "uint256" }], 45 | payable: false, 46 | type: "function", 47 | }, 48 | { 49 | constant: false, 50 | inputs: [{ name: "x", type: "uint256" }], 51 | name: "set", 52 | outputs: [], 53 | payable: false, 54 | type: "function", 55 | }, 56 | { 57 | constant: true, 58 | inputs: [], 59 | name: "get", 60 | outputs: [{ name: "retVal", type: "uint256" }], 61 | payable: false, 62 | type: "function", 63 | }, 64 | { 65 | inputs: [{ name: "initVal", type: "uint256" }], 66 | payable: false, 67 | type: "constructor", 68 | }, 69 | ]; 70 | 71 | const bytecode = 72 | "0x6060604052341561000f57600080fd5b604051602080610149833981016040528080519060200190919050505b806000819055505b505b610104806100456000396000f30060606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680632a1afcd914605157806360fe47b11460775780636d4ce63c146097575b600080fd5b3415605b57600080fd5b606160bd565b6040518082815260200191505060405180910390f35b3415608157600080fd5b6095600480803590602001909190505060c3565b005b341560a157600080fd5b60a760ce565b6040518082815260200191505060405180910390f35b60005481565b806000819055505b50565b6000805490505b905600a165627a7a72305820d5851baab720bba574474de3d09dbeaabc674a15f4dd93b974908476542c23f00029"; 73 | 74 | const simpleContract = new web3.eth.Contract(abi); 75 | 76 | const bytecodeWithInitParam = simpleContract 77 | .deploy({ data: bytecode, arguments: [42] }) 78 | .encodeABI(); 79 | 80 | (async () => { 81 | try { 82 | const txCount = await web3.eth.getTransactionCount(`0x${accAddress}`); 83 | const tx = await web3.priv.generateAndSendRawTransaction({ 84 | gasPrice: 0, 85 | gasLimit: 4300000, 86 | value: 0, 87 | data: bytecodeWithInitParam, 88 | from: signAcct, 89 | isPrivate: true, 90 | privateFrom: "BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo=", 91 | privateFor: ["QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc="], 92 | nonce: txCount, 93 | }); 94 | 95 | console.log("Contract address: ", tx.contractAddress); 96 | const simpleContract2 = new web3.eth.Contract(abi, tx.contractAddress); 97 | const result = await simpleContract2.methods.get().call(); 98 | console.log("result :>> ", result); 99 | return simpleContract2; 100 | } catch (error) { 101 | console.error("error :>> ", error); 102 | return error; 103 | } 104 | })(); 105 | -------------------------------------------------------------------------------- /7nodes-test/extension.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | 3 | const Web3Quorum = require("../src"); 4 | 5 | const web3 = new Web3Quorum( 6 | new Web3("http://localhost:22000"), 7 | { 8 | privateUrl: "http://localhost:9081", 9 | }, 10 | true 11 | ); 12 | 13 | // Example of calling Quorum specific API 14 | web3.raft.leader().then(console.log).catch(console.log); 15 | -------------------------------------------------------------------------------- /7nodes-test/multi-tenant.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const https = require("https"); 3 | const axios = require("axios"); 4 | const FormData = require("form-data"); 5 | const Web3HttpProvider = require("web3-providers-http"); 6 | 7 | const Web3Quorum = require("../src"); 8 | 9 | const PSI = "PS1"; // To change based on the private state identifier 10 | const url = `https://localhost:22000?PSI=${PSI}`; 11 | const oauthURL = "https://localhost:4445/clients"; 12 | 13 | const getAccessToken = async () => { 14 | try { 15 | const instance = axios.create({ 16 | httpsAgent: new https.Agent({ 17 | rejectUnauthorized: false, 18 | }), 19 | }); 20 | await instance.delete(`${oauthURL}/${PSI}`); 21 | const args = { 22 | client_id: PSI, 23 | client_secret: "foofoo", 24 | scope: `rpc://eth_* rpc://quorumExtension_* rpc://rpc_modules psi://${PSI}?self.eoa=0x0&node.eoa=0x0`, 25 | }; 26 | await instance.post(oauthURL, { 27 | grant_types: ["client_credentials"], 28 | token_endpoint_auth_method: "client_secret_post", 29 | audience: ["Node1"], 30 | ...args, 31 | }); 32 | 33 | const body = new FormData(); 34 | body.append("grant_type", "client_credentials"); 35 | body.append("audience", "Node1"); 36 | Object.keys(args).forEach((key) => { 37 | body.append(key, args[key]); 38 | }); 39 | 40 | const { data } = await instance.post( 41 | `https://localhost:4444/oauth2/token`, 42 | body, 43 | { 44 | headers: { 45 | ...body.getHeaders(), 46 | }, 47 | } 48 | ); 49 | return data.access_token; 50 | } catch (e) { 51 | console.error(e.response.data); 52 | } 53 | return null; 54 | }; 55 | (async () => { 56 | try { 57 | const accessToken = await getAccessToken(); 58 | const options = { 59 | keepAlive: true, 60 | headers: [{ name: "Authorization", value: `bearer ${accessToken}` }], 61 | agent: { 62 | https: https.Agent({ 63 | rejectUnauthorized: false, 64 | }), 65 | baseUrl: url, 66 | }, 67 | }; 68 | 69 | const provider = new Web3HttpProvider(url, options); 70 | const web3 = new Web3Quorum( 71 | new Web3(provider), 72 | { 73 | privateUrl: "http://localhost:9081", 74 | }, 75 | true 76 | ); 77 | // Example of calling PSI specific API 78 | const psi = await web3.eth.getPSI(); 79 | console.log(`You are connected to ${psi}`); 80 | } catch (e) { 81 | console.error(e); 82 | } 83 | })(); 84 | -------------------------------------------------------------------------------- /CLA.md: -------------------------------------------------------------------------------- 1 | Sign the CLA 2 | ============= 3 | 4 | This page is the step-by-step guide to signing the Consensys Software Inc. 5 | Individual Contributor License Agreement. 6 | 7 | 1. First and foremost, read [the current version of the CLA]. 8 | It is written to be as close to plain English as possible. 9 | 10 | 2. Make an account on [GitHub] if you don't already have one. 11 | 12 | 3. After creating your first pull request, you will see a merge 13 | pre-requisite requiring to you read and sign the CLA. 14 | 15 | If you have any questions, you can reach us on [RocketChat]. 16 | 17 | [GitHub]: https://github.com/ 18 | [the current version of the CLA]: https://gist.github.com/ConsensysOps/a6a59bf512fc4cfdf08890aa8d3d89c9 19 | [RocketChat]: https://chat.hyperledger.org/channel/besu 20 | -------------------------------------------------------------------------------- /CODE-OF-CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [private-quorum@consensys.net]. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [Contributor Covenant]: https://www.contributor-covenant.org 74 | [private-quorum@consensys.net]: mailto:private-quorum@consensys.net -------------------------------------------------------------------------------- /GOVERNANCE.md: -------------------------------------------------------------------------------- 1 | # Overview 2 | This project is led by a benevolent dictator (ConsenSys) and managed by the community. That is, the community actively contributes to the day-to-day maintenance of the project, but the general strategic line is drawn by the benevolent dictator. In case of disagreement, they have the last word. It is the benevolent dictator’s job to resolve disputes within the community and to ensure that the project is able to progress in a coordinated way. In turn, it is the community’s job to guide the decisions of the benevolent dictator through active engagement and contribution. 3 | 4 | 5 | # Principles 6 | 7 | The community adheres to the following principles: 8 | * Open: permissioning smart contracts is open source. See repository guidelines and CLA, below. 9 | * Welcoming and respectful: See Code of Conduct, below. 10 | * Transparent and accessible: Work and collaboration should be done in public. 11 | * Merit: Ideas and contributions are accepted according to their technical merit and alignment with project objectives and design principles. 12 | 13 | # Code of Conduct 14 | See [code of conduct] 15 | 16 | # Community membership 17 | See [community membership] 18 | 19 | # Decision Making 20 | Decision making will be handled by the Approvers (see [community membership]). If consensus cannot be reached, the Benevolent Dictator will provide the final word on the decision. 21 | 22 | 23 | # CLA 24 | All contributors must sign the CLA, as described in [CLA.md]. 25 | 26 | ## Attribution 27 | This document was influenced by the following: 28 | - Kubernetes community-membership.md, available at [kub community membership]. 29 | - Kubernetes governance.md, available at [kub governance] 30 | - OSSWatch Benevolent Dictator Governance Model, available at [oss watch benevolent dictator]. 31 | 32 | [CLA.md]: /CLA.md 33 | [community membership]: /docs/community/community_membership.md 34 | [code of conduct]: /CODE-OF-CONDUCT.md 35 | [oss watch benevolent dictator]: http://oss-watch.ac.uk/resources/benevolentdictatorgovernancemodel 36 | [kub community membership]: https://raw.githubusercontent.com/kubernetes/community/master/community-membership.md 37 | [kub governance]:https://github.com/kubernetes/community/blob/master/governance.md 38 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Website](https://img.shields.io/website?label=documentation&url=https://consensys.github.io/web3js-quorum/latest/index.html)](https://consensys.github.io/web3js-quorum/latest/index.html) 2 | [![npm](https://img.shields.io/npm/v/web3js-quorum)](https://www.npmjs.com/package/web3js-quorum) 3 | [![Known Vulnerabilities](https://snyk.io/test/github/ConsenSys/web3js-quorum/badge.svg?targetFile=package.json)](https://snyk.io/test/github/ConsenSys/web3js-quorum?targetFile=package.json) 4 | 5 | *This repository is not actively maintained by Consensys and is provided as-is. It is open-source and contributions are welcome.* 6 | 7 | # web3js-quorum 8 | 9 | Web3js-Quorum is an Ethereum JavaScript library extending [web3.js](https://github.com/ethereum/web3.js/) that adds supports for [GoQuorum](https://docs.goquorum.consensys.net/en/stable/) and [Hyperledger Besu](https://besu.hyperledger.org/en/stable/) specific JSON-RPC APIs and features. In particular it enables to use [web3.js](https://github.com/ethereum/web3.js/) with private transactions. 10 | 11 | Web3js-Quorum gather all features from [quorum.js](https://github.com/ConsenSys/quorum.js) and [web3js-eea](https://github.com/ConsenSys/web3js-eea) in a single library. 12 | 13 | Please read the [documentation](https://consensys.github.io/web3js-quorum/latest/index.html) for more. 14 | 15 | ## Features 16 | 17 | - Supports GoQuorum and Besu JSON-RPC APIs 18 | - Create and send private transactions 19 | - Privacy group management 20 | 21 | ## Installation 22 | 23 | ```shell 24 | npm install web3 web3js-quorum 25 | ``` 26 | 27 | ## Quickstart 28 | 29 | The Quorum client APIs methods provided by web3js-quorum are accessed like so: 30 | 31 | ### Extending web3 object 32 | 33 | ```js 34 | const Web3 = require("web3"); 35 | const Web3Quorum = require("web3js-quorum"); 36 | const web3 = new Web3Quorum(new Web3("http://localhost:22000")); 37 | web3.priv.generateAndSendRawTransaction(options); 38 | ``` 39 | 40 | ## Documentation 41 | 42 | For full usage and API details see the [documentation](https://consensys.github.io/web3js-quorum/latest/index.html). 43 | 44 | ## Examples 45 | 46 | The [example](https://github.com/ConsenSys/web3js-quorum/tree/master/example) directory contains examples of web3js-quorum usage with Besu as a Quorum client. 47 | The [7nodes-test](https://github.com/ConsenSys/web3js-quorum/tree/master/7nodes-test) directory contains examples of web3js-quorum usage with GoQuorum as a Quorum client. 48 | 49 | ## Migrations 50 | * To migrate from web3js-eea refer to [this](https://consensys.github.io/web3js-quorum/latest/tutorial-Migrate%20from%20web3js-eea.html) 51 | * To migrate from quorum.js refer to [this](https://consensys.github.io/web3js-quorum/latest/tutorial-Migrate%20from%20quorum.js.html) 52 | 53 | ## Contributing 54 | 55 | Please follow the [Contribution Guidelines](https://github.com/ConsenSys/web3js-quorum/blob/master/CONTRIBUTING.md) and Review Guidelines. 56 | -------------------------------------------------------------------------------- /docker/.env: -------------------------------------------------------------------------------- 1 | 2 | BESU_VERSION=latest 3 | QUORUM_TESSERA_VERSION=latest 4 | QUORUM_ETHSIGNER_VERSION=latest 5 | QUICKSTART_VERSION=$BESU_VERSION 6 | LOCK_FILE=.quickstart.lock 7 | BESU_PUBLIC_KEY_DIRECTORY=/opt/besu/public-keys/ 8 | 9 | -------------------------------------------------------------------------------- /docker/config/besu/.env: -------------------------------------------------------------------------------- 1 | LOG4J_CONFIGURATION_FILE=/config/log-config.xml 2 | -------------------------------------------------------------------------------- /docker/config/besu/cliqueGenesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config":{ 3 | "chainId":1337, 4 | "constantinoplefixblock": 0, 5 | "clique":{ 6 | "blockperiodseconds":15, 7 | "epochlength":30000 8 | } 9 | }, 10 | "coinbase":"0x0000000000000000000000000000000000000000", 11 | "difficulty":"0x1", 12 | "extraData":"0x00000000000000000000000000000000000000000000000000000000000000004592c8e45706cc08b8f44b11e43cba0cfc5892cb0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 13 | "gasLimit":"0xa00000", 14 | "mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000", 15 | "nonce":"0x0", 16 | "timestamp":"0x5c51a607", 17 | "alloc": { 18 | "fe3b557e8fb62b89f4916b721be55ceb828dbd73": { 19 | "privateKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 20 | "comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", 21 | "balance": "0xad78ebc5ac6200000" 22 | }, 23 | "627306090abaB3A6e1400e9345bC60c78a8BEf57": { 24 | "privateKey": "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", 25 | "comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", 26 | "balance": "90000000000000000000000" 27 | }, 28 | "f17f52151EbEF6C7334FAD080c5704D77216b732": { 29 | "privateKey": "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f", 30 | "comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", 31 | "balance": "90000000000000000000000" 32 | } 33 | }, 34 | "number":"0x0", 35 | "gasUsed":"0x0", 36 | "parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000" 37 | } 38 | -------------------------------------------------------------------------------- /docker/config/besu/config.toml: -------------------------------------------------------------------------------- 1 | 2 | logging="INFO" 3 | data-path="/opt/besu/data" 4 | host-whitelist=["*"] 5 | 6 | # rpc 7 | rpc-http-enabled=true 8 | rpc-http-host="0.0.0.0" 9 | rpc-http-port=8545 10 | rpc-http-cors-origins=["*"] 11 | 12 | # ws 13 | rpc-ws-enabled=true 14 | rpc-ws-host="0.0.0.0" 15 | rpc-ws-port=8546 16 | 17 | # graphql 18 | graphql-http-enabled=true 19 | graphql-http-host="0.0.0.0" 20 | graphql-http-port=8547 21 | graphql-http-cors-origins=["*"] 22 | 23 | # metrics 24 | metrics-enabled=true 25 | metrics-host="0.0.0.0" 26 | metrics-port=9545 27 | 28 | # permissions 29 | permissions-nodes-config-file-enabled=true 30 | permissions-nodes-config-file="/config/permissions_config.toml" 31 | 32 | # bootnodes 33 | bootnodes=["enode://c1979a8a48693db804316b5acebe35e11731e1fb1c9c21ff7268ab25db6f6e03390a429b83cf0ec0865a7205f2669ec1ace652a3def11e2e01571c74939cbe22@172.16.239.11:30303"] 34 | 35 | 36 | # Discovery at boot is set to a list of static files, but will also discover new nodes should they be added 37 | # static nodes 38 | static-nodes-file="/config/static-nodes.json" 39 | discovery-enabled=true 40 | -------------------------------------------------------------------------------- /docker/config/besu/log-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | INFO 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/member1/keys/key: -------------------------------------------------------------------------------- 1 | 8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63 -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/member1/keys/key.pub: -------------------------------------------------------------------------------- 1 | 09b02f8a5fddd222ade4ea4528faefc399623af3f736be3c44f03e2df22fb792f3931a4d9573d333ca74343305762a753388c3422a86d98b713fc91c1ea04842 2 | -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/member2/keys/key: -------------------------------------------------------------------------------- 1 | c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3 -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/member2/keys/key.pub: -------------------------------------------------------------------------------- 1 | af80b90d25145da28c583359beb47b21796b2fe1a23c1511e443e7a64dfdb27d7434c380f0aa4c500e220aa1a9d068514b1ff4d5019e624e7ba1efe82b340a59 2 | -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/member3/keys/key: -------------------------------------------------------------------------------- 1 | ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/member3/keys/key.pub: -------------------------------------------------------------------------------- 1 | ce7edc292d7b747fab2f23584bbafaffde5c8ff17cf689969614441e0527b90015ea9fee96aed6d9c0fc2fbe0bd1883dee223b3200246ff1e21976bdbc9a0fc8 2 | -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/rpcnode/keys/key: -------------------------------------------------------------------------------- 1 | 7fd74d74209b7dd06a556b5745f9f4aa85b0ac54ee72bd0969929096210a666a -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/rpcnode/keys/key.pub: -------------------------------------------------------------------------------- 1 | 51729f1b4186db1701e13d9e71b7b4f0a35e0cc1f480c904c5e758b5b76936685dccde490c623a79f6c6c5d1dfd3eae37d35101e1a9a2d06536074562dd77604 -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/validator1/keys/key: -------------------------------------------------------------------------------- 1 | 0fd4aecd8f02b24f468325aa06e1428ab8076d283bac3ed804c9f70187dedb63 -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/validator1/keys/key.pub: -------------------------------------------------------------------------------- 1 | c1979a8a48693db804316b5acebe35e11731e1fb1c9c21ff7268ab25db6f6e03390a429b83cf0ec0865a7205f2669ec1ace652a3def11e2e01571c74939cbe22 -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/validator2/keys/key: -------------------------------------------------------------------------------- 1 | 47c63c0afd1a85b16915934a2d75cf5a0d3bd13c509d6ee9d7ef1315a36bdc0a -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/validator2/keys/key.pub: -------------------------------------------------------------------------------- 1 | e40129f02c9e29a02049668346d4777bb55809042746882b33b20a8b5a7310eb5f107a53f0aa3da766ee77f401557a79c0c328329ea48bf0996c6c9dff817f76 -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/validator3/keys/key: -------------------------------------------------------------------------------- 1 | 35dcc853354b24e98889fa4b2e214d5e92e759ef8312bb6a444bad3182187b68 -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/validator3/keys/key.pub: -------------------------------------------------------------------------------- 1 | a3e4af081a0ab853c959b9acd0596f818b91a9409b9d04c50af055072c929abfa340e14111dcfa76e049fdb16bb9198e722d5e7be3e8ef37562ea0d0ce1eda11 -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/validator4/keys/key: -------------------------------------------------------------------------------- 1 | d38e71552943e18061fdb44a72eca14ea193e0505a7ead404864f9840e275b49 -------------------------------------------------------------------------------- /docker/config/besu/networkFiles/validator4/keys/key.pub: -------------------------------------------------------------------------------- 1 | 8f4e444a73034236ab4244c7a572aa2c6198b9e0d483ef17bf4b751cac5c0370bc527a5b0c5d01aa3ef41704af838c74730aeecac0f0c22dc4c17b0a9f03ad76 -------------------------------------------------------------------------------- /docker/config/besu/permissions_config.toml: -------------------------------------------------------------------------------- 1 | nodes-whitelist=[ 2 | "enode://c1979a8a48693db804316b5acebe35e11731e1fb1c9c21ff7268ab25db6f6e03390a429b83cf0ec0865a7205f2669ec1ace652a3def11e2e01571c74939cbe22@172.16.239.11:30303", 3 | "enode://e40129f02c9e29a02049668346d4777bb55809042746882b33b20a8b5a7310eb5f107a53f0aa3da766ee77f401557a79c0c328329ea48bf0996c6c9dff817f76@172.16.239.12:30303", 4 | "enode://a3e4af081a0ab853c959b9acd0596f818b91a9409b9d04c50af055072c929abfa340e14111dcfa76e049fdb16bb9198e722d5e7be3e8ef37562ea0d0ce1eda11@172.16.239.13:30303", 5 | "enode://8f4e444a73034236ab4244c7a572aa2c6198b9e0d483ef17bf4b751cac5c0370bc527a5b0c5d01aa3ef41704af838c74730aeecac0f0c22dc4c17b0a9f03ad76@172.16.239.14:30303", 6 | "enode://51729f1b4186db1701e13d9e71b7b4f0a35e0cc1f480c904c5e758b5b76936685dccde490c623a79f6c6c5d1dfd3eae37d35101e1a9a2d06536074562dd77604@172.16.239.15:30303", 7 | "enode://09b02f8a5fddd222ade4ea4528faefc399623af3f736be3c44f03e2df22fb792f3931a4d9573d333ca74343305762a753388c3422a86d98b713fc91c1ea04842@172.16.239.16:30303", 8 | "enode://af80b90d25145da28c583359beb47b21796b2fe1a23c1511e443e7a64dfdb27d7434c380f0aa4c500e220aa1a9d068514b1ff4d5019e624e7ba1efe82b340a59@172.16.239.17:30303", 9 | "enode://ce7edc292d7b747fab2f23584bbafaffde5c8ff17cf689969614441e0527b90015ea9fee96aed6d9c0fc2fbe0bd1883dee223b3200246ff1e21976bdbc9a0fc8@172.16.239.18:30303" 10 | ] 11 | -------------------------------------------------------------------------------- /docker/config/besu/static-nodes.json: -------------------------------------------------------------------------------- 1 | [ 2 | "enode://c1979a8a48693db804316b5acebe35e11731e1fb1c9c21ff7268ab25db6f6e03390a429b83cf0ec0865a7205f2669ec1ace652a3def11e2e01571c74939cbe22@172.16.239.11:30303", 3 | "enode://e40129f02c9e29a02049668346d4777bb55809042746882b33b20a8b5a7310eb5f107a53f0aa3da766ee77f401557a79c0c328329ea48bf0996c6c9dff817f76@172.16.239.12:30303", 4 | "enode://a3e4af081a0ab853c959b9acd0596f818b91a9409b9d04c50af055072c929abfa340e14111dcfa76e049fdb16bb9198e722d5e7be3e8ef37562ea0d0ce1eda11@172.16.239.13:30303", 5 | "enode://8f4e444a73034236ab4244c7a572aa2c6198b9e0d483ef17bf4b751cac5c0370bc527a5b0c5d01aa3ef41704af838c74730aeecac0f0c22dc4c17b0a9f03ad76@172.16.239.14:30303", 6 | "enode://51729f1b4186db1701e13d9e71b7b4f0a35e0cc1f480c904c5e758b5b76936685dccde490c623a79f6c6c5d1dfd3eae37d35101e1a9a2d06536074562dd77604@172.16.239.15:30303", 7 | "enode://09b02f8a5fddd222ade4ea4528faefc399623af3f736be3c44f03e2df22fb792f3931a4d9573d333ca74343305762a753388c3422a86d98b713fc91c1ea04842@172.16.239.16:30303", 8 | "enode://af80b90d25145da28c583359beb47b21796b2fe1a23c1511e443e7a64dfdb27d7434c380f0aa4c500e220aa1a9d068514b1ff4d5019e624e7ba1efe82b340a59@172.16.239.17:30303", 9 | "enode://ce7edc292d7b747fab2f23584bbafaffde5c8ff17cf689969614441e0527b90015ea9fee96aed6d9c0fc2fbe0bd1883dee223b3200246ff1e21976bdbc9a0fc8@172.16.239.18:30303" 10 | ] -------------------------------------------------------------------------------- /docker/config/ethsigner/createKey.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | 3 | // Web3 initialization (should point to the JSON-RPC endpoint) 4 | const web3 = new Web3(new Web3.providers.HttpProvider("http://127.0.0.1:8545")); 5 | 6 | const V3KeyStore = web3.eth.accounts.encrypt( 7 | "797bbe0373132e8c5483515b68ecbb6d3581b56f0205b653ad2b30a559e83891", 8 | "Password1" 9 | ); 10 | console.log(JSON.stringify(V3KeyStore)); 11 | process.exit(); 12 | -------------------------------------------------------------------------------- /docker/config/ethsigner/key: -------------------------------------------------------------------------------- 1 | {"version":3,"id":"0e3d044a-26b4-429f-a5d3-def4a62a77ec","address":"9b790656b9ec0db1936ed84b3bea605873558198","crypto":{"ciphertext":"4d34be50618c36fb57349a8a7dc7b0c46f7c6883c817087863ff4f1fbc957582","cipherparams":{"iv":"723a6a815dcaf255ebd1da1bfb14e1b8"},"cipher":"aes-128-ctr","kdf":"scrypt","kdfparams":{"dklen":32,"salt":"3d04a3f225d3d5874388484730a30d45627399d69721a7c7fb653f8e0e90a67b","n":8192,"r":8,"p":1},"mac":"5c04e705196b35abace4da61700921d7762dba782ed68a4cd9917dd75badaacb"}} -------------------------------------------------------------------------------- /docker/config/ethsigner/password: -------------------------------------------------------------------------------- 1 | Password1 -------------------------------------------------------------------------------- /docker/config/tessera/networkFiles/member1/tm.key: -------------------------------------------------------------------------------- 1 | {"data":{"bytes":"Wl+xSyXVuuqzpvznOS7dOobhcn4C5auxkFRi7yLtgtA="},"type":"unlocked"} -------------------------------------------------------------------------------- /docker/config/tessera/networkFiles/member1/tm.pub: -------------------------------------------------------------------------------- 1 | BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo= -------------------------------------------------------------------------------- /docker/config/tessera/networkFiles/member2/tm.key: -------------------------------------------------------------------------------- 1 | {"data":{"bytes":"nDFwJNHSiT1gNzKBy9WJvMhmYRkW3TzFUmPsNzR6oFk="},"type":"unlocked"} -------------------------------------------------------------------------------- /docker/config/tessera/networkFiles/member2/tm.pub: -------------------------------------------------------------------------------- 1 | QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc= -------------------------------------------------------------------------------- /docker/config/tessera/networkFiles/member3/tm.key: -------------------------------------------------------------------------------- 1 | {"data":{"bytes":"tMxUVR8bX7aq/TbpVHc2QV3SN2iUuExBwefAuFsO0Lg="},"type":"unlocked"} -------------------------------------------------------------------------------- /docker/config/tessera/networkFiles/member3/tm.pub: -------------------------------------------------------------------------------- 1 | 1iTZde/ndBHvzhcl7V68x44Vx7pl8nwx9LqnM/AfJUg= -------------------------------------------------------------------------------- /docker/remove.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | NO_LOCK_REQUIRED=false 4 | 5 | . ./.env 6 | 7 | docker-compose down -v 8 | docker-compose rm -sfv 9 | 10 | rm ${LOCK_FILE} 11 | echo "Lock file ${LOCK_FILE} removed" 12 | -------------------------------------------------------------------------------- /docker/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | 3 | NO_LOCK_REQUIRED=true 4 | . ./.env 5 | 6 | # Make sure we have an up-to-date image 7 | docker pull hyperledger/besu:${BESU_VERSION} 8 | 9 | # Build and run containers and network 10 | echo "${QUICKSTART_VERSION}" >> ${LOCK_FILE} 11 | echo "Starting network..." 12 | docker-compose build --pull 13 | docker-compose up --detach 14 | echo "Services are up and running ..." 15 | -------------------------------------------------------------------------------- /docker/stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -u 2 | 3 | NO_LOCK_REQUIRED=false 4 | . ./.env 5 | 6 | docker-compose stop 7 | -------------------------------------------------------------------------------- /docs/.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 2 | jobs: 3 | build: 4 | docker: 5 | - image: alpine:3.7 6 | steps: 7 | - run: 8 | name: noop 9 | command: "true" 10 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Generated SDK documentation sources 2 | 3 | This directory contains base files to generate SDK doc with CI. 4 | 5 | - index.html is the root redirect file 6 | - TARGET_README.md is the readme that will be renamed and copied to the gh-pages branch 7 | - README.md will not be copied, it's this file. 8 | - .circleci directory contains a configuration for CircleCI that does nothing but prevents CI to fail. 9 | 10 | The actual up to date generated doc is available at [`https://consensys.github.io/web3js-quorum/`](https://consensys.github.io/web3js-quorum/) 11 | -------------------------------------------------------------------------------- /docs/TARGET_README.md: -------------------------------------------------------------------------------- 1 | ## Github Pages publishing branch 2 | 3 | **This branch content is generated by CI process and should not be modified manually.** 4 | 5 | Sources for this content can be found on /docs directory on 6 | [master](https://github.com/ConsenSysQuorum/web3js-quorum/tree/master) and its children branches. 7 | 8 | The actual up to date generated doc is available at [`https://consensys.github.io/web3js-quorum/`](https://consensys.github.io/web3js-quorum/) 9 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |

Web3js-Quorum SDK documentation

10 |

11 | You will be redirected to latest version of the documentation. 12 |

13 | 14 | 15 | -------------------------------------------------------------------------------- /example/accessPublicState/deployContract.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const Web3 = require("web3"); 5 | const Tx = require("ethereumjs-tx").Transaction; 6 | const Web3Quorum = require("../../src"); 7 | 8 | const { enclave, network } = require("../keys.js"); 9 | 10 | const binaryEventEmitter = fs.readFileSync( 11 | path.join(__dirname, "../solidity/EventEmitter/EventEmitter.bin") 12 | ); 13 | 14 | const binaryCrossContractReader = fs.readFileSync( 15 | path.join( 16 | __dirname, 17 | "../solidity/CrossContractReader/CrossContractReader.bin" 18 | ) 19 | ); 20 | 21 | const web3 = new Web3Quorum(new Web3(network.node1.url)); 22 | let logBuffer = ""; 23 | 24 | const createPublicEventEmitter = () => { 25 | const besuAccount = web3.eth.accounts.privateKeyToAccount( 26 | `0x${network.node1.privateKey}` 27 | ); 28 | return web3.eth 29 | .getTransactionCount(besuAccount.address, "pending") 30 | .then((count) => { 31 | const rawTx = { 32 | nonce: web3.utils.numberToHex(count), 33 | from: besuAccount.address, 34 | value: 0, 35 | to: null, 36 | data: `0x${binaryEventEmitter}`, 37 | gasPrice: "0xFFFFF", 38 | gasLimit: "0xFFFFFF", 39 | }; 40 | const tx = new Tx(rawTx, { 41 | chain: "mainnet", 42 | hardfork: "homestead", 43 | }); 44 | tx.sign(Buffer.from(network.node1.privateKey, "hex")); 45 | const serializedTx = tx.serialize(); 46 | return web3.eth.sendSignedTransaction( 47 | `0x${serializedTx.toString("hex")}` 48 | ); 49 | }) 50 | .then((transactionReceipt) => { 51 | console.log("Public Transaction Receipt\n", transactionReceipt); 52 | logBuffer += `now you have to run:\n export PUBLIC_CONTRACT_ADDRESS=${transactionReceipt.contractAddress}\n`; 53 | return transactionReceipt.contractAddress; 54 | }); 55 | }; 56 | 57 | const createPrivateCrossContractReader = () => { 58 | const contractOptions = { 59 | data: `0x${binaryCrossContractReader}`, 60 | privateFrom: enclave.node1.publicKey, 61 | privateFor: [enclave.node2.publicKey], 62 | privateKey: network.node1.privateKey, 63 | }; 64 | return web3.priv.generateAndSendRawTransaction(contractOptions); 65 | }; 66 | 67 | const getPrivateContractAddress = (transactionHash) => { 68 | console.log("Transaction Hash ", transactionHash); 69 | return web3.priv 70 | .waitForTransactionReceipt(transactionHash) 71 | .then((privateTransactionReceipt) => { 72 | console.log("Private Transaction Receipt\n", privateTransactionReceipt); 73 | logBuffer += ` export PRIVATE_CONTRACT_ADDRESS=${privateTransactionReceipt.contractAddress}`; 74 | console.log(logBuffer); 75 | return privateTransactionReceipt.contractAddress; 76 | }); 77 | }; 78 | 79 | module.exports = () => { 80 | return createPublicEventEmitter() 81 | .then(createPrivateCrossContractReader) 82 | .then(getPrivateContractAddress) 83 | .catch((error) => { 84 | console.log(error); 85 | console.log( 86 | "\nThis example requires ONCHAIN privacy to be DISABLED. \nCheck config for ONCHAIN privacy groups." 87 | ); 88 | }); 89 | }; 90 | 91 | if (require.main === module) { 92 | module.exports(); 93 | } 94 | -------------------------------------------------------------------------------- /example/config/httpHeaders.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | 4 | // Define an HTTP provider, passing in the desired options: 5 | // from https://github.com/ethereum/web3.js/tree/1.x/packages/web3-providers-http 6 | // var options = { 7 | // keepAlive: true, 8 | // timeout: 20000, // milliseconds, 9 | // headers: [{name: 'Access-Control-Allow-Origin', value: '*'},{...}], 10 | // withCredentials: false, 11 | // agent: {http: http.Agent(...), baseUrl: ''} 12 | // }; 13 | const providerOptions = { 14 | headers: [{ name: "X-My-Custom-Header", value: "some value" }], 15 | }; 16 | const httpProvider = new Web3.providers.HttpProvider( 17 | "http://localhost:20000", 18 | providerOptions 19 | ); 20 | const web3Http = new Web3Quorum(new Web3(httpProvider)); 21 | 22 | web3Http.eth 23 | .getBlockNumber() 24 | .then((num) => { 25 | console.log("Current block:", num); 26 | return num; 27 | }) 28 | .catch(console.error); 29 | -------------------------------------------------------------------------------- /example/config/webSocket.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | 4 | // Define a WebSocket provider, passing in the desired options: 5 | // See https://github.com/ethereum/web3.js/tree/1.x/packages/web3-providers-ws for options 6 | 7 | // See https://besu.hyperledger.org/en/stable/HowTo/Interact/APIs/Authentication/#jwt-public-key-authentication 8 | // for details on authenticating with JWTs 9 | const JWT_TOKEN = 10 | "ewogICJhbGciOiAibm9uZSIsCiAgInR5cCI6ICJKV1QiCn0.eyJpYXQiOjE1MTYyMzkwMjIsImV4cCI6NDcyOTM2MzIwMCwicGVybWlzc2lvbnMiOlsibmV0OnBlZXJDb3VudCJdfQ"; 11 | 12 | const providerOptions = { 13 | timeout: 30000, // ms 14 | headers: { 15 | authorization: `Bearer ${JWT_TOKEN}`, 16 | }, 17 | reconnect: { 18 | auto: true, 19 | delay: 5000, // ms 20 | maxAttempts: 5, 21 | onTimeout: false, 22 | }, 23 | }; 24 | 25 | const websocketProvider = new Web3.providers.WebsocketProvider( 26 | "http://localhost:8546", 27 | providerOptions 28 | ); 29 | 30 | const web3Ws = new Web3Quorum(new Web3(websocketProvider)); 31 | 32 | web3Ws.eth 33 | .getBlockNumber() 34 | .then((num) => { 35 | console.log("Current block:", num); 36 | return num; 37 | }) 38 | .then(() => { 39 | console.log("disconnecting..."); 40 | return web3Ws.currentProvider.disconnect(); 41 | }) 42 | .catch(console.error); 43 | -------------------------------------------------------------------------------- /example/erc20.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const Web3 = require("web3"); 5 | const Web3Quorum = require("../src"); 6 | const contractMeta = require("../solidity/HumanStandardToken/HumanStandardToken.json") 7 | .contracts["HumanStandardToken.sol:HumanStandardToken"]; 8 | 9 | const HumanStandardTokenAbi = JSON.parse(contractMeta.interface); 10 | 11 | const ethUtil = require("../src/util/custom-ethjs-util"); 12 | const { enclave, network } = require("./keys.js"); 13 | 14 | const binary = fs.readFileSync( 15 | path.join(__dirname, "./solidity/EventEmitter/EventEmitter.bin") 16 | ); 17 | 18 | const web3 = new Web3Quorum(new Web3(network.node1.url)); 19 | 20 | const contract = new web3.eth.Contract(HumanStandardTokenAbi); 21 | 22 | // create HumanStandardToken constructor 23 | const constructorAbi = contract._jsonInterface.find((e) => { 24 | return e.type === "constructor"; 25 | }); 26 | const constructorArgs = web3.eth.abi 27 | .encodeParameters(constructorAbi.inputs, [ 28 | 1000000, 29 | "PegaSys Token", 30 | 10, 31 | "PegaSys", 32 | ]) 33 | .slice(2); 34 | 35 | const contractOptions = { 36 | data: `0x${binary}${constructorArgs}`, 37 | privateFrom: enclave.node1.publicKey, 38 | privateFor: [enclave.node1.publicKey], 39 | privateKey: network.node1.privateKey, 40 | }; 41 | 42 | web3.priv 43 | .generateAndSendRawTransaction(contractOptions) 44 | .then((hash) => { 45 | console.log(`Transaction Hash ${hash}`); 46 | return web3.priv.waitForTransactionReceipt(hash); 47 | }) 48 | .then((privateTransactionReceipt) => { 49 | console.log("Private Transaction Receipt"); 50 | console.log(privateTransactionReceipt); 51 | return privateTransactionReceipt.contractAddress; 52 | }) 53 | .then((contractAddress) => { 54 | // const contract = web3.eth.Contract(HumandStandartTokenAbi, contractAddress); 55 | // contract.methods.transfer(["to", "value"]).send(??) 56 | 57 | // already 0x prefixed 58 | const functionAbi = contract._jsonInterface.find((element) => { 59 | return element.name === "transfer"; 60 | }); 61 | const transferTo = `0x${ethUtil 62 | .privateToAddress(Buffer.from(network.node2.privateKey, "hex")) 63 | .toString("hex")}`; 64 | const functionArgs = web3.eth.abi 65 | .encodeParameters(functionAbi.inputs, [transferTo, 1]) 66 | .slice(2); 67 | 68 | return web3.priv.generateAndSendRawTransaction({ 69 | to: contractAddress, 70 | data: functionAbi.signature + functionArgs, 71 | privateFrom: enclave.node1.publicKey, 72 | privateFor: [enclave.node2.publicKey], 73 | privateKey: network.node1.privateKey, 74 | }); 75 | }) 76 | .then((transactionHash) => { 77 | console.log(`Transaction Hash ${transactionHash}`); 78 | return web3.priv.waitForTransactionReceipt(transactionHash); 79 | }) 80 | .then((privateTransactionReceipt) => { 81 | console.log("Private Transaction Receipt"); 82 | console.log(privateTransactionReceipt); 83 | if (privateTransactionReceipt.logs.length > 0) { 84 | console.log("Log 0"); 85 | console.log(privateTransactionReceipt.logs[0]); 86 | } 87 | return privateTransactionReceipt; 88 | }) 89 | .catch((e) => { 90 | console.log(e); 91 | }); 92 | -------------------------------------------------------------------------------- /example/eventEmitter.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const Web3 = require("web3"); 5 | const Web3Quorum = require("../src"); 6 | const EventEmitterAbi = require("./solidity/EventEmitter/EventEmitter.json") 7 | .output.abi; 8 | 9 | const { enclave, network } = require("./keys.js"); 10 | 11 | const binary = fs.readFileSync( 12 | path.join(__dirname, "./solidity/EventEmitter/EventEmitter.bin") 13 | ); 14 | 15 | const web3 = new Web3Quorum(new Web3(network.node1.url)); 16 | // eslint-disable-next-line no-new 17 | new web3.eth.Contract(EventEmitterAbi); 18 | 19 | const createPrivateEmitterContract = () => { 20 | const contractOptions = { 21 | data: `0x${binary}`, 22 | privateFrom: enclave.node1.publicKey, 23 | privateFor: [enclave.node2.publicKey], 24 | privateKey: network.node1.privateKey, 25 | }; 26 | return web3.priv.generateAndSendRawTransaction(contractOptions); 27 | }; 28 | 29 | const getPrivateContractAddress = (transactionHash) => { 30 | console.log("Transaction Hash ", transactionHash); 31 | return web3.priv 32 | .waitForTransactionReceipt(transactionHash) 33 | .then((privateTransactionReceipt) => { 34 | console.log("Private Transaction Receipt\n", privateTransactionReceipt); 35 | return privateTransactionReceipt.contractAddress; 36 | }); 37 | }; 38 | 39 | const storeValue = (contractAddress, value) => { 40 | const functionAbi = EventEmitterAbi.find((e) => { 41 | return e.name === "store"; 42 | }); 43 | const functionArgs = web3.eth.abi 44 | .encodeParameters(functionAbi.inputs, [value]) 45 | .slice(2); 46 | 47 | const functionCall = { 48 | to: contractAddress, 49 | data: functionAbi.signature + functionArgs, 50 | privateFrom: enclave.node1.publicKey, 51 | privateFor: [enclave.node2.publicKey], 52 | privateKey: network.node1.privateKey, 53 | }; 54 | return web3.priv.generateAndSendRawTransaction(functionCall); 55 | }; 56 | 57 | const getValue = (contractAddress) => { 58 | const functionAbi = EventEmitterAbi.find((e) => { 59 | return e.name === "value"; 60 | }); 61 | 62 | const functionCall = { 63 | to: contractAddress, 64 | data: functionAbi.signature, 65 | privateFrom: enclave.node1.publicKey, 66 | privateFor: [enclave.node2.publicKey], 67 | privateKey: network.node1.privateKey, 68 | }; 69 | 70 | return web3.priv 71 | .generateAndSendRawTransaction(functionCall) 72 | .then((transactionHash) => { 73 | return web3.priv.waitForTransactionReceipt(transactionHash); 74 | }) 75 | .then((result) => { 76 | console.log("Get Value:", result.output); 77 | return result.output; 78 | }); 79 | }; 80 | 81 | const getPrivateTransactionReceipt = (transactionHash) => { 82 | return web3.priv.waitForTransactionReceipt(transactionHash).then((result) => { 83 | console.log("Transaction Hash:", transactionHash); 84 | console.log("Event Emitted:", result.logs[0].data); 85 | return result; 86 | }); 87 | }; 88 | 89 | createPrivateEmitterContract() 90 | .then(getPrivateContractAddress) 91 | .then((contractAddress) => { 92 | // eslint-disable-next-line promise/no-nesting 93 | return storeValue(contractAddress, 1000) 94 | .then((transactionHash) => { 95 | return getPrivateTransactionReceipt(transactionHash); 96 | }) 97 | .then(() => { 98 | return getValue(contractAddress); 99 | }) 100 | .then(() => { 101 | return storeValue(contractAddress, 42); 102 | }) 103 | .then((transactionHash) => { 104 | return getPrivateTransactionReceipt(transactionHash); 105 | }) 106 | .then(() => { 107 | return getValue(contractAddress); 108 | }); 109 | }) 110 | .catch(console.log); 111 | -------------------------------------------------------------------------------- /example/eventLogs/README.md: -------------------------------------------------------------------------------- 1 | # Examples of getting event logs 2 | 3 | Scripts: 4 | - `setup.js` - create privacy group and deploy contract 5 | - `subscribe.js` - subscribe to new logs sent to the contract using the HTTP polling API 6 | - `subscribeWebSocket.js` - subscribe to new logs sent to the contract using the WebSocket pub-sub API 7 | - `sendTransaction.js` - send a transaction to update the value in the contract 8 | - `getPastLogs.js` - get past logs 9 | 10 | ## Usage 11 | Run `setup.js` to create a new privacy group and deploy an [EventEmitter](../solidity/EventEmitter/EventEmitter.sol) contract into it. The privacy group ID and the contract address will be saved in `params.json` to be used by the other scripts. 12 | 13 | Next, run `subscribe.js` or `subscribeWebSockets.js` to subscribe to logs for the contract. The script will print any past and incoming logs until exited. 14 | 15 | Run `sendTransaction.js` to update the value stored in the contract and emit a log. You can specify the value to store as a command line argument. 16 | ``` 17 | node sendTransaction.js 5 18 | ``` 19 | 20 | Each time you run the script, you should see a new log output from `subscribe.js`/`subscribeWebSocket.js`. 21 | 22 | Finally, run `getPastLogs.js` for all of the logs sent to the contract. 23 | -------------------------------------------------------------------------------- /example/eventLogs/getPastLogs.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Web3 = require("web3"); 4 | const Web3Quorum = require("../../src"); 5 | 6 | const { network } = require("../keys"); 7 | 8 | const node = new Web3Quorum(new Web3(network.node1.url)); 9 | const params = JSON.parse(fs.readFileSync(path.join(__dirname, "params.json"))); 10 | 11 | function run() { 12 | const { privacyGroupId, contractAddress: address } = params; 13 | 14 | const filter = { 15 | address, 16 | }; 17 | 18 | return node.priv.getLogs(privacyGroupId, filter).then((logs) => { 19 | console.log("Received logs\n", logs); 20 | return logs; 21 | }); 22 | } 23 | 24 | run(); 25 | -------------------------------------------------------------------------------- /example/eventLogs/sendTransaction.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Web3 = require("web3"); 4 | 5 | const { network, enclave } = require("../keys"); 6 | const Web3Quorum = require("../../src"); 7 | 8 | const artifact = fs.readFileSync( 9 | path.join(__dirname, "../solidity/EventEmitter/EventEmitter.json") 10 | ); 11 | const { abi } = JSON.parse(artifact).output; 12 | const params = JSON.parse(fs.readFileSync(path.join(__dirname, "params.json"))); 13 | 14 | const node = new Web3Quorum(new Web3(network.node1.url)); 15 | 16 | async function run() { 17 | const { privacyGroupId, contractAddress } = params; 18 | const enclaveKey = enclave.node1.publicKey; 19 | 20 | // send a transaction 21 | const args = process.argv.slice(2); 22 | const value = args.shift() || 3; 23 | 24 | const to = contractAddress; 25 | const contract = new node.eth.Contract(abi); 26 | 27 | const writeReceipt = await node.priv 28 | .generateAndSendRawTransaction({ 29 | to, 30 | data: contract.methods.store([value]).encodeABI(), 31 | privateFrom: enclaveKey, 32 | privacyGroupId, 33 | privateKey: network.node1.privateKey, 34 | }) 35 | .then((transactionHash) => { 36 | return node.priv.waitForTransactionReceipt(transactionHash); 37 | }); 38 | 39 | console.log(writeReceipt); 40 | } 41 | 42 | run(); 43 | -------------------------------------------------------------------------------- /example/eventLogs/setup.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const Web3Quorum = require("../../src"); 5 | 6 | const { network, enclave } = require("../keys"); 7 | 8 | const bytecode = fs.readFileSync( 9 | path.join(__dirname, "../solidity/EventEmitter/EventEmitter.bin") 10 | ); 11 | 12 | const provider = new Web3.providers.HttpProvider(network.node1.url); 13 | const node = new Web3Quorum(new Web3(provider)); 14 | 15 | async function run() { 16 | const enclaveKey = enclave.node1.publicKey; 17 | const addresses = [enclave.node1.publicKey, enclave.node2.publicKey]; 18 | 19 | // create privacy group 20 | const privacyGroupId = await node.priv.createPrivacyGroup({ addresses }); 21 | console.log("Created privacy group", privacyGroupId); 22 | 23 | // deploy contract 24 | const deployReceipt = await node.priv 25 | .generateAndSendRawTransaction({ 26 | data: `0x${bytecode}`, 27 | privateFrom: enclaveKey, 28 | privacyGroupId, 29 | privateKey: network.node1.privateKey, 30 | }) 31 | .then((hash) => { 32 | return node.priv.waitForTransactionReceipt(hash); 33 | }); 34 | 35 | const { contractAddress, blockNumber } = deployReceipt; 36 | console.log("deployed", contractAddress); 37 | 38 | // save to file 39 | const params = { 40 | privacyGroupId, 41 | contractAddress, 42 | blockNumber, 43 | }; 44 | 45 | fs.writeFileSync(path.join(__dirname, "params.json"), JSON.stringify(params)); 46 | 47 | console.log(params); 48 | } 49 | 50 | run(); 51 | -------------------------------------------------------------------------------- /example/eventLogs/subscribe.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Web3 = require("web3"); 4 | const Web3Quorum = require("../../src"); 5 | 6 | const { network } = require("../keys"); 7 | 8 | const node = new Web3Quorum(new Web3(network.node1.url)); 9 | const params = JSON.parse(fs.readFileSync(path.join(__dirname, "params.json"))); 10 | 11 | async function run() { 12 | const { privacyGroupId, contractAddress: address, blockNumber } = params; 13 | 14 | const filter = { 15 | address, 16 | fromBlock: blockNumber, 17 | }; 18 | 19 | // Set the polling interval to something fairly high 20 | node.priv.subscriptionPollingInterval = 5000; 21 | 22 | console.log("Installing filter", filter); 23 | 24 | // Create subscription 25 | return node.priv 26 | .subscribeWithPooling(privacyGroupId, filter, (error, result) => { 27 | if (!error) { 28 | console.log("Installed filter", result); 29 | } else { 30 | console.error("Problem installing filter", error); 31 | throw error; 32 | } 33 | }) 34 | .then((subscription) => { 35 | // Add handler for each log received 36 | subscription 37 | .on("data", (log) => { 38 | console.log("LOG =>", log); 39 | }) 40 | .on("error", console.error); 41 | 42 | // Unsubscribe on interrupt 43 | process.on("SIGINT", async () => { 44 | console.log("unsubscribing"); 45 | await subscription.unsubscribe((error, success) => { 46 | if (!error) { 47 | console.log("Unsubscribed:", success); 48 | } else { 49 | console.log("Failed to unsubscribe:", error); 50 | } 51 | }); 52 | }); 53 | 54 | return subscription; 55 | }); 56 | } 57 | 58 | run(); 59 | -------------------------------------------------------------------------------- /example/eventLogs/subscribeWebSockets.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Web3 = require("web3"); 4 | const Web3Quorum = require("../../src"); 5 | 6 | const { network } = require("../keys"); 7 | 8 | const node = new Web3Quorum(new Web3(network.node1.wsUrl)); 9 | const params = JSON.parse(fs.readFileSync(path.join(__dirname, "params.json"))); 10 | 11 | async function run() { 12 | const { privacyGroupId, contractAddress: address, blockNumber } = params; 13 | 14 | const filter = { 15 | address, 16 | fromBlock: blockNumber, 17 | }; 18 | 19 | console.log("Installing filter", filter); 20 | 21 | // Create subscription 22 | return node.priv 23 | .subscribeWithPooling(privacyGroupId, filter, (error, result) => { 24 | if (!error) { 25 | console.log("Installed filter", result); 26 | } else { 27 | console.error("Problem installing filter:", error); 28 | throw error; 29 | } 30 | }) 31 | .then(async (subscription) => { 32 | // Add handlers for incoming events 33 | subscription 34 | .on("data", (log) => { 35 | if (log.result != null) { 36 | // Logs from subscription are nested in `result` key 37 | console.log("LOG =>", log.result); 38 | } else { 39 | console.log("LOG =>", log); 40 | } 41 | }) 42 | .on("error", console.error); 43 | 44 | // Unsubscribe and disconnect on interrupt 45 | process.on("SIGINT", async () => { 46 | console.log("unsubscribing"); 47 | await subscription.unsubscribe((error, success) => { 48 | if (!error) { 49 | console.log("Unsubscribed:", success); 50 | } else { 51 | console.error("Failed to unsubscribe:", error); 52 | } 53 | 54 | node.currentProvider.disconnect(); 55 | }); 56 | }); 57 | 58 | return subscription; 59 | }) 60 | .catch((error) => { 61 | console.error(error); 62 | }); 63 | } 64 | 65 | run(); 66 | -------------------------------------------------------------------------------- /example/helpers.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | 3 | function logMatchingGroup(findResult, pgId) { 4 | const groups = Object.values(findResult); 5 | console.log(`TOTAL number of matching privacy groups = ${groups.length}`); 6 | groups.forEach((group) => { 7 | // only log the group with the given id 8 | if (group.privacyGroupId === pgId) { 9 | console.log("FIND Privacy Group Result:"); 10 | console.log(group); 11 | } 12 | }); 13 | } 14 | 15 | function createHttpProvider(jwtToken, besuNodeUrl) { 16 | return new Web3.providers.HttpProvider(besuNodeUrl, { 17 | headers: [ 18 | { 19 | name: "Authorization", 20 | value: `Bearer ${jwtToken}`, 21 | }, 22 | ], 23 | }); 24 | } 25 | 26 | module.exports = { 27 | logMatchingGroup, 28 | createHttpProvider, 29 | }; 30 | -------------------------------------------------------------------------------- /example/keys.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | enclave: { 3 | node1: { 4 | publicKey: "BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo=", 5 | }, 6 | node2: { 7 | publicKey: "QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc=", 8 | }, 9 | node3: { 10 | publicKey: "1iTZde/ndBHvzhcl7V68x44Vx7pl8nwx9LqnM/AfJUg=", 11 | }, 12 | }, 13 | network: { 14 | node1: { 15 | url: "http://localhost:20000", 16 | wsUrl: "ws://localhost:20001", 17 | privateKey: 18 | "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 19 | }, 20 | node2: { 21 | url: "http://localhost:20002", 22 | wsUrl: "ws://localhost:20003", 23 | privateKey: 24 | "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", 25 | }, 26 | node3: { 27 | url: "http://localhost:20004", 28 | wsUrl: "ws://localhost:20005", 29 | privateKey: 30 | "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f", 31 | }, 32 | }, 33 | }; 34 | -------------------------------------------------------------------------------- /example/multiNodeExample/deployContract.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const Web3 = require("web3"); 5 | const Web3Quorum = require("../../src"); 6 | 7 | const { enclave, network } = require("../keys.js"); 8 | 9 | const binary = fs.readFileSync( 10 | path.join(__dirname, "../solidity/EventEmitter/EventEmitter.bin") 11 | ); 12 | 13 | const web3 = new Web3Quorum(new Web3(network.node1.url)); 14 | 15 | const createPrivateEmitterContract = () => { 16 | const contractOptions = { 17 | data: `0x${binary}`, 18 | privateFrom: enclave.node1.publicKey, 19 | privateFor: [enclave.node2.publicKey], 20 | privateKey: network.node1.privateKey, 21 | }; 22 | return web3.priv.generateAndSendRawTransaction(contractOptions); 23 | }; 24 | 25 | const getPrivateContractAddress = (transactionHash) => { 26 | console.log("Transaction Hash ", transactionHash); 27 | return web3.priv 28 | .waitForTransactionReceipt(transactionHash) 29 | .then((privateTransactionReceipt) => { 30 | console.log("Private Transaction Receipt\n", privateTransactionReceipt); 31 | console.log( 32 | `now you have to run:\n export CONTRACT_ADDRESS=${privateTransactionReceipt.contractAddress}\n` 33 | ); 34 | return privateTransactionReceipt.contractAddress; 35 | }); 36 | }; 37 | 38 | module.exports = () => { 39 | return createPrivateEmitterContract() 40 | .then(getPrivateContractAddress) 41 | .catch((error) => { 42 | console.log(error); 43 | console.log( 44 | "\nThis example requires ONCHAIN privacy to be DISABLED. \nCheck config for ONCHAIN privacy groups." 45 | ); 46 | }); 47 | }; 48 | 49 | if (require.main === module) { 50 | module.exports(); 51 | } 52 | -------------------------------------------------------------------------------- /example/multiNodeExample/storeValueFromNode1.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | const EventEmitterAbi = require("../solidity/EventEmitter/EventEmitter.json") 4 | .output.abi; 5 | 6 | const { enclave, network } = require("../keys.js"); 7 | 8 | const storeValueFromNode1 = (address, value) => { 9 | const web3 = new Web3Quorum(new Web3(network.node1.url)); 10 | const contract = new web3.eth.Contract(EventEmitterAbi); 11 | 12 | const functionAbi = contract._jsonInterface.find((e) => { 13 | return e.name === "store"; 14 | }); 15 | const functionArgs = web3.eth.abi 16 | .encodeParameters(functionAbi.inputs, [value]) 17 | .slice(2); 18 | 19 | const functionCall = { 20 | to: address, 21 | data: functionAbi.signature + functionArgs, 22 | privateFrom: enclave.node1.publicKey, 23 | privateFor: [enclave.node2.publicKey], 24 | privateKey: network.node1.privateKey, 25 | }; 26 | return web3.priv 27 | .generateAndSendRawTransaction(functionCall) 28 | .then((transactionHash) => { 29 | console.log("Transaction Hash:", transactionHash); 30 | return web3.priv.waitForTransactionReceipt(transactionHash); 31 | }) 32 | .then((result) => { 33 | console.log("Event Emitted:", result.logs[0].data); 34 | return result; 35 | }); 36 | }; 37 | 38 | const getValue = (url, address, privateFrom, privateFor, privateKey) => { 39 | const web3 = new Web3Quorum(new Web3(url)); 40 | 41 | const contract = new web3.eth.Contract(EventEmitterAbi); 42 | 43 | const functionAbi = contract._jsonInterface.find((e) => { 44 | return e.name === "value"; 45 | }); 46 | 47 | const functionCall = { 48 | to: address, 49 | data: functionAbi.signature, 50 | privateFrom, 51 | privateFor, 52 | privateKey, 53 | }; 54 | 55 | return web3.priv 56 | .generateAndSendRawTransaction(functionCall) 57 | .then((transactionHash) => { 58 | return web3.priv.waitForTransactionReceipt(transactionHash); 59 | }) 60 | .then((result) => { 61 | console.log(`Get Value from ${url}:`, result.output); 62 | return result; 63 | }); 64 | }; 65 | 66 | const getValueFromNode1 = (address) => { 67 | return getValue( 68 | network.node1.url, 69 | address, 70 | enclave.node1.publicKey, 71 | [enclave.node2.publicKey], 72 | network.node1.privateKey 73 | ); 74 | }; 75 | 76 | const getValueFromNode2 = (address) => { 77 | return getValue( 78 | network.node2.url, 79 | address, 80 | enclave.node2.publicKey, 81 | [enclave.node1.publicKey], 82 | network.node2.privateKey 83 | ); 84 | }; 85 | 86 | const getValueFromNode3 = (address) => { 87 | return getValue( 88 | network.node3.url, 89 | address, 90 | enclave.node3.publicKey, 91 | [enclave.node1.publicKey], 92 | network.node3.privateKey 93 | ); 94 | }; 95 | 96 | module.exports = { 97 | storeValueFromNode1, 98 | getValueFromNode1, 99 | getValueFromNode2, 100 | getValueFromNode3, 101 | }; 102 | 103 | if (require.main === module) { 104 | if (!process.env.CONTRACT_ADDRESS) { 105 | throw Error( 106 | "You need to export the following variable in your shell environment: CONTRACT_ADDRESS=" 107 | ); 108 | } 109 | 110 | const address = process.env.CONTRACT_ADDRESS; 111 | storeValueFromNode1(address, 1000) 112 | .then(() => { 113 | return getValueFromNode1(address); 114 | }) 115 | .then(() => { 116 | return getValueFromNode2(address); 117 | }) 118 | .then(() => { 119 | return getValueFromNode3(address); 120 | }) 121 | .catch(console.log); 122 | } 123 | -------------------------------------------------------------------------------- /example/multiNodeExample/storeValueFromNode2.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | const EventEmitterAbi = require("../solidity/EventEmitter/EventEmitter.json") 4 | .output.abi; 5 | 6 | const { enclave, network } = require("../keys.js"); 7 | 8 | const storeValueFromNode2 = (address, value) => { 9 | const web3 = new Web3Quorum(new Web3(network.node2.url)); 10 | const contract = new web3.eth.Contract(EventEmitterAbi); 11 | 12 | const functionAbi = contract._jsonInterface.find((e) => { 13 | return e.name === "store"; 14 | }); 15 | const functionArgs = web3.eth.abi 16 | .encodeParameters(functionAbi.inputs, [value]) 17 | .slice(2); 18 | 19 | const functionCall = { 20 | to: address, 21 | data: functionAbi.signature + functionArgs, 22 | privateFrom: enclave.node2.publicKey, 23 | privateFor: [enclave.node1.publicKey], 24 | privateKey: network.node2.privateKey, 25 | }; 26 | return web3.priv 27 | .generateAndSendRawTransaction(functionCall) 28 | .then((transactionHash) => { 29 | console.log("Transaction Hash:", transactionHash); 30 | return web3.priv.waitForTransactionReceipt(transactionHash); 31 | }) 32 | .then((result) => { 33 | console.log("Event Emitted:", result.logs[0].data); 34 | return result; 35 | }); 36 | }; 37 | 38 | const getValue = (url, address, privateFrom, privateFor, privateKey) => { 39 | const web3 = new Web3Quorum(new Web3(url)); 40 | const contract = new web3.eth.Contract(EventEmitterAbi); 41 | 42 | const functionAbi = contract._jsonInterface.find((e) => { 43 | return e.name === "value"; 44 | }); 45 | 46 | const functionCall = { 47 | to: address, 48 | data: functionAbi.signature, 49 | privateFrom, 50 | privateFor, 51 | privateKey, 52 | }; 53 | 54 | return web3.priv 55 | .generateAndSendRawTransaction(functionCall) 56 | .then((transactionHash) => { 57 | return web3.priv.waitForTransactionReceipt(transactionHash); 58 | }) 59 | .then((result) => { 60 | console.log(`Get Value from ${url}:`, result.output); 61 | return result; 62 | }); 63 | }; 64 | 65 | const getValueFromNode1 = (address) => { 66 | return getValue( 67 | network.node1.url, 68 | address, 69 | enclave.node1.publicKey, 70 | [enclave.node2.publicKey], 71 | network.node1.privateKey 72 | ); 73 | }; 74 | 75 | const getValueFromNode2 = (address) => { 76 | return getValue( 77 | network.node2.url, 78 | address, 79 | enclave.node2.publicKey, 80 | [enclave.node1.publicKey], 81 | network.node2.privateKey 82 | ); 83 | }; 84 | 85 | const getValueFromNode3 = (address) => { 86 | return getValue( 87 | network.node3.url, 88 | address, 89 | enclave.node3.publicKey, 90 | [enclave.node1.publicKey], 91 | network.node3.privateKey 92 | ); 93 | }; 94 | 95 | module.exports = { 96 | storeValueFromNode2, 97 | getValueFromNode1, 98 | getValueFromNode2, 99 | getValueFromNode3, 100 | }; 101 | 102 | if (require.main === module) { 103 | if (!process.env.CONTRACT_ADDRESS) { 104 | throw Error( 105 | "You need to export the following variable in your shell environment: CONTRACT_ADDRESS=" 106 | ); 107 | } 108 | 109 | const address = process.env.CONTRACT_ADDRESS; 110 | storeValueFromNode2(address, 42) 111 | .then(() => { 112 | return getValueFromNode1(address); 113 | }) 114 | .then(() => { 115 | return getValueFromNode2(address); 116 | }) 117 | .then(() => { 118 | return getValueFromNode3(address); 119 | }) 120 | .catch(console.log); 121 | } 122 | -------------------------------------------------------------------------------- /example/multiNodeExamplePrivacyGroup/deployContract.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const Web3 = require("web3"); 5 | const Web3Quorum = require("../../src"); 6 | 7 | const createGroup = require("../privacyGroupManagement/createPrivacyGroup"); 8 | 9 | const { enclave, network } = require("../keys.js"); 10 | 11 | const binary = fs.readFileSync( 12 | path.join(__dirname, "../solidity/EventEmitter/EventEmitter.bin") 13 | ); 14 | 15 | const web3 = new Web3Quorum(new Web3(network.node1.url)); 16 | 17 | const createGroupId = () => { 18 | return createGroup.createPrivacyGroup(); 19 | }; 20 | 21 | const createPrivateEmitterContract = (privacyGroupId) => { 22 | const contractOptions = { 23 | data: `0x${binary}`, 24 | privateFrom: enclave.node1.publicKey, 25 | privacyGroupId, 26 | privateKey: network.node1.privateKey, 27 | }; 28 | return web3.priv.generateAndSendRawTransaction(contractOptions); 29 | }; 30 | 31 | const getPrivateContractAddress = (transactionHash) => { 32 | console.log("Transaction Hash ", transactionHash); 33 | return web3.priv 34 | .waitForTransactionReceipt(transactionHash) 35 | .then((privateTransactionReceipt) => { 36 | console.log("Private Transaction Receipt\n", privateTransactionReceipt); 37 | 38 | return privateTransactionReceipt.contractAddress; 39 | }); 40 | }; 41 | 42 | module.exports = async () => { 43 | const privacyGroupId = await createGroupId(); 44 | const contractAddress = await createPrivateEmitterContract(privacyGroupId) 45 | .then(getPrivateContractAddress) 46 | .catch(console.error); 47 | console.log( 48 | `now you have to run:\n export CONTRACT_ADDRESS=${contractAddress}` 49 | ); 50 | console.log(` export PRIVACY_GROUP_ID=${privacyGroupId}`); 51 | 52 | return { contractAddress, privacyGroupId }; 53 | }; 54 | 55 | if (require.main === module) { 56 | module.exports().catch((error) => { 57 | console.log(error); 58 | console.log( 59 | "\nThis example requires ONCHAIN privacy to be DISABLED. \nCheck config for ONCHAIN privacy groups." 60 | ); 61 | }); 62 | } 63 | -------------------------------------------------------------------------------- /example/multiNodeExamplePrivateCall/deployContract.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const Web3 = require("web3"); 5 | const Web3Quorum = require("../../src"); 6 | 7 | const createGroup = require("../privacyGroupManagement/createPrivacyGroup"); 8 | 9 | const { enclave, network } = require("../keys.js"); 10 | 11 | const binary = fs.readFileSync( 12 | path.join(__dirname, "../solidity/EventEmitter/EventEmitter.bin") 13 | ); 14 | 15 | const web3 = new Web3Quorum(new Web3(network.node1.url)); 16 | 17 | const createGroupId = () => { 18 | return createGroup.createPrivacyGroup(); 19 | }; 20 | 21 | const createPrivateEmitterContract = (privacyGroupId) => { 22 | const contractOptions = { 23 | data: `0x${binary}`, 24 | privateFrom: enclave.node1.publicKey, 25 | privacyGroupId, 26 | privateKey: network.node1.privateKey, 27 | }; 28 | return web3.priv.generateAndSendRawTransaction(contractOptions); 29 | }; 30 | 31 | const getPrivateContractAddress = (transactionHash) => { 32 | console.log("Transaction Hash ", transactionHash); 33 | return web3.priv 34 | .waitForTransactionReceipt(transactionHash) 35 | .then((privateTransactionReceipt) => { 36 | console.log("Private Transaction Receipt\n", privateTransactionReceipt); 37 | return privateTransactionReceipt.contractAddress; 38 | }); 39 | }; 40 | 41 | module.exports = async () => { 42 | const privacyGroupId = await createGroupId(); 43 | const contractAddress = await createPrivateEmitterContract( 44 | privacyGroupId 45 | ).then(getPrivateContractAddress); 46 | console.log( 47 | `now you have to run:\n export CONTRACT_ADDRESS=${contractAddress}` 48 | ); 49 | console.log(` export PRIVACY_GROUP_ID=${privacyGroupId}`); 50 | return { contractAddress, privacyGroupId }; 51 | }; 52 | 53 | if (require.main === module) { 54 | module.exports().catch((error) => { 55 | console.log(error); 56 | console.log( 57 | "\nThis example requires ONCHAIN privacy to be DISABLED. \nCheck config for ONCHAIN privacy groups." 58 | ); 59 | }); 60 | } 61 | -------------------------------------------------------------------------------- /example/multiNodeExamplePrivateCall/storeValueFromNode1.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | const EventEmitterAbi = require("../solidity/EventEmitter/EventEmitter.json") 4 | .output.abi; 5 | 6 | const { enclave, network } = require("../keys.js"); 7 | 8 | const storeValueFromNode1 = (address, value, privacyGroupId) => { 9 | const web3 = new Web3Quorum(new Web3(network.node1.url)); 10 | const contract = new web3.eth.Contract(EventEmitterAbi); 11 | 12 | const functionAbi = contract._jsonInterface.find((e) => { 13 | return e.name === "store"; 14 | }); 15 | const functionArgs = web3.eth.abi 16 | .encodeParameters(functionAbi.inputs, [value]) 17 | .slice(2); 18 | 19 | const functionCall = { 20 | to: address, 21 | data: functionAbi.signature + functionArgs, 22 | privateFrom: enclave.node1.publicKey, 23 | privacyGroupId, 24 | privateKey: network.node1.privateKey, 25 | }; 26 | return web3.priv 27 | .generateAndSendRawTransaction(functionCall) 28 | .then((transactionHash) => { 29 | console.log("Transaction Hash:", transactionHash); 30 | return web3.priv.waitForTransactionReceipt(transactionHash); 31 | }) 32 | .then((result) => { 33 | console.log("Event Emitted:", result.logs[0].data); 34 | return result; 35 | }); 36 | }; 37 | 38 | const getValue = (url, address, privacyGroupId) => { 39 | const web3 = new Web3Quorum(new Web3(url)); 40 | const contract = new web3.eth.Contract(EventEmitterAbi); 41 | 42 | const functionAbi = contract._jsonInterface.find((e) => { 43 | return e.name === "value"; 44 | }); 45 | 46 | return web3.priv 47 | .call(privacyGroupId, { to: address, data: functionAbi.signature }) 48 | .then((result) => { 49 | console.log(`Get Value from ${url}:`, result); 50 | return result; 51 | }); 52 | }; 53 | 54 | const getValueFromNode1 = (address, privacyGroupId) => { 55 | return getValue(network.node1.url, address, privacyGroupId); 56 | }; 57 | 58 | const getValueFromNode2 = (address, privacyGroupId) => { 59 | return getValue(network.node2.url, address, privacyGroupId); 60 | }; 61 | 62 | const getValueFromNode3 = (address, privacyGroupId) => { 63 | return getValue(network.node3.url, address, privacyGroupId); 64 | }; 65 | 66 | module.exports = { 67 | storeValueFromNode1, 68 | getValueFromNode1, 69 | getValueFromNode2, 70 | getValueFromNode3, 71 | }; 72 | 73 | if (require.main === module) { 74 | if (!process.env.CONTRACT_ADDRESS) { 75 | throw Error( 76 | "You need to export the following variable in your shell environment: CONTRACT_ADDRESS=" 77 | ); 78 | } 79 | 80 | if (!process.env.PRIVACY_GROUP_ID) { 81 | throw Error( 82 | "You need to export the following variable in your shell environment: PRIVACY_GROUP_ID=" 83 | ); 84 | } 85 | 86 | const address = process.env.CONTRACT_ADDRESS; 87 | const privacyGroupId = process.env.PRIVACY_GROUP_ID; 88 | storeValueFromNode1(address, 1000, privacyGroupId) 89 | .then(() => { 90 | return getValueFromNode1(address, privacyGroupId); 91 | }) 92 | .then(() => { 93 | return getValueFromNode2(address, privacyGroupId); 94 | }) 95 | .then(() => { 96 | return getValueFromNode3(address, privacyGroupId); 97 | }) 98 | .catch(console.log); 99 | } 100 | -------------------------------------------------------------------------------- /example/multiNodeExamplePrivateCall/storeValueFromNode2.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | const EventEmitterAbi = require("../solidity/EventEmitter/EventEmitter.json") 4 | .output.abi; 5 | 6 | const { enclave, network } = require("../keys.js"); 7 | 8 | const storeValueFromNode2 = (address, value, privacyGroupId) => { 9 | const web3 = new Web3Quorum(new Web3(network.node2.url)); 10 | const contract = new web3.eth.Contract(EventEmitterAbi); 11 | 12 | const functionAbi = contract._jsonInterface.find((e) => { 13 | return e.name === "store"; 14 | }); 15 | const functionArgs = web3.eth.abi 16 | .encodeParameters(functionAbi.inputs, [value]) 17 | .slice(2); 18 | 19 | const functionCall = { 20 | to: address, 21 | data: functionAbi.signature + functionArgs, 22 | privateFrom: enclave.node2.publicKey, 23 | privacyGroupId, 24 | privateKey: network.node2.privateKey, 25 | }; 26 | return web3.priv 27 | .generateAndSendRawTransaction(functionCall) 28 | .then((transactionHash) => { 29 | console.log("Transaction Hash:", transactionHash); 30 | return web3.priv.waitForTransactionReceipt(transactionHash); 31 | }) 32 | .then((result) => { 33 | console.log("Event Emitted:", result.logs[0].data); 34 | return result; 35 | }); 36 | }; 37 | 38 | const getValue = (url, address, privacyGroupId) => { 39 | const web3 = new Web3Quorum(new Web3(url)); 40 | const contract = new web3.eth.Contract(EventEmitterAbi); 41 | 42 | const functionAbi = contract._jsonInterface.find((e) => { 43 | return e.name === "value"; 44 | }); 45 | 46 | return web3.priv 47 | .call(privacyGroupId, { to: address, data: functionAbi.signature }) 48 | .then((result) => { 49 | console.log(`Get Value from ${url}:`, result); 50 | return result; 51 | }); 52 | }; 53 | 54 | const getValueFromNode1 = (address, privacyGroupId) => { 55 | return getValue(network.node1.url, address, privacyGroupId); 56 | }; 57 | 58 | const getValueFromNode2 = (address, privacyGroupId) => { 59 | return getValue(network.node2.url, address, privacyGroupId); 60 | }; 61 | 62 | const getValueFromNode3 = (address, privacyGroupId) => { 63 | return getValue(network.node3.url, address, privacyGroupId); 64 | }; 65 | 66 | module.exports = { 67 | storeValueFromNode2, 68 | getValueFromNode1, 69 | getValueFromNode2, 70 | getValueFromNode3, 71 | }; 72 | 73 | if (require.main === module) { 74 | if (!process.env.CONTRACT_ADDRESS) { 75 | throw Error( 76 | "You need to export the following variable in your shell environment: CONTRACT_ADDRESS=" 77 | ); 78 | } 79 | 80 | if (!process.env.PRIVACY_GROUP_ID) { 81 | throw Error( 82 | "You need to export the following variable in your shell environment: PRIVACY_GROUP_ID=" 83 | ); 84 | } 85 | 86 | const address = process.env.CONTRACT_ADDRESS; 87 | const privacyGroupId = process.env.PRIVACY_GROUP_ID; 88 | storeValueFromNode2(address, 42, privacyGroupId) 89 | .then(() => { 90 | return getValueFromNode1(address, privacyGroupId); 91 | }) 92 | .then(() => { 93 | return getValueFromNode2(address, privacyGroupId); 94 | }) 95 | .then(() => { 96 | return getValueFromNode3(address, privacyGroupId); 97 | }) 98 | .catch(console.log); 99 | } 100 | -------------------------------------------------------------------------------- /example/multiTenancy/README.md: -------------------------------------------------------------------------------- 1 | ## Examples using Multi-tenancy 2 | In order to use multi-tenancy examples, you need to be running Besu and enclave in multi-tenancy mode. Add extra enclave keys and JWT authentication tokens to keys.js. This is for running examples only, not for use in production environments. 3 | 4 | For example, to add a second tenant on node 1 5 | 6 | ```json 7 | { enclave: { 8 | ... 9 | node11: { 10 | publicKey: "dyKxmO5Ji7d8aZTXvk02x98l6oB9Q4MBTq6W4tIW+AM=", 11 | jwt: 12 | "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJwZXJtaXNzaW9ucyI6WyIqOioiXSwicHJpdmFjeVB1YmxpY0tleSI6ImR5S3htTzVKaTdkOGFaVFh2azAyeDk4bDZvQjlRNE1CVHE2VzR0SVcrQU09IiwiZXhwIjoxNjAwODk5OTk5MDAyfQ.psocMuOFSIIpiU6xFFLAvENGLDaTGc9nvGKQRz2OizT_sVZZowcewDWdOK5ZPDvaLSbweLNlnrDEycmNhLB0coGDf-gqK7pgeN_rMn4vMPFyBaeV3DoPnQzNl9JYrldPRzEv70Z6MInKy4mYm649Owow9K_MNuHTUjPdUZOypUVVRBae94B6PgQFrrWZnwZ3wjfZyc-e8cF8s_Ao067xjkoomBA-asYnPuMwTsyjdykypNx2Y0_cdjc8t-F1n2xWLqEvbx8QmrMNk9_2o9fURCSMd4QDq6dqswQOveTTTw2FbhicH9_dSmg_J64lFoLkg7BEDJ5yUIeZ2rF6ytv-wQ" 13 | } 14 | } 15 | } 16 | ``` -------------------------------------------------------------------------------- /example/multiTenancy/createPrivacyGroupMultiTenancy.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | const { enclave, network } = require("../keys.js"); 4 | const { createHttpProvider } = require("../helpers.js"); 5 | 6 | const web3 = new Web3Quorum( 7 | new Web3(createHttpProvider(enclave.node1.jwt, network.node1.url)) 8 | ); 9 | 10 | const createPrivacyGroup = () => { 11 | const contractOptions = { 12 | addresses: [enclave.node1.publicKey, enclave.node12.publicKey], 13 | name: "web3js-quorum", 14 | description: "test", 15 | }; 16 | return web3.priv.createPrivacyGroup(contractOptions).then((result) => { 17 | console.log(`The privacy group created is:`, result); 18 | return result; 19 | }); 20 | }; 21 | 22 | const createPrivacyGroupForNode123 = () => { 23 | const contractOptions = { 24 | addresses: [ 25 | enclave.node1.publicKey, 26 | enclave.node11.publicKey, 27 | enclave.node12.publicKey, 28 | ], 29 | name: "web3js-quorum", 30 | description: "test", 31 | }; 32 | return web3.priv.createPrivacyGroup(contractOptions).then((result) => { 33 | console.log(`The privacy group created is:`, result); 34 | return result; 35 | }); 36 | }; 37 | 38 | module.exports = { 39 | createPrivacyGroup, 40 | createPrivacyGroupForNode123, 41 | }; 42 | 43 | if (require.main === module) { 44 | createPrivacyGroup(); 45 | } 46 | -------------------------------------------------------------------------------- /example/multiTenancy/onchainPrivacyAddRemoveExample.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | 4 | const { enclave, network } = require("../keys.js"); 5 | const { logMatchingGroup, createHttpProvider } = require("../helpers.js"); 6 | 7 | const node1 = new Web3Quorum( 8 | new Web3(createHttpProvider(enclave.node1.jwt, network.node1.url)) 9 | ); 10 | const node2 = new Web3Quorum( 11 | new Web3(createHttpProvider(enclave.node2.jwt, network.node2.url)) 12 | ); 13 | // in this example node3 is a second tenant on besu/enclave node1 with enclave key enclave11 14 | const node3 = new Web3Quorum( 15 | new Web3(createHttpProvider(enclave.node11.jwt, network.node1.url)) 16 | ); 17 | 18 | module.exports = async () => { 19 | const onChainPrivacyGroupCreationResult = await node1.eth.flexiblePrivacyGroup.create( 20 | { 21 | participants: [enclave.node1.publicKey, enclave.node2.publicKey], 22 | enclaveKey: enclave.node1.publicKey, 23 | privateFrom: enclave.node1.publicKey, 24 | privateKey: network.node1.privateKey, 25 | } 26 | ); 27 | console.log("Created new on-chain privacy group:"); 28 | console.log(onChainPrivacyGroupCreationResult); 29 | 30 | const findResult = await node2.eth.flexiblePrivacyGroup.find([ 31 | enclave.node1.publicKey, 32 | enclave.node2.publicKey, 33 | ]); 34 | console.log("Found privacy group results:"); 35 | logMatchingGroup( 36 | findResult, 37 | onChainPrivacyGroupCreationResult.privacyGroupId 38 | ); 39 | 40 | const addResult = await node1.eth.flexiblePrivacyGroup.addTo({ 41 | participants: [enclave.node11.publicKey], 42 | enclaveKey: enclave.node1.publicKey, 43 | privateFrom: enclave.node1.publicKey, 44 | privacyGroupId: onChainPrivacyGroupCreationResult.privacyGroupId, 45 | privateKey: network.node1.privateKey, 46 | }); 47 | console.log("Added new node to privacy group:"); 48 | console.log(addResult); 49 | 50 | const receiptFromNode3 = await node3.priv.waitForTransactionReceipt( 51 | addResult.commitmentHash 52 | ); 53 | console.log("Got transaction receipt from added node:"); 54 | console.log(receiptFromNode3); 55 | 56 | const findResultWithAddedNode = await node2.eth.flexiblePrivacyGroup.find([ 57 | enclave.node1.publicKey, 58 | enclave.node2.publicKey, 59 | enclave.node11.publicKey, 60 | ]); 61 | console.log("Found privacy groups with added node:"); 62 | logMatchingGroup( 63 | findResultWithAddedNode, 64 | onChainPrivacyGroupCreationResult.privacyGroupId 65 | ); 66 | 67 | const removeResult = await node1.eth.flexiblePrivacyGroup.removeFrom({ 68 | participant: enclave.node11.publicKey, 69 | enclaveKey: enclave.node1.publicKey, 70 | privateFrom: enclave.node1.publicKey, 71 | privacyGroupId: onChainPrivacyGroupCreationResult.privacyGroupId, 72 | privateKey: network.node1.privateKey, 73 | }); 74 | console.log("Removed third participant from privacy group:"); 75 | console.log(removeResult); 76 | 77 | const findResultRemovedNode = await node2.eth.flexiblePrivacyGroup.find([ 78 | enclave.node1.publicKey, 79 | enclave.node2.publicKey, 80 | ]); 81 | logMatchingGroup( 82 | findResultRemovedNode, 83 | onChainPrivacyGroupCreationResult.privacyGroupId 84 | ); 85 | }; 86 | 87 | if (require.main === module) { 88 | module.exports(); 89 | } 90 | -------------------------------------------------------------------------------- /example/multiTenancy/onchainPrivacySimpleExample.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | 4 | const { enclave, network } = require("../keys.js"); 5 | const { logMatchingGroup, createHttpProvider } = require("../helpers.js"); 6 | 7 | const node1 = new Web3Quorum( 8 | new Web3(createHttpProvider(enclave.node1.jwt, network.node1.url)) 9 | ); 10 | const node2 = new Web3Quorum( 11 | new Web3(createHttpProvider(enclave.node2.jwt, network.node2.url)) 12 | ); 13 | 14 | module.exports = async () => { 15 | const onChainPrivacyGroupCreationResult = await node1.eth.flexiblePrivacyGroup.create( 16 | { 17 | participants: [enclave.node1.publicKey, enclave.node2.publicKey], 18 | enclaveKey: enclave.node1.publicKey, 19 | privateFrom: enclave.node1.publicKey, 20 | privateKey: network.node1.privateKey, 21 | } 22 | ); 23 | console.log("Creation result"); 24 | console.log(onChainPrivacyGroupCreationResult); 25 | 26 | await node1.priv.waitForTransactionReceipt( 27 | onChainPrivacyGroupCreationResult.commitmentHash 28 | ); 29 | 30 | const findResult1 = await node1.eth.flexiblePrivacyGroup.find([ 31 | enclave.node1.publicKey, 32 | enclave.node2.publicKey, 33 | ]); 34 | console.log("finding groups on node1"); 35 | logMatchingGroup( 36 | findResult1, 37 | onChainPrivacyGroupCreationResult.privacyGroupId 38 | ); 39 | 40 | const findResult2 = await node2.eth.flexiblePrivacyGroup.find([ 41 | enclave.node1.publicKey, 42 | enclave.node2.publicKey, 43 | ]); 44 | console.log("finding groups on node2"); 45 | logMatchingGroup( 46 | findResult2, 47 | onChainPrivacyGroupCreationResult.privacyGroupId 48 | ); 49 | }; 50 | 51 | if (require.main === module) { 52 | module.exports().catch((error) => { 53 | console.log(error); 54 | console.log( 55 | "\nThis example requires ONCHAIN privacy (and JWT Auth) to be ENABLED. \nCheck Besu config." 56 | ); 57 | }); 58 | } 59 | -------------------------------------------------------------------------------- /example/multiTenancyExampleOnchainPrivacyEventLogs/README.md: -------------------------------------------------------------------------------- 1 | # Examples of getting event logs 2 | 3 | Scripts: 4 | - `setup.js` - create privacy group and deploy contract 5 | - `subscribe.js` - subscribe to new logs sent to the contract using the HTTP polling API 6 | - `subscribeWebSocket.js` - subscribe to new logs sent to the contract using the WebSocket pub-sub API 7 | - `sendTransaction.js` - send a transaction to update the value in the contract 8 | - `getPastLogs.js` - get past logs 9 | 10 | ## Usage 11 | Run `setup.js` to create a new privacy group and deploy an [EventEmitter](../solidity/EventEmitter/EventEmitter.sol) contract into it. The privacy group ID and the contract address will be saved in `params.json` to be used by the other scripts. 12 | 13 | Next, run `subscribe.js` or `subscribeWebSockets.js` to subscribe to logs for the contract. The script will print any past and incoming logs until exited. 14 | 15 | Run `sendTransaction.js` to update the value stored in the contract and emit a log. You can specify the value to store as a command line argument. 16 | ``` 17 | node sendTransaction.js 5 18 | ``` 19 | 20 | Each time you run the script, you should see a new log output from `subscribe.js`/`subscribeWebSocket.js`. 21 | 22 | Finally, run `getPastLogs.js` for all of the logs sent to the contract. 23 | 24 | ## flexible privacy groups 25 | To test behavior when adding and removing users from flexible privacy groups, additional scripts are provided. 26 | Once you have verified that logs are received with subscribe/subscribeWebSockets and sendTransaction as above, run `removeParticipantFromGroup.js` and you will see the subscription revoked. In besu logs you will see "Error processing JSON-RPC requestBody 27 | org.hyperledger.besu.ethereum.privacy.MultiTenancyValidationException: Privacy group must contain the enclave public key" 28 | run sendTransaction now and you won't see any logs 29 | then run `addParticipantToGroup.js` and you will see access reinstated, including logs that were sent while user was not a member of the group. 30 | -------------------------------------------------------------------------------- /example/multiTenancyExampleOnchainPrivacyEventLogs/addParticipantToGroup.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Web3 = require("web3"); 4 | const Web3Quorum = require("../../src"); 5 | 6 | const { network, enclave } = require("../keys"); 7 | const { createHttpProvider } = require("../helpers.js"); 8 | 9 | const node = new Web3Quorum( 10 | new Web3(createHttpProvider(enclave.node1.jwt, network.node1.url)) 11 | ); 12 | const params = JSON.parse(fs.readFileSync(path.join(__dirname, "params.json"))); 13 | 14 | async function run() { 15 | const { privacyGroupId } = params; 16 | const addressesToAdd = [enclave.node11.publicKey]; 17 | 18 | const addResult = await node.eth.flexiblePrivacyGroup.addTo({ 19 | participants: addressesToAdd, 20 | enclaveKey: enclave.node1.publicKey, 21 | privateFrom: enclave.node1.publicKey, 22 | privacyGroupId, 23 | privateKey: network.node1.privateKey, 24 | }); 25 | 26 | console.log(addResult); 27 | console.log( 28 | `Added new participant ${addressesToAdd} to privacy group ${privacyGroupId}` 29 | ); 30 | } 31 | 32 | run(); 33 | -------------------------------------------------------------------------------- /example/multiTenancyExampleOnchainPrivacyEventLogs/getPastLogs.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Web3 = require("web3"); 4 | const Web3Quorum = require("../../src"); 5 | 6 | const { network, enclave } = require("../keys"); 7 | const { createHttpProvider } = require("../helpers.js"); 8 | 9 | // use an enclave key that is not a member of the group (eg enclave.node11.jwt) 10 | // to demonstrate that they can't get the logs 11 | const node = new Web3Quorum( 12 | new Web3(createHttpProvider(enclave.node1.jwt, network.node1.url)) 13 | ); 14 | const params = JSON.parse(fs.readFileSync(path.join(__dirname, "params.json"))); 15 | 16 | function run() { 17 | const { privacyGroupId, contractAddress: address } = params; 18 | 19 | const filter = { 20 | address, 21 | }; 22 | 23 | return node.priv.getLogs(privacyGroupId, filter).then((logs) => { 24 | console.log("Received logs\n", logs); 25 | return logs; 26 | }); 27 | } 28 | 29 | run(); 30 | -------------------------------------------------------------------------------- /example/multiTenancyExampleOnchainPrivacyEventLogs/removeParticipantFromGroup.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Web3 = require("web3"); 4 | const Web3Quorum = require("../../src"); 5 | 6 | const { network, enclave } = require("../keys"); 7 | const { logMatchingGroup, createHttpProvider } = require("../helpers.js"); 8 | 9 | const node = new Web3Quorum( 10 | new Web3(createHttpProvider(enclave.node1.jwt, network.node1.url)) 11 | ); 12 | const params = JSON.parse(fs.readFileSync(path.join(__dirname, "params.json"))); 13 | 14 | async function run() { 15 | const { privacyGroupId } = params; 16 | const addressToRemove = enclave.node11.publicKey; 17 | 18 | const findResultWithAddedNode = await node.eth.flexiblePrivacyGroup.find([ 19 | enclave.node1.publicKey, 20 | enclave.node2.publicKey, 21 | enclave.node11.publicKey, 22 | ]); 23 | console.log("Found privacy groups with added node:"); 24 | logMatchingGroup(findResultWithAddedNode, privacyGroupId); 25 | 26 | const removeResult = await node.eth.flexiblePrivacyGroup.removeFrom({ 27 | participant: addressToRemove, 28 | enclaveKey: enclave.node1.publicKey, 29 | privateFrom: enclave.node1.publicKey, 30 | privacyGroupId, 31 | privateKey: network.node1.privateKey, 32 | }); 33 | console.log(removeResult); 34 | console.log( 35 | `Removed third participant ${addressToRemove} from privacy group ${privacyGroupId}` 36 | ); 37 | 38 | const findResultWithRemovedNode = await node.eth.flexiblePrivacyGroup.find([ 39 | enclave.node1.publicKey, 40 | enclave.node2.publicKey, 41 | ]); 42 | console.log("Found privacy groups with removed node:"); 43 | logMatchingGroup(findResultWithRemovedNode, privacyGroupId); 44 | } 45 | 46 | run(); 47 | -------------------------------------------------------------------------------- /example/multiTenancyExampleOnchainPrivacyEventLogs/sendTransaction.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Web3 = require("web3"); 4 | 5 | const { network, enclave } = require("../keys"); 6 | const { createHttpProvider } = require("../helpers.js"); 7 | const Web3Quorum = require("../../src"); 8 | 9 | const artifact = fs.readFileSync( 10 | path.join(__dirname, "../solidity/EventEmitter/EventEmitter.json") 11 | ); 12 | const { abi } = JSON.parse(artifact).output; 13 | const params = JSON.parse(fs.readFileSync(path.join(__dirname, "params.json"))); 14 | 15 | const node = new Web3Quorum( 16 | new Web3(createHttpProvider(enclave.node1.jwt, network.node1.url)) 17 | ); 18 | 19 | async function run() { 20 | const { privacyGroupId, contractAddress } = params; 21 | const enclaveKey = enclave.node1.publicKey; 22 | 23 | // send a transaction 24 | const args = process.argv.slice(2); 25 | const value = args.shift() || 3; 26 | 27 | const to = contractAddress; 28 | const contract = new node.eth.Contract(abi); 29 | 30 | const writeReceipt = await node.priv 31 | .generateAndSendRawTransaction({ 32 | to, 33 | data: contract.methods.store([value]).encodeABI(), 34 | privateFrom: enclaveKey, 35 | privacyGroupId, 36 | privateKey: network.node1.privateKey, 37 | }) 38 | .then((transactionHash) => { 39 | return node.priv.waitForTransactionReceipt(transactionHash); 40 | }); 41 | 42 | console.log(writeReceipt); 43 | } 44 | 45 | run(); 46 | -------------------------------------------------------------------------------- /example/multiTenancyExampleOnchainPrivacyEventLogs/setup.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const Web3Quorum = require("../../src"); 5 | 6 | const { network, enclave } = require("../keys"); 7 | const { createHttpProvider } = require("../helpers.js"); 8 | 9 | const bytecode = fs.readFileSync( 10 | path.join(__dirname, "../solidity/EventEmitter/EventEmitter.bin") 11 | ); 12 | 13 | const node = new Web3Quorum( 14 | new Web3(createHttpProvider(enclave.node1.jwt, network.node1.url)) 15 | ); 16 | 17 | async function run() { 18 | const enclaveKey = enclave.node1.publicKey; 19 | const addresses = [ 20 | enclave.node1.publicKey, 21 | enclave.node11.publicKey, 22 | enclave.node2.publicKey, 23 | ]; 24 | 25 | // create privacy group 26 | const onChainPrivacyGroupCreationResult = await node.eth.flexiblePrivacyGroup.create( 27 | { 28 | participants: addresses, 29 | enclaveKey: enclave.node1.publicKey, 30 | privateFrom: enclave.node1.publicKey, 31 | privateKey: network.node1.privateKey, 32 | } 33 | ); 34 | console.log("Creation result"); 35 | console.log(onChainPrivacyGroupCreationResult); 36 | 37 | await node.priv.waitForTransactionReceipt( 38 | onChainPrivacyGroupCreationResult.commitmentHash 39 | ); 40 | 41 | const { privacyGroupId } = onChainPrivacyGroupCreationResult; 42 | 43 | console.log("Created privacy group", privacyGroupId); 44 | console.log(`with members ${addresses}`); 45 | 46 | // deploy contract 47 | const deployReceipt = await node.priv 48 | .generateAndSendRawTransaction({ 49 | data: `0x${bytecode}`, 50 | privateFrom: enclaveKey, 51 | privacyGroupId, 52 | privateKey: network.node1.privateKey, 53 | }) 54 | .then((hash) => { 55 | return node.priv.waitForTransactionReceipt(hash); 56 | }); 57 | 58 | const { contractAddress, blockNumber } = deployReceipt; 59 | console.log("Deployed contract at address ", contractAddress); 60 | 61 | // save to file 62 | const params = { 63 | privacyGroupId, 64 | contractAddress, 65 | blockNumber, 66 | }; 67 | 68 | fs.writeFileSync(path.join(__dirname, "params.json"), JSON.stringify(params)); 69 | 70 | console.log(params); 71 | } 72 | 73 | run(); 74 | -------------------------------------------------------------------------------- /example/multiTenancyExampleOnchainPrivacyEventLogs/subscribe.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Web3 = require("web3"); 4 | const Web3Quorum = require("../../src"); 5 | 6 | const { network, enclave } = require("../keys"); 7 | const { createHttpProvider } = require("../helpers.js"); 8 | 9 | // use an enclave key that is not a member of the group (eg enclave.node11.jwt) 10 | // to demonstrate that they can't create a filter 11 | const node = new Web3Quorum( 12 | new Web3(createHttpProvider(enclave.node11.jwt, network.node1.url)) 13 | ); 14 | const params = JSON.parse(fs.readFileSync(path.join(__dirname, "params.json"))); 15 | 16 | async function run() { 17 | const { privacyGroupId, contractAddress: address, blockNumber } = params; 18 | 19 | const filter = { 20 | address, 21 | fromBlock: blockNumber, 22 | }; 23 | 24 | // Set the polling interval to something fairly high 25 | node.priv.subscriptionPollingInterval = 5000; 26 | 27 | console.log("Installing filter", filter); 28 | 29 | // Create subscription 30 | return node.priv 31 | .subscribeWithPooling(privacyGroupId, filter, (error, result) => { 32 | if (!error) { 33 | console.log("Installed filter", result); 34 | } else { 35 | console.error("Problem installing filter", error); 36 | throw error; 37 | } 38 | }) 39 | .then((subscription) => { 40 | // Add handler for each log received 41 | subscription 42 | .on("data", (log) => { 43 | console.log("LOG =>", log); 44 | }) 45 | .on("error", console.error); 46 | 47 | // Unsubscribe on interrupt 48 | process.on("SIGINT", async () => { 49 | console.log("unsubscribing"); 50 | await subscription.unsubscribe((error, success) => { 51 | if (!error) { 52 | console.log("Unsubscribed:", success); 53 | } else { 54 | console.log("Failed to unsubscribe:", error); 55 | } 56 | }); 57 | }); 58 | 59 | return subscription; 60 | }); 61 | } 62 | 63 | run(); 64 | -------------------------------------------------------------------------------- /example/multiTenancyExampleOnchainPrivacyEventLogs/subscribeWebSockets.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Web3 = require("web3"); 4 | const Web3Quorum = require("../../src"); 5 | 6 | const { network, enclave } = require("../keys"); 7 | const { createHttpProvider } = require("../helpers.js"); 8 | 9 | // use an enclave key that is not a member of the group (eg enclave.node11.jwt) 10 | // to demonstrate that they can't create a subscription 11 | const node = new Web3Quorum( 12 | new Web3(createHttpProvider(enclave.node11.jwt, network.node1.url)) 13 | ); 14 | const params = JSON.parse(fs.readFileSync(path.join(__dirname, "params.json"))); 15 | 16 | async function run() { 17 | const { privacyGroupId, contractAddress: address, blockNumber } = params; 18 | 19 | const filter = { 20 | address, 21 | fromBlock: blockNumber, 22 | }; 23 | 24 | console.log("Installing filter", filter); 25 | 26 | // Create subscription 27 | return node.priv 28 | .subscribeWithPooling(privacyGroupId, filter, (error, result) => { 29 | if (!error) { 30 | console.log("Installed filter", result); 31 | } else { 32 | console.error("Problem installing filter:", error); 33 | throw error; 34 | } 35 | }) 36 | .then(async (subscription) => { 37 | // Add handlers for incoming events 38 | subscription 39 | .on("data", (log) => { 40 | if (log.result != null) { 41 | // Logs from subscription are nested in `result` key 42 | console.log("LOG =>", log.result); 43 | } else { 44 | console.log("LOG =>", log); 45 | } 46 | }) 47 | .on("error", console.error); 48 | 49 | // Unsubscribe and disconnect on interrupt 50 | process.on("SIGINT", async () => { 51 | console.log("unsubscribing"); 52 | await subscription.unsubscribe((error, success) => { 53 | if (!error) { 54 | console.log("Unsubscribed:", success); 55 | } else { 56 | console.error("Failed to unsubscribe:", error); 57 | } 58 | 59 | node.currentProvider.disconnect(); 60 | }); 61 | }); 62 | 63 | return subscription; 64 | }) 65 | .catch((error) => { 66 | console.error(error); 67 | }); 68 | } 69 | 70 | run(); 71 | -------------------------------------------------------------------------------- /example/multiTenancyExamplePrivateCall/deployContract.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | const Web3 = require("web3"); 4 | const Web3Quorum = require("../../src"); 5 | 6 | const { enclave, network } = require("../keys.js"); 7 | const { createHttpProvider } = require("../helpers.js"); 8 | 9 | const binary = fs.readFileSync( 10 | path.join(__dirname, "../solidity/EventEmitter/EventEmitter.bin") 11 | ); 12 | 13 | const node1 = new Web3Quorum( 14 | new Web3(createHttpProvider(enclave.node1.jwt, network.node1.url)) 15 | ); 16 | 17 | const createEventEmitterContract = (privacyGroupId) => { 18 | const contractOptions = { 19 | data: `0x${binary}`, 20 | privateFrom: enclave.node1.publicKey, 21 | privacyGroupId, 22 | privateKey: network.node1.privateKey, 23 | }; 24 | return node1.priv.generateAndSendRawTransaction(contractOptions); 25 | }; 26 | 27 | const getPrivateContractAddress = (transactionHash) => { 28 | return node1.priv 29 | .waitForTransactionReceipt(transactionHash) 30 | .then((privateTransactionReceipt) => { 31 | return privateTransactionReceipt.contractAddress; 32 | }); 33 | }; 34 | 35 | module.exports = async () => { 36 | const privacyGroupCreationResult = await node1.eth.flexiblePrivacyGroup.create( 37 | { 38 | participants: [enclave.node1.publicKey, enclave.node2.publicKey], 39 | enclaveKey: enclave.node1.publicKey, 40 | privateFrom: enclave.node1.publicKey, 41 | privateKey: network.node1.privateKey, 42 | } 43 | ); 44 | 45 | console.log("Created privacy group"); 46 | console.log(privacyGroupCreationResult); 47 | 48 | const contractAddress = await createEventEmitterContract( 49 | privacyGroupCreationResult.privacyGroupId 50 | ).then((res) => { 51 | return getPrivateContractAddress(res); 52 | }); 53 | console.log( 54 | `now you have to run:\n export CONTRACT_ADDRESS=${contractAddress}` 55 | ); 56 | console.log( 57 | ` export PRIVACY_GROUP_ID=${privacyGroupCreationResult.privacyGroupId}` 58 | ); 59 | }; 60 | 61 | if (require.main === module) { 62 | module.exports(); 63 | } 64 | -------------------------------------------------------------------------------- /example/onChainPrivacy/addRemoveExample.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | 4 | const Utils = require("../helpers.js"); 5 | const { enclave, network } = require("../keys.js"); 6 | 7 | const node1 = new Web3Quorum(new Web3(network.node1.url)); 8 | const node2 = new Web3Quorum(new Web3(network.node2.url)); 9 | const node3 = new Web3Quorum(new Web3(network.node3.url)); 10 | 11 | module.exports = async () => { 12 | const onChainPrivacyGroupCreationResult = await node1.eth.flexiblePrivacyGroup.create( 13 | { 14 | participants: [enclave.node1.publicKey, enclave.node2.publicKey], 15 | enclaveKey: enclave.node1.publicKey, 16 | privateFrom: enclave.node1.publicKey, 17 | privateKey: network.node1.privateKey, 18 | } 19 | ); 20 | console.log("Created new on-chain privacy group:"); 21 | console.log(onChainPrivacyGroupCreationResult); 22 | 23 | const findResult = await node2.eth.flexiblePrivacyGroup.find([ 24 | enclave.node1.publicKey, 25 | enclave.node2.publicKey, 26 | ]); 27 | console.log("Found privacy group results:"); 28 | Utils.logMatchingGroup( 29 | findResult, 30 | onChainPrivacyGroupCreationResult.privacyGroupId 31 | ); 32 | 33 | const addResult = await node1.eth.flexiblePrivacyGroup.addTo({ 34 | participants: [enclave.node3.publicKey], 35 | enclaveKey: enclave.node1.publicKey, 36 | privateFrom: enclave.node1.publicKey, 37 | privacyGroupId: onChainPrivacyGroupCreationResult.privacyGroupId, 38 | privateKey: network.node1.privateKey, 39 | }); 40 | console.log("Added new node to privacy group:"); 41 | console.log(addResult); 42 | 43 | const receiptFromNode3 = await node3.priv.waitForTransactionReceipt( 44 | addResult.commitmentHash 45 | ); 46 | console.log("Got transaction receipt from added node:"); 47 | console.log(receiptFromNode3); 48 | 49 | const findResultWithAddedNode = await node2.eth.flexiblePrivacyGroup.find([ 50 | enclave.node1.publicKey, 51 | enclave.node2.publicKey, 52 | enclave.node3.publicKey, 53 | ]); 54 | console.log("Found privacy groups with added node:"); 55 | Utils.logMatchingGroup( 56 | findResultWithAddedNode, 57 | onChainPrivacyGroupCreationResult.privacyGroupId 58 | ); 59 | 60 | const removeResult = await node1.eth.flexiblePrivacyGroup.removeFrom({ 61 | participant: enclave.node3.publicKey, 62 | enclaveKey: enclave.node1.publicKey, 63 | privateFrom: enclave.node1.publicKey, 64 | privacyGroupId: onChainPrivacyGroupCreationResult.privacyGroupId, 65 | privateKey: network.node1.privateKey, 66 | }); 67 | console.log("Removed third participant from privacy group:"); 68 | console.log(removeResult); 69 | 70 | const findResultRemovedNode = await node2.eth.flexiblePrivacyGroup.find([ 71 | enclave.node1.publicKey, 72 | enclave.node2.publicKey, 73 | ]); 74 | Utils.logMatchingGroup( 75 | findResultRemovedNode, 76 | onChainPrivacyGroupCreationResult.privacyGroupId 77 | ); 78 | }; 79 | 80 | if (require.main === module) { 81 | module.exports(); 82 | } 83 | -------------------------------------------------------------------------------- /example/onChainPrivacy/simpleExample.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | 4 | const Utils = require("../helpers.js"); 5 | const { enclave, network } = require("../keys.js"); 6 | 7 | const node1 = new Web3Quorum(new Web3(network.node1.url)); 8 | const node2 = new Web3Quorum(new Web3(network.node2.url)); 9 | 10 | module.exports = async () => { 11 | const onChainPrivacyGroupCreationResult = await node1.eth.flexiblePrivacyGroup.create( 12 | { 13 | participants: [enclave.node1.publicKey, enclave.node2.publicKey], 14 | enclaveKey: enclave.node1.publicKey, 15 | privateFrom: enclave.node1.publicKey, 16 | privateKey: network.node1.privateKey, 17 | } 18 | ); 19 | console.log("CREATION RESULT"); 20 | console.log(onChainPrivacyGroupCreationResult); 21 | 22 | await node2.priv.waitForTransactionReceipt( 23 | onChainPrivacyGroupCreationResult.commitmentHash 24 | ); 25 | 26 | const findResult = await node2.eth.flexiblePrivacyGroup.find([ 27 | enclave.node1.publicKey, 28 | enclave.node2.publicKey, 29 | ]); 30 | Utils.logMatchingGroup( 31 | findResult, 32 | onChainPrivacyGroupCreationResult.privacyGroupId 33 | ); 34 | }; 35 | 36 | if (require.main === module) { 37 | module.exports().catch((error) => { 38 | console.log(error); 39 | console.log( 40 | "\nThis example requires ONCHAIN privacy to be ENABLED. \nCheck config for ONCHAIN privacy groups." 41 | ); 42 | }); 43 | } 44 | -------------------------------------------------------------------------------- /example/privacyGroupManagement/createPrivacyGroup.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | const { enclave, network } = require("../keys.js"); 4 | 5 | const web3 = new Web3Quorum(new Web3(network.node1.url)); 6 | 7 | const createPrivacyGroup = () => { 8 | const contractOptions = { 9 | addresses: [enclave.node1.publicKey, enclave.node2.publicKey], 10 | name: "web3js-quorum", 11 | description: "test", 12 | }; 13 | return web3.priv.createPrivacyGroup(contractOptions).then((result) => { 14 | console.log(`The privacy group created is:`, result); 15 | return result; 16 | }); 17 | }; 18 | 19 | const createPrivacyGroupForNode123 = () => { 20 | const contractOptions = { 21 | addresses: [ 22 | enclave.node1.publicKey, 23 | enclave.node2.publicKey, 24 | enclave.node3.publicKey, 25 | ], 26 | name: "web3js-quorum", 27 | description: "test", 28 | }; 29 | return web3.priv.createPrivacyGroup(contractOptions).then((result) => { 30 | console.log(`The privacy group created is:`, result); 31 | return result; 32 | }); 33 | }; 34 | 35 | module.exports = { 36 | createPrivacyGroup, 37 | createPrivacyGroupForNode123, 38 | }; 39 | 40 | if (require.main === module) { 41 | createPrivacyGroup(); 42 | } 43 | -------------------------------------------------------------------------------- /example/privacyGroupManagement/createPrivacyGroupNode2.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | const { enclave, network } = require("../keys.js"); 4 | 5 | const web3 = new Web3Quorum(new Web3(network.node2.url)); 6 | 7 | const createPrivacyGroupForNode23 = () => { 8 | const contractOptions = { 9 | addresses: [enclave.node2.publicKey, enclave.node3.publicKey], 10 | name: "web3js-quorum", 11 | description: "test", 12 | }; 13 | return web3.priv.createPrivacyGroup(contractOptions).then((result) => { 14 | console.log(`The privacy group created is:`, result); 15 | return result; 16 | }); 17 | }; 18 | 19 | module.exports = { 20 | createPrivacyGroupForNode23, 21 | }; 22 | 23 | if (require.main === module) { 24 | createPrivacyGroupForNode23(); 25 | } 26 | -------------------------------------------------------------------------------- /example/privacyGroupManagement/deletePrivacyGroup.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | const { network } = require("../keys.js"); 4 | 5 | const web3 = new Web3Quorum(new Web3(network.node1.url)); 6 | 7 | const deletePrivacyGroup = (givenPrivacyGroupId) => { 8 | return web3.priv.deletePrivacyGroup(givenPrivacyGroupId).then((result) => { 9 | console.log(`The privacy group deleted is:`, result); 10 | return result; 11 | }); 12 | }; 13 | 14 | module.exports = { 15 | deletePrivacyGroup, 16 | }; 17 | 18 | if (require.main === module) { 19 | if (!process.env.PRIVACY_GROUP_TO_DELETE) { 20 | throw Error( 21 | "You need to export the following variable in your shell environment: PRIVACY_GROUP_TO_DELETE=" 22 | ); 23 | } 24 | 25 | const privacyGroupId = process.env.PRIVACY_GROUP_TO_DELETE; 26 | deletePrivacyGroup(privacyGroupId).catch((error) => { 27 | console.log(error); 28 | console.log( 29 | `\nAttempted to delete PRIVACY_GROUP_TO_DELETE=${privacyGroupId}` 30 | ); 31 | console.log("You may need to update PRIVACY_GROUP_TO_DELETE"); 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /example/privacyGroupManagement/findPrivacyGroup.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | const { enclave, network } = require("../keys.js"); 4 | 5 | const web3 = new Web3Quorum(new Web3(network.node1.url)); 6 | 7 | const findPrivacyGroup = () => { 8 | return web3.priv 9 | .findPrivacyGroup([enclave.node1.publicKey, enclave.node2.publicKey]) 10 | .then((result) => { 11 | console.log(`The privacy groups found are:`, result); 12 | return result; 13 | }); 14 | }; 15 | 16 | const findPrivacyGroupForNode123 = () => { 17 | return web3.priv 18 | .findPrivacyGroup([ 19 | enclave.node1.publicKey, 20 | enclave.node2.publicKey, 21 | enclave.node3.publicKey, 22 | ]) 23 | .then((result) => { 24 | console.log(`The privacy groups found are:`, result); 25 | return result; 26 | }); 27 | }; 28 | 29 | const findPrivacyGroupForNode23 = () => { 30 | return web3.priv 31 | .findPrivacyGroup([enclave.node2.publicKey, enclave.node3.publicKey]) 32 | .then((result) => { 33 | console.log(`The privacy groups found are:`, result); 34 | return result; 35 | }); 36 | }; 37 | 38 | module.exports = { 39 | findPrivacyGroup, 40 | findPrivacyGroupForNode123, 41 | findPrivacyGroupForNode23, 42 | }; 43 | 44 | if (require.main === module) { 45 | findPrivacyGroup(); 46 | } 47 | -------------------------------------------------------------------------------- /example/privacyGroupManagement/findPrivacyGroupNode2.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../src"); 3 | const { enclave, network } = require("../keys.js"); 4 | 5 | const web3 = new Web3Quorum(new Web3(network.node2.url)); 6 | 7 | const findPrivacyGroupForNode23 = () => { 8 | return web3.priv 9 | .findPrivacyGroup([enclave.node2.publicKey, enclave.node3.publicKey]) 10 | .then((result) => { 11 | console.log(`The privacy groups found are:`, result); 12 | return result; 13 | }); 14 | }; 15 | 16 | module.exports = { 17 | findPrivacyGroupForNode23, 18 | }; 19 | 20 | if (require.main === module) { 21 | findPrivacyGroupForNode23(); 22 | } 23 | -------------------------------------------------------------------------------- /example/solidity/CrossContractReader/CrossContractReader.bin: -------------------------------------------------------------------------------- 1 | 608060405234801561001057600080fd5b506104b7806100206000396000f3fe608060405234801561001057600080fd5b506004361061007d5760003560e01c806383197ef01161005b57806383197ef0146100d8578063a087a87e146100e0578063d09de08a14610118578063e689ef8a146101205761007d565b8063305155f9146100825780635374ded2146100aa578063775c300c146100d0575b600080fd5b6100a86004803603602081101561009857600080fd5b50356001600160a01b0316610146565b005b6100a8600480360360208110156100c057600080fd5b50356001600160a01b03166101a2565b6100a86101e2565b6100a8610250565b610106600480360360208110156100f657600080fd5b50356001600160a01b0316610253565b60408051918252519081900360200190f35b6100a86102c5565b6100a86004803603602081101561013657600080fd5b50356001600160a01b03166102d0565b6000819050806001600160a01b03166383197ef06040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561018657600080fd5b505af115801561019a573d6000803e3d6000fd5b505050505050565b6000819050806001600160a01b031663775c300c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561018657600080fd5b60006040516101f090610310565b604051809103906000f08015801561020c573d6000803e3d6000fd5b50604080516001600160a01b038316815290519192507f9ac6876e0aa40667ffeaa9b359b5ed924f4cdd0e029eb6e9c369e78c68f711fb919081900360200190a150565b33ff5b600080829050806001600160a01b0316633fa4f2456040518163ffffffff1660e01b815260040160206040518083038186803b15801561029257600080fd5b505afa1580156102a6573d6000803e3d6000fd5b505050506040513d60208110156102bc57600080fd5b50519392505050565b600080546001019055565b6000819050806001600160a01b031663d09de08a6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561018657600080fd5b6101658061031e8339019056fe608060405234801561001057600080fd5b50600080546001600160a01b03191633179055610133806100326000396000f3fe6080604052348015600f57600080fd5b5060043610603c5760003560e01c80633fa4f2451460415780636057361d14605957806367e404ce146075575b600080fd5b60476097565b60408051918252519081900360200190f35b607360048036036020811015606d57600080fd5b5035609d565b005b607b60ef565b604080516001600160a01b039092168252519081900360200190f35b60025490565b604080513381526020810183905281517fc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f5929181900390910190a1600255600180546001600160a01b03191633179055565b6001546001600160a01b03169056fea265627a7a72305820dc1ce4d08260105d146ec5efa5274950ee9e66f81ff18994d44a40fbd33e45c064736f6c634300050a0032a265627a7a72305820d71e5a225a48fdeb043aaba4264138353b3443a28658bacec7570e108659ad2864736f6c634300050a0032 -------------------------------------------------------------------------------- /example/solidity/CrossContractReader/CrossContractReader.json: -------------------------------------------------------------------------------- 1 | {"compiler":{"version":"0.5.12+commit.7709ece9"},"language":"Solidity","output":{"abi":[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"contractAddress","type":"address"}],"name":"NewEventEmitter","type":"event"},{"constant":false,"inputs":[],"name":"deploy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"crossAddress","type":"address"}],"name":"deployRemote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"destroy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"increment","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"crossAddress","type":"address"}],"name":"incrementRemote","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"internalType":"address","name":"emitter_address","type":"address"}],"name":"read","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"internalType":"address","name":"crossAddress","type":"address"}],"name":"remoteDestroy","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}],"devdoc":{"methods":{}},"userdoc":{"methods":{}}},"settings":{"compilationTarget":{"CrossContractReader.sol":"CrossContractReader"},"evmVersion":"petersburg","libraries":{},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"CrossContractReader.sol":{"keccak256":"0x431bc626d50b65058aba147886a4ac1e7a10fc2916f32df1193f817e599b1513","urls":["bzz-raw://1847d5d74a48a287400faa16fcd3bb43875c872b4e508dd1d54e666721f6a32d","dweb:/ipfs/QmURmdg1u6akULaur9L8tb9rXa2R1zBVJnKDXvSCVG8TTz"]},"EventEmitter.sol":{"keccak256":"0xde4a63a09d85a410ae209d701010ba2c5997c5c7b0c0d2d5aeb2a995139ad4d6","urls":["bzz-raw://d9401c9f07a960c57448b25cf8d4a77eb93ec7bf71f1a508ebf2dd250f1143bd","dweb:/ipfs/QmPYZZHRwMFu27QGcD8g2JFqbrPAoGvFNytgzq5NtTJUyb"]}},"version":1} -------------------------------------------------------------------------------- /example/solidity/CrossContractReader/CrossContractReader.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright ConsenSys Software Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | * 13 | * SPDX-License-Identifier: Apache-2.0 14 | */ 15 | pragma solidity >=0.4.0 <0.6.0; 16 | 17 | import "./EventEmitter.sol"; 18 | 19 | // compile with: 20 | // solc CrossContractReader.sol --bin --abi --optimize --overwrite -o . 21 | // then create web3j wrappers with: 22 | // web3j solidity generate -b ./generated/CrossContractReader.bin -a ./generated/CrossContractReader.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated 23 | contract CrossContractReader { 24 | uint counter; 25 | 26 | event NewEventEmitter( 27 | address contractAddress 28 | ); 29 | 30 | function read(address emitter_address) view public returns (uint) { 31 | EventEmitter em = EventEmitter(emitter_address); 32 | return em.value(); 33 | } 34 | 35 | function deploy() public { 36 | EventEmitter em = new EventEmitter(); 37 | emit NewEventEmitter(address(em)); 38 | } 39 | 40 | function deployRemote(address crossAddress) public { 41 | CrossContractReader cross = CrossContractReader(crossAddress); 42 | cross.deploy(); 43 | } 44 | 45 | function increment() public { 46 | counter++; 47 | } 48 | 49 | function incrementRemote(address crossAddress) public { 50 | CrossContractReader cross = CrossContractReader(crossAddress); 51 | cross.increment(); 52 | } 53 | 54 | function destroy() public { 55 | selfdestruct(msg.sender); 56 | } 57 | 58 | function remoteDestroy(address crossAddress) public { 59 | CrossContractReader cross = CrossContractReader(crossAddress); 60 | cross.destroy(); 61 | } 62 | } -------------------------------------------------------------------------------- /example/solidity/EventEmitter/EventEmitter.bin: -------------------------------------------------------------------------------- 1 | 608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610221806100606000396000f300608060405260043610610057576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680633fa4f2451461005c5780636057361d1461008757806367e404ce146100b4575b600080fd5b34801561006857600080fd5b5061007161010b565b6040518082815260200191505060405180910390f35b34801561009357600080fd5b506100b260048036038101908080359060200190929190505050610115565b005b3480156100c057600080fd5b506100c96101cb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6000600254905090565b7fc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f53382604051808373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020018281526020019250505060405180910390a18060028190555033600160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000600160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050905600a165627a7a723058208efaf938851fb2d235f8bf9a9685f149129a30fe0f4b20a6c1885dc02f639eba0029 -------------------------------------------------------------------------------- /example/solidity/EventEmitter/EventEmitter.json: -------------------------------------------------------------------------------- 1 | {"compiler":{"version":"0.4.25+commit.59dbf8f1"},"language":"Solidity","output":{"abi":[{"constant":true,"inputs":[],"name":"value","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_amount","type":"uint256"}],"name":"store","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"sender","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_to","type":"address"},{"indexed":false,"name":"_amount","type":"uint256"}],"name":"stored","type":"event"}],"devdoc":{"methods":{}},"userdoc":{"methods":{}}},"settings":{"compilationTarget":{"EventEmitter.sol":"EventEmitter"},"evmVersion":"byzantium","libraries":{},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"EventEmitter.sol":{"keccak256":"0xde4a63a09d85a410ae209d701010ba2c5997c5c7b0c0d2d5aeb2a995139ad4d6","urls":["bzzr://935f251f62e5c54f010eae818cfdb12442830715ad0681187340c0f88239b50e"]}},"version":1} -------------------------------------------------------------------------------- /example/solidity/EventEmitter/EventEmitter.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright ConsenSys Software Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | * 13 | * SPDX-License-Identifier: Apache-2.0 14 | */ 15 | pragma solidity >=0.4.0 <0.6.0; 16 | 17 | // compile with: 18 | // solc EventEmitter.sol --bin --abi --optimize --overwrite -o . 19 | // then create web3j wrappers with: 20 | // web3j solidity generate -b ./generated/EventEmitter.bin -a ./generated/EventEmitter.abi -o ../../../../../ -p org.hyperledger.besu.tests.web3j.generated 21 | contract EventEmitter { 22 | address owner; 23 | event stored(address _to, uint _amount); 24 | address _sender; 25 | uint _value; 26 | 27 | constructor() public { 28 | owner = msg.sender; 29 | } 30 | 31 | function store(uint _amount) public { 32 | emit stored(msg.sender, _amount); 33 | _value = _amount; 34 | _sender = msg.sender; 35 | } 36 | 37 | function value() view public returns (uint) { 38 | return _value; 39 | } 40 | 41 | function sender() view public returns (address) { 42 | return _sender; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /example/solidity/Greeter/Greeter.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.2; 2 | 3 | contract mortal { 4 | /* Define variable owner of the type address*/ 5 | address owner; 6 | 7 | /* this function is executed at initialization 8 | and sets the owner of the contract */ 9 | function mortal() { owner = msg.sender; } 10 | 11 | /* Function to recover the funds on the contract */ 12 | function kill() { if (msg.sender == owner) suicide(owner); } 13 | } 14 | 15 | contract greeter is mortal { 16 | /* define variable greeting of the type string */ 17 | string greeting = "test123"; 18 | event Approval(string _g); 19 | 20 | function setGreeting(string _greeting) public { 21 | greeting = _greeting; 22 | } 23 | 24 | /* main function */ 25 | function greet() public constant returns (string) { 26 | return greeting; 27 | } 28 | 29 | function fire() public { 30 | emit Approval(greeting); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /example/solidity/Greeter/greeter.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"fire","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_greeting","type":"string"}],"name":"setGreeting","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_g","type":"string"}],"name":"Approval","type":"event"}] -------------------------------------------------------------------------------- /example/solidity/Greeter/greeter.bin: -------------------------------------------------------------------------------- 1 | 60806040526040805190810160405280600781526020017f74657374313233000000000000000000000000000000000000000000000000008152506001908051906020019061004f929190610095565b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061013a565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106100d657805160ff1916838001178555610104565b82800160010185558215610104579182015b828111156101035782518255916020019190600101906100e8565b5b5090506101119190610115565b5090565b61013791905b8082111561013357600081600090555060010161011b565b5090565b90565b610468806101496000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806341c0e1b514610067578063457094cc1461007e578063a413686214610095578063cfae3217146100fe575b600080fd5b34801561007357600080fd5b5061007c61018e565b005b34801561008a57600080fd5b5061009361021f565b005b3480156100a157600080fd5b506100fc600480360381019080803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091929192905050506102db565b005b34801561010a57600080fd5b506101136102f5565b6040518080602001828103825283818151815260200191508051906020019080838360005b83811015610153578082015181840152602081019050610138565b50505050905090810190601f1680156101805780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561021d576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b565b7f2a6a5a1825051fcbfa57a90aa1b69d86a5bc5318d93d5144d8ec18c52a664b60600160405180806020018281038252838181546001816001161561010002031660029004815260200191508054600181600116156101000203166002900480156102cb5780601f106102a0576101008083540402835291602001916102cb565b820191906000526020600020905b8154815290600101906020018083116102ae57829003601f168201915b50509250505060405180910390a1565b80600190805190602001906102f1929190610397565b5050565b606060018054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561038d5780601f106103625761010080835404028352916020019161038d565b820191906000526020600020905b81548152906001019060200180831161037057829003601f168201915b5050505050905090565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106103d857805160ff1916838001178555610406565b82800160010185558215610406579182015b828111156104055782518255916020019190600101906103ea565b5b5090506104139190610417565b5090565b61043991905b8082111561043557600081600090555060010161041d565b5090565b905600a165627a7a72305820f983ca284af6609e82d3ffc0060410694130fd071810f8889e82ff80a59c63500029 -------------------------------------------------------------------------------- /example/solidity/Greeter/greeter_meta.json: -------------------------------------------------------------------------------- 1 | {"compiler":{"version":"0.4.25+commit.59dbf8f1"},"language":"Solidity","output":{"abi":[{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"fire","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_greeting","type":"string"}],"name":"setGreeting","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"greet","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_g","type":"string"}],"name":"Approval","type":"event"}],"devdoc":{"methods":{}},"userdoc":{"methods":{}}},"settings":{"compilationTarget":{"Greeter.sol":"greeter"},"evmVersion":"byzantium","libraries":{},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"Greeter.sol":{"keccak256":"0xbf74e9b3d36f48b5f1d5a51b59e05661d5e4c203d8b18102a26a04d318c2cbf4","urls":["bzzr://7dad18d983aa02dc40c2bdc89c289b7cb7b2ac1866063c1388a86ffdd15f4233"]}},"version":1} -------------------------------------------------------------------------------- /example/solidity/Greeter/mortal.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] -------------------------------------------------------------------------------- /example/solidity/Greeter/mortal.bin: -------------------------------------------------------------------------------- 1 | 608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550610114806100606000396000f300608060405260043610603f576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806341c0e1b5146044575b600080fd5b348015604f57600080fd5b5060566058565b005b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141560e6576000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b5600a165627a7a72305820626b7f3f5078f0bbc25243a8940f70b01b7765708c160b186b4950a1f8e86a840029 -------------------------------------------------------------------------------- /example/solidity/Greeter/mortal_meta.json: -------------------------------------------------------------------------------- 1 | {"compiler":{"version":"0.4.25+commit.59dbf8f1"},"language":"Solidity","output":{"abi":[{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}],"devdoc":{"methods":{}},"userdoc":{"methods":{}}},"settings":{"compilationTarget":{"Greeter.sol":"mortal"},"evmVersion":"byzantium","libraries":{},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"Greeter.sol":{"keccak256":"0xbf74e9b3d36f48b5f1d5a51b59e05661d5e4c203d8b18102a26a04d318c2cbf4","urls":["bzzr://7dad18d983aa02dc40c2bdc89c289b7cb7b2ac1866063c1388a86ffdd15f4233"]}},"version":1} -------------------------------------------------------------------------------- /integration-tests/errorFromNode1ForGroup23.test.js: -------------------------------------------------------------------------------- 1 | const createGroup = require("../example/privacyGroupManagement/createPrivacyGroup"); 2 | const createGroupNode2 = require("../example/privacyGroupManagement/createPrivacyGroupNode2"); 3 | const findGroup = require("../example/privacyGroupManagement/findPrivacyGroup"); 4 | const findGroupNode2 = require("../example/privacyGroupManagement/findPrivacyGroupNode2"); 5 | 6 | describe("[MultiNodeExample]: Can manage privacy groups", () => { 7 | it("can create and find privacy group", async () => { 8 | const [privacyGroup12, privacyGroup23] = await Promise.all([ 9 | createGroup.createPrivacyGroup(), 10 | createGroupNode2.createPrivacyGroupForNode23(), 11 | ]); 12 | const listPrivacyGroups = await findGroup.findPrivacyGroup(); 13 | const listWithPrivacyGroupAfterCreate = listPrivacyGroups.filter((i) => { 14 | return i.privacyGroupId === privacyGroup12; 15 | }); 16 | expect(listWithPrivacyGroupAfterCreate).toHaveLength(1); 17 | 18 | const listFindFromNode1 = await findGroup.findPrivacyGroupForNode23(); 19 | 20 | // node1 should not see privacyGroup23 21 | expect(listFindFromNode1).toHaveLength(0); 22 | 23 | const listFromNode2 = await findGroupNode2.findPrivacyGroupForNode23(); 24 | const listWithPrivacyGroupNode2AfterCreate23 = listFromNode2.filter((i) => { 25 | return i.privacyGroupId === privacyGroup23; 26 | }); 27 | 28 | expect(listWithPrivacyGroupNode2AfterCreate23).toHaveLength(1); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /integration-tests/getPrivateTransaction.test.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../src"); 3 | 4 | const { contracts } = require("./support/helpers"); 5 | const { network, enclave } = require("./support/keys"); 6 | 7 | describe("getPrivateTransaction", () => { 8 | const node2Client = new Web3Quorum(new Web3(network.node2.url)); 9 | const node1Client = new Web3Quorum(new Web3(network.node1.url)); 10 | const node3Client = new Web3Quorum(new Web3(network.node3.url)); 11 | 12 | let privacyGroupId; 13 | let publicHash; 14 | beforeAll(async () => { 15 | // create a privacy group with nodes 1 and 2 16 | privacyGroupId = await node1Client.priv.createPrivacyGroup({ 17 | addresses: [enclave.node1.publicKey, enclave.node2.publicKey], 18 | }); 19 | 20 | // deploy a contract and get the receipt 21 | const receipt = await node1Client.priv 22 | .generateAndSendRawTransaction({ 23 | data: `0x${contracts.eventEmitter.bytecode}`, 24 | privateFrom: enclave.node1.publicKey, 25 | privacyGroupId, 26 | privateKey: network.node1.privateKey, 27 | }) 28 | .then((hash) => { 29 | return node1Client.priv.waitForTransactionReceipt(hash); 30 | }); 31 | publicHash = receipt.commitmentHash; 32 | }); 33 | 34 | // group membership 35 | it("should get tx from originating node", async () => { 36 | const result = await node1Client.priv.getPrivateTransaction(publicHash); 37 | 38 | expect(result.privateFrom).toEqual(enclave.node1.publicKey); 39 | expect(result.privacyGroupId).toEqual(privacyGroupId); 40 | }); 41 | 42 | it("should get tx from other member node", async () => { 43 | const result = await node2Client.priv.getPrivateTransaction(publicHash); 44 | 45 | expect(result.privateFrom).toEqual(enclave.node1.publicKey); 46 | expect(result.privacyGroupId).toEqual(privacyGroupId); 47 | }); 48 | 49 | it("should get error from non-member node", async () => { 50 | const result = await node3Client.priv.getPrivateTransaction(publicHash); 51 | expect(result).toBeNull(); 52 | }); 53 | 54 | // inputs 55 | it("should fail if the transaction hash is invalid", async () => { 56 | await expect( 57 | node1Client.priv.getPrivateTransaction(undefined) 58 | ).rejects.toThrowError("Invalid params"); 59 | }); 60 | 61 | it("should return null if the txHash does not exist", async () => { 62 | const invalidHash = 63 | "0x0000000000000000000000000000000000000000000000000000000000000000"; 64 | const result = await node3Client.priv.getPrivateTransaction(invalidHash); 65 | expect(result).toBeNull(); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /integration-tests/privacyGroupManagement.test.js: -------------------------------------------------------------------------------- 1 | const createGroup = require("../example/privacyGroupManagement/createPrivacyGroup"); 2 | const findGroup = require("../example/privacyGroupManagement/findPrivacyGroup"); 3 | const deleteGroup = require("../example/privacyGroupManagement/deletePrivacyGroup"); 4 | 5 | describe("[MultiNodeExample]: Can manage privacy groups", () => { 6 | it("can create and find privacy group", async () => { 7 | const createdGroupId = await createGroup.createPrivacyGroup(); 8 | const returnedPrivacyGroup = await findGroup.findPrivacyGroup(); 9 | const listWithPrivacyGroup = returnedPrivacyGroup.filter((i) => { 10 | return i.privacyGroupId === createdGroupId; 11 | }); 12 | 13 | expect(createdGroupId).toEqual(listWithPrivacyGroup[0].privacyGroupId); 14 | }); 15 | 16 | it("can create, find and delete privacy group", async () => { 17 | const createdGroupId = await createGroup.createPrivacyGroup(); 18 | 19 | let returnedPrivacyGroup = await findGroup.findPrivacyGroup(); 20 | 21 | const listWithPrivacyGroup = returnedPrivacyGroup.filter((i) => { 22 | return i.privacyGroupId === createdGroupId; 23 | }); 24 | 25 | expect(createdGroupId).toEqual(listWithPrivacyGroup[0].privacyGroupId); 26 | 27 | const deletedGroup = await deleteGroup.deletePrivacyGroup(createdGroupId); 28 | 29 | expect(deletedGroup).toEqual(createdGroupId); 30 | 31 | returnedPrivacyGroup = await findGroup.findPrivacyGroup(); 32 | 33 | const listWithPrivacyGroupAfterDelete = returnedPrivacyGroup.filter((i) => { 34 | return i.privacyGroupId === deletedGroup; 35 | }); 36 | 37 | expect(listWithPrivacyGroupAfterDelete).toHaveLength(0); 38 | }); 39 | 40 | it("create twice and delete once", async () => { 41 | const newPrivacyGroup1 = await createGroup.createPrivacyGroup(); 42 | const newPrivacyGroup2 = await createGroup.createPrivacyGroup(); 43 | 44 | let privacyGroupList = await findGroup.findPrivacyGroup(); 45 | 46 | let newListWithPrivacyGroup1 = privacyGroupList.filter((i) => { 47 | return i.privacyGroupId === newPrivacyGroup1; 48 | }); 49 | 50 | expect(newListWithPrivacyGroup1).toHaveLength(1); 51 | 52 | let newListWithPrivacyGroup2 = privacyGroupList.filter((i) => { 53 | return i.privacyGroupId === newPrivacyGroup2; 54 | }); 55 | 56 | expect(newListWithPrivacyGroup2).toHaveLength(1); 57 | 58 | const deletedGroup = await deleteGroup.deletePrivacyGroup(newPrivacyGroup1); 59 | 60 | expect(deletedGroup).toEqual(newPrivacyGroup1); 61 | 62 | privacyGroupList = await findGroup.findPrivacyGroup(); 63 | 64 | newListWithPrivacyGroup1 = privacyGroupList.filter((i) => { 65 | return i.privacyGroupId === newPrivacyGroup1; 66 | }); 67 | 68 | expect(newListWithPrivacyGroup1).toHaveLength(0); 69 | 70 | newListWithPrivacyGroup2 = privacyGroupList.filter((i) => { 71 | return i.privacyGroupId === newPrivacyGroup2; 72 | }); 73 | 74 | expect(newListWithPrivacyGroup2).toHaveLength(1); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /integration-tests/quickstartExample.test.js: -------------------------------------------------------------------------------- 1 | const deployContract = require("../example/multiNodeExample/deployContract"); 2 | const node1Example = require("../example/multiNodeExample/storeValueFromNode1"); 3 | const node2Example = require("../example/multiNodeExample/storeValueFromNode2"); 4 | 5 | describe("[MultiNodeExample]: Can run quickstart", () => { 6 | let contractAddress; 7 | beforeAll(async () => { 8 | contractAddress = await deployContract(); 9 | }); 10 | 11 | it("store and gets from node 1", async () => { 12 | const result = await node1Example.storeValueFromNode1( 13 | contractAddress, 14 | 1000 15 | ); 16 | expect(result.logs[0].data).toEqual( 17 | "0x000000000000000000000000fe3b557e8fb62b89f4916b721be55ceb828dbd7300000000000000000000000000000000000000000000000000000000000003e8" 18 | ); 19 | 20 | const getNode1 = await node1Example.getValueFromNode1(contractAddress); 21 | expect(getNode1.output).toEqual( 22 | "0x00000000000000000000000000000000000000000000000000000000000003e8" 23 | ); 24 | 25 | const getNode2 = await node1Example.getValueFromNode2(contractAddress); 26 | expect(getNode2.output).toEqual( 27 | "0x00000000000000000000000000000000000000000000000000000000000003e8" 28 | ); 29 | 30 | const getNode3 = await node1Example.getValueFromNode3(contractAddress); 31 | expect(getNode3.output).toEqual("0x"); 32 | }); 33 | 34 | it("store and gets from node 2", async () => { 35 | const result = await node2Example.storeValueFromNode2(contractAddress, 42); 36 | expect(result.logs[0].data).toEqual( 37 | "0x000000000000000000000000627306090abab3a6e1400e9345bc60c78a8bef57000000000000000000000000000000000000000000000000000000000000002a" 38 | ); 39 | 40 | const getNode1 = await node2Example.getValueFromNode1(contractAddress); 41 | expect(getNode1.output).toEqual( 42 | "0x000000000000000000000000000000000000000000000000000000000000002a" 43 | ); 44 | 45 | const getNode2 = await node2Example.getValueFromNode2(contractAddress); 46 | expect(getNode2.output).toEqual( 47 | "0x000000000000000000000000000000000000000000000000000000000000002a" 48 | ); 49 | 50 | const getNode3 = await node2Example.getValueFromNode3(contractAddress); 51 | expect(getNode3.output).toEqual("0x"); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /integration-tests/quickstartPrivacyGroupIdExample.test.js: -------------------------------------------------------------------------------- 1 | const deployContract = require("../example/multiNodeExamplePrivacyGroup/deployContract"); 2 | const node1Example = require("../example/multiNodeExamplePrivacyGroup/storeValueFromNode1"); 3 | const node2Example = require("../example/multiNodeExamplePrivacyGroup/storeValueFromNode2"); 4 | 5 | describe("[MultiNodeExample]: Can run quickstart with privacyGroupId instead of privateFor", () => { 6 | let contractAddress; 7 | let privacyGroupId; 8 | beforeAll(async () => { 9 | const response = await deployContract(); 10 | ({ contractAddress, privacyGroupId } = response); 11 | }); 12 | 13 | it("store and gets from node 1", async () => { 14 | const result = await node1Example.storeValueFromNode1( 15 | contractAddress, 16 | 1000, 17 | privacyGroupId 18 | ); 19 | 20 | expect(result.logs[0].data).toEqual( 21 | "0x000000000000000000000000fe3b557e8fb62b89f4916b721be55ceb828dbd7300000000000000000000000000000000000000000000000000000000000003e8" 22 | ); 23 | 24 | const getNode1 = await node1Example.getValueFromNode1( 25 | contractAddress, 26 | privacyGroupId 27 | ); 28 | 29 | expect(getNode1.output).toEqual( 30 | "0x00000000000000000000000000000000000000000000000000000000000003e8" 31 | ); 32 | 33 | const getNode2 = await node1Example.getValueFromNode2( 34 | contractAddress, 35 | privacyGroupId 36 | ); 37 | 38 | expect(getNode2.output).toEqual( 39 | "0x00000000000000000000000000000000000000000000000000000000000003e8" 40 | ); 41 | }); 42 | 43 | it("store and gets from node 2", async () => { 44 | const result = await node2Example.storeValueFromNode2( 45 | contractAddress, 46 | 42, 47 | privacyGroupId 48 | ); 49 | 50 | expect(result.logs[0].data).toEqual( 51 | "0x000000000000000000000000627306090abab3a6e1400e9345bc60c78a8bef57000000000000000000000000000000000000000000000000000000000000002a" 52 | ); 53 | 54 | const getNode1 = await node2Example.getValueFromNode1( 55 | contractAddress, 56 | privacyGroupId 57 | ); 58 | 59 | expect(getNode1.output).toEqual( 60 | "0x000000000000000000000000000000000000000000000000000000000000002a" 61 | ); 62 | 63 | const getNode2 = await node2Example.getValueFromNode2( 64 | contractAddress, 65 | privacyGroupId 66 | ); 67 | 68 | expect(getNode2.output).toEqual( 69 | "0x000000000000000000000000000000000000000000000000000000000000002a" 70 | ); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /integration-tests/quickstartPrivateCallExample.test.js: -------------------------------------------------------------------------------- 1 | const deployContract = require("../example/multiNodeExamplePrivateCall/deployContract"); 2 | const node1Example = require("../example/multiNodeExamplePrivateCall/storeValueFromNode1"); 3 | const node2Example = require("../example/multiNodeExamplePrivateCall/storeValueFromNode2"); 4 | 5 | describe("[MultiNodeExample]: Can run quickstart with privacyGroupId and private Call", () => { 6 | let contractAddress; 7 | let privacyGroupId; 8 | // deploy contract 9 | beforeEach(async () => { 10 | const response = await deployContract(); 11 | ({ contractAddress, privacyGroupId } = response); 12 | }); 13 | 14 | it("store and gets from node 1", async () => { 15 | const result = await node1Example.storeValueFromNode1( 16 | contractAddress, 17 | 1000, 18 | privacyGroupId 19 | ); 20 | 21 | expect(result.logs[0].data).toEqual( 22 | "0x000000000000000000000000fe3b557e8fb62b89f4916b721be55ceb828dbd7300000000000000000000000000000000000000000000000000000000000003e8" 23 | ); 24 | 25 | const getNode1 = await node1Example.getValueFromNode1( 26 | contractAddress, 27 | privacyGroupId 28 | ); 29 | 30 | expect(getNode1).toEqual( 31 | "0x00000000000000000000000000000000000000000000000000000000000003e8" 32 | ); 33 | 34 | const getNode2 = await node1Example.getValueFromNode2( 35 | contractAddress, 36 | privacyGroupId 37 | ); 38 | 39 | expect(getNode2).toEqual( 40 | "0x00000000000000000000000000000000000000000000000000000000000003e8" 41 | ); 42 | }); 43 | 44 | it("store and gets from node 2", async () => { 45 | const result = await node2Example.storeValueFromNode2( 46 | contractAddress, 47 | 42, 48 | privacyGroupId 49 | ); 50 | 51 | expect(result.logs[0].data).toEqual( 52 | "0x000000000000000000000000627306090abab3a6e1400e9345bc60c78a8bef57000000000000000000000000000000000000000000000000000000000000002a" 53 | ); 54 | 55 | const getNode1 = await node2Example.getValueFromNode1( 56 | contractAddress, 57 | privacyGroupId 58 | ); 59 | 60 | expect(getNode1).toEqual( 61 | "0x000000000000000000000000000000000000000000000000000000000000002a" 62 | ); 63 | 64 | const getNode2 = await node2Example.getValueFromNode2( 65 | contractAddress, 66 | privacyGroupId 67 | ); 68 | 69 | expect(getNode2).toEqual( 70 | "0x000000000000000000000000000000000000000000000000000000000000002a" 71 | ); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /integration-tests/quorum-privacy/connection.test.js: -------------------------------------------------------------------------------- 1 | const httpConfig = require("./helpers/httpConfig"); 2 | const ipcConfig = require("./helpers/ipcConfig"); 3 | 4 | [ 5 | { 6 | name: "Http", 7 | config: httpConfig, 8 | }, 9 | { 10 | name: "Ipc", 11 | config: ipcConfig, 12 | }, 13 | ].forEach((testCase) => { 14 | const { web3 } = testCase.config; 15 | 16 | describe(`${testCase.name}`, () => { 17 | it("can connect to upcheck", async () => { 18 | const data = await web3.ptm.upCheck(); 19 | 20 | expect(data).toEqual("I'm up!"); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /integration-tests/quorum-privacy/greeter.test.js: -------------------------------------------------------------------------------- 1 | const { decryptedAccount, fromAddress } = require("./helpers/quorumConfig"); 2 | 3 | const httpConfig = require("./helpers/httpConfig"); 4 | const ipcConfig = require("./helpers/ipcConfig"); 5 | 6 | const contract = require("../../solidity/greeter.json").contracts[ 7 | "Greeter.sol:Greeter" 8 | ]; 9 | 10 | const abi = JSON.parse(contract.abi); 11 | const code = `0x${contract.bin}`; 12 | 13 | const options = { 14 | data: code, 15 | }; 16 | 17 | [ 18 | { 19 | name: "Http", 20 | config: httpConfig, 21 | }, 22 | { 23 | name: "Ipc", 24 | config: ipcConfig, 25 | }, 26 | ].forEach((testCase) => { 27 | const { web3, toPublicKey, fromPublicKey } = testCase.config; 28 | 29 | const tokenContract = new web3.eth.Contract(abi, null, options); 30 | 31 | describe(`${testCase.name}`, () => { 32 | describe("Greeter Contract", () => { 33 | const contractPayload = tokenContract 34 | .deploy({ 35 | data: code, 36 | arguments: ["Hello Tessera!"], 37 | }) 38 | .encodeABI(); 39 | 40 | it("can be deployed and executed", async () => { 41 | const nonce = await web3.eth.getTransactionCount(fromAddress); 42 | const result = await web3.priv.generateAndSendRawTransaction({ 43 | gasPrice: 0, 44 | gasLimit: 4300000, 45 | to: "", 46 | value: 0, 47 | data: contractPayload, 48 | from: decryptedAccount, 49 | isPrivate: true, 50 | privateFrom: fromPublicKey, 51 | privateFor: [toPublicKey], 52 | nonce, 53 | }); 54 | expect(result).not.toEqual("0x"); 55 | expect(result.status).toBeTruthy(); 56 | expect(result.contractAddress).toEqual(expect.any(String)); 57 | 58 | const token = new web3.eth.Contract( 59 | abi, 60 | result.contractAddress, 61 | options 62 | ); 63 | const greetingResult = await token.methods.greet().call({ 64 | from: fromAddress, 65 | }); 66 | expect(greetingResult).toEqual("Hello Tessera!"); 67 | }); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /integration-tests/quorum-privacy/helpers/httpConfig.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../../src"); 3 | const { address } = require("./quorumConfig"); 4 | 5 | const fromPublicKey = "BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo="; 6 | const toPublicKey = "QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc="; 7 | 8 | const web3 = new Web3Quorum( 9 | new Web3(address), 10 | { 11 | ipcPath: null, 12 | privateUrl: "http://localhost:9081", 13 | }, 14 | true 15 | ); 16 | 17 | module.exports = { 18 | web3, 19 | fromPublicKey, 20 | toPublicKey, 21 | }; 22 | -------------------------------------------------------------------------------- /integration-tests/quorum-privacy/helpers/ipcConfig.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../../../src"); 3 | const { address } = require("./quorumConfig"); 4 | 5 | const fromPublicKey = "BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo="; 6 | const toPublicKey = "QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc="; 7 | 8 | const ipcPath = process.env.IPC_PATH; 9 | const web3 = new Web3Quorum( 10 | new Web3(address), 11 | { 12 | ipcPath, 13 | privateUrl: "http://localhost:9081", 14 | }, 15 | true 16 | ); 17 | 18 | module.exports = { 19 | web3, 20 | fromPublicKey, 21 | toPublicKey, 22 | }; 23 | -------------------------------------------------------------------------------- /integration-tests/quorum-privacy/helpers/quorumConfig.js: -------------------------------------------------------------------------------- 1 | const decryptedAccount = { 2 | privateKey: 3 | "0xe6181caaffff94a09d7e332fc8da9884d99902c7874eb74354bdcadf411929f1", 4 | }; 5 | const fromAddress = "0xed9d02e382b34818e88b88a309c7fe71e65f419d"; 6 | const toAddress = "0xca843569e3427144cead5e4d5999a3d0ccf92b8e"; 7 | 8 | module.exports = { 9 | address: "http://localhost:22000", 10 | decryptedAccount, 11 | fromAddress, 12 | toAddress, 13 | }; 14 | -------------------------------------------------------------------------------- /integration-tests/quorum-privacy/transfer.test.js: -------------------------------------------------------------------------------- 1 | const { 2 | decryptedAccount, 3 | fromAddress, 4 | toAddress, 5 | } = require("./helpers/quorumConfig"); 6 | 7 | const httpConfig = require("./helpers/httpConfig"); 8 | const ipcConfig = require("./helpers/ipcConfig"); 9 | 10 | const contract = require("../../solidity/HumanStandardToken/HumanStandardToken.json") 11 | .contracts["HumanStandardToken.sol:HumanStandardToken"]; 12 | 13 | const abi = JSON.parse(contract.interface); 14 | const code = `0x${contract.bytecode}`; 15 | 16 | const options = { 17 | data: code, 18 | }; 19 | 20 | [ 21 | { 22 | name: "Http", 23 | config: httpConfig, 24 | }, 25 | { 26 | name: "Ipc", 27 | config: ipcConfig, 28 | }, 29 | ].forEach((testCase) => { 30 | describe(`${testCase.name}`, () => { 31 | const { web3, toPublicKey, fromPublicKey } = testCase.config; 32 | 33 | describe("Human Standard Contract", () => { 34 | const transferQty = 160; 35 | const totalSupplyQty = "100000"; 36 | const tokenContract = new web3.eth.Contract(abi, null, options); 37 | 38 | const contractPayload = tokenContract 39 | .deploy({ 40 | data: code, 41 | arguments: [totalSupplyQty, "web3js token", 18, "web3js"], 42 | }) 43 | .encodeABI(); 44 | 45 | it("can transfer funds with private payload", async () => { 46 | let nonce = await web3.eth.getTransactionCount(fromAddress); 47 | const result = await web3.priv.generateAndSendRawTransaction({ 48 | gasPrice: 0, 49 | gasLimit: 4300000, 50 | to: "", 51 | value: 0, 52 | data: contractPayload, 53 | from: decryptedAccount, 54 | isPrivate: true, 55 | privateFrom: fromPublicKey, 56 | privateFor: [toPublicKey], 57 | nonce, 58 | }); 59 | expect(result).not.toEqual("0x"); 60 | expect(result.status).toBeTruthy(); 61 | expect(result.contractAddress).toEqual(expect.any(String)); 62 | 63 | const token = new web3.eth.Contract( 64 | abi, 65 | result.contractAddress, 66 | options 67 | ); 68 | const totalSupply = await token.methods 69 | .totalSupply() 70 | .call({ from: fromAddress }); 71 | expect(totalSupply).toEqual(totalSupplyQty); 72 | const supply = await token.methods 73 | .balanceOf(fromAddress) 74 | .call({ from: fromAddress }); 75 | expect(supply).toEqual(totalSupplyQty); 76 | 77 | const transferAbi = token.methods 78 | .transfer(toAddress, transferQty) 79 | .encodeABI(); 80 | 81 | nonce = await web3.eth.getTransactionCount(fromAddress); 82 | await web3.priv.generateAndSendRawTransaction({ 83 | gasPrice: 0, 84 | gasLimit: 4300000, 85 | to: token.options.address, 86 | value: 0, 87 | data: transferAbi, 88 | from: decryptedAccount, 89 | isPrivate: true, 90 | privateFrom: fromPublicKey, 91 | privateFor: [toPublicKey], 92 | nonce, 93 | }); 94 | const aliceBalance = await token.methods 95 | .balanceOf(fromAddress) 96 | .call({ from: fromAddress }); 97 | const remainingQty = totalSupplyQty - transferQty; 98 | expect(aliceBalance).toEqual(String(remainingQty)); 99 | 100 | const bobBalance = await token.methods 101 | .balanceOf(toAddress) 102 | .call({ from: toAddress }); 103 | expect(bobBalance).toEqual(String(transferQty)); 104 | }); 105 | }); 106 | }); 107 | }); 108 | -------------------------------------------------------------------------------- /integration-tests/support/after.js: -------------------------------------------------------------------------------- 1 | const { spawn } = require("child_process"); 2 | 3 | const stopPrivacyDocker = () => { 4 | return new Promise((resolve) => { 5 | const run = spawn("cd docker && ./stop.sh && ./remove.sh", { 6 | shell: true, 7 | stdio: "inherit", 8 | }); 9 | 10 | run.on("close", () => { 11 | return resolve({}); 12 | }); 13 | }); 14 | }; 15 | 16 | stopPrivacyDocker().catch(console.error); 17 | -------------------------------------------------------------------------------- /integration-tests/support/before.js: -------------------------------------------------------------------------------- 1 | const { spawn } = require("child_process"); 2 | const axios = require("axios"); 3 | 4 | axios.interceptors.response.use(undefined, function axiosRetryInterceptor(err) { 5 | const { config } = err; 6 | if (!config || !config.retry) return Promise.reject(err); 7 | config.__retryCount = config.__retryCount || 0; 8 | 9 | if (config.__retryCount >= config.retry) { 10 | return Promise.reject(err); 11 | } 12 | config.__retryCount += 1; 13 | 14 | /* eslint-disable promise/avoid-new */ 15 | const backoff = new Promise((resolve) => { 16 | setTimeout(() => { 17 | resolve(); 18 | }, config.retryDelay || 1); 19 | }); 20 | 21 | return backoff.then(() => { 22 | return axios(config); 23 | }); 24 | }); 25 | 26 | const runPrivacyDocker = () => { 27 | return new Promise((resolve) => { 28 | const run = spawn("cd docker && ./run.sh", { 29 | shell: true, 30 | stdio: "inherit", 31 | }); 32 | 33 | run.on("close", () => { 34 | return resolve({}); 35 | }); 36 | }); 37 | }; 38 | 39 | const waitForBesu = () => { 40 | return axios.get("http://localhost:20000", { 41 | retry: 60 * 5, 42 | retryDelay: 1000, 43 | }); 44 | }; 45 | 46 | runPrivacyDocker() 47 | .then(waitForBesu) 48 | .then(() => { 49 | return console.log("Finished: Besu Network is Running"); 50 | }) 51 | .catch(console.error); 52 | -------------------------------------------------------------------------------- /integration-tests/support/contractFactory.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Interacts with a private contract 3 | * @param {Client} client 4 | * @param {Contract} contract 5 | * @param {String} address 6 | * @param {object} options 7 | */ 8 | function PrivateContract(client, contract, address, options) { 9 | this.client = client; 10 | this.contract = contract; 11 | this.address = address; 12 | 13 | // extract options 14 | this.privacyOptions = options.privacyOptions; 15 | this.privateKey = options.privateKey; 16 | this.enclaveKey = options.enclaveKey; 17 | this.deployReceipt = options.deployReceipt; 18 | 19 | return this; 20 | } 21 | 22 | /** 23 | * Send a private transaction 24 | */ 25 | PrivateContract.prototype.send = async function send(method, params) { 26 | const data = this.contract.methods[method](params).encodeABI(); 27 | 28 | return this.client.priv 29 | .generateAndSendRawTransaction({ 30 | to: this.address, 31 | data, 32 | privateFrom: this.privacyOptions.enclaveKey, 33 | privacyGroupId: this.privacyOptions.privacyGroupId, 34 | privateKey: this.privateKey, 35 | }) 36 | .then((transactionHash) => { 37 | return this.client.priv.waitForTransactionReceipt(transactionHash); 38 | }); 39 | }; 40 | 41 | /** 42 | * Creates private contracts 43 | * @param {*} bytecode 44 | * @param {*} jsonInterface 45 | */ 46 | function ContractFactory(bytecode, jsonInterface) { 47 | this.bytecode = bytecode; 48 | this.jsonInterface = jsonInterface; 49 | this.deployedTx = undefined; 50 | this.client = undefined; 51 | this.privacyOptions = {}; 52 | this.contract = undefined; 53 | 54 | return this; 55 | } 56 | 57 | /** 58 | * Connect to a client and set credentials for transactions 59 | */ 60 | ContractFactory.prototype.connect = async function connect( 61 | client, 62 | privacyOptions, 63 | privateKey 64 | ) { 65 | this.client = client; 66 | this.privacyOptions = privacyOptions; 67 | this.privateKey = privateKey; 68 | this.contract = new this.client.eth.Contract(this.jsonInterface); 69 | }; 70 | 71 | ContractFactory.prototype._checkConnection = function _checkConnection() { 72 | if (this.client == null || this.privateKey == null) { 73 | throw new Error("Must connect to a client first"); 74 | } 75 | }; 76 | 77 | /** 78 | * Deploy a private contract to a privacy group 79 | */ 80 | ContractFactory.prototype.privateDeploy = async function privateDeploy( 81 | privacyGroupId 82 | ) { 83 | this._checkConnection(); 84 | 85 | const receipt = await this.client.priv 86 | .generateAndSendRawTransaction({ 87 | data: `0x${this.bytecode}`, 88 | privateFrom: this.privacyOptions.enclaveKey, 89 | privacyGroupId, 90 | privateKey: this.privateKey, 91 | }) 92 | .then((hash) => { 93 | return this.client.priv.waitForTransactionReceipt(hash); 94 | }); 95 | this.deployedTx = receipt; 96 | 97 | // Create a new contract with the current options 98 | const contractOptions = { 99 | privacyOptions: this.privacyOptions, 100 | privateKey: this.privateKey, 101 | deployReceipt: receipt, 102 | }; 103 | 104 | return new PrivateContract( 105 | this.client, 106 | new this.client.eth.Contract(this.jsonInterface), 107 | receipt.contractAddress, 108 | contractOptions 109 | ); 110 | }; 111 | 112 | module.exports = { ContractFactory }; 113 | -------------------------------------------------------------------------------- /integration-tests/support/helpers.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const path = require("path"); 3 | 4 | const { ContractFactory } = require("./contractFactory"); 5 | 6 | const artifactDir = path.join(__dirname, "../../example/solidity"); 7 | const eventEmitterBytecode = fs.readFileSync( 8 | path.join(artifactDir, "EventEmitter/EventEmitter.bin") 9 | ); 10 | 11 | const eventEmitterArtifacts = JSON.parse( 12 | fs.readFileSync(path.join(artifactDir, "EventEmitter/EventEmitter.json")) 13 | ); 14 | const eventEmitterAbi = eventEmitterArtifacts.output.abi; 15 | 16 | const privacyArtifactDir = path.join(__dirname, "../../solidity"); 17 | const privacyInterfaceAbi = JSON.parse( 18 | fs.readFileSync(path.join(privacyArtifactDir, "PrivacyInterface.abi")) 19 | ); 20 | const parseError = (error) => { 21 | const prefix = "Returned error: "; 22 | const start = error.message.indexOf(prefix) + prefix.length; 23 | const msg = error.message.substr(start); 24 | return { message: msg }; 25 | }; 26 | 27 | module.exports = { 28 | parseError, 29 | contracts: { 30 | eventEmitter: { abi: eventEmitterAbi, bytecode: eventEmitterBytecode }, 31 | privacyInterface: { 32 | abi: privacyInterfaceAbi, 33 | address: "0x000000000000000000000000000000000000007c", 34 | }, 35 | }, 36 | ContractFactory, 37 | }; 38 | -------------------------------------------------------------------------------- /integration-tests/support/keys.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | enclave: { 3 | node1: { 4 | publicKey: "BULeR8JyUWhiuuCMU/HLA0Q5pzkYT+cHII3ZKBey3Bo=", 5 | }, 6 | node2: { 7 | publicKey: "QfeDAys9MPDs2XHExtc84jKGHxZg/aj52DTh0vtA3Xc=", 8 | }, 9 | node3: { 10 | publicKey: "1iTZde/ndBHvzhcl7V68x44Vx7pl8nwx9LqnM/AfJUg=", 11 | }, 12 | }, 13 | network: { 14 | node1: { 15 | url: "http://localhost:20000", 16 | privateKey: 17 | "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 18 | }, 19 | node2: { 20 | url: "http://localhost:20002", 21 | privateKey: 22 | "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", 23 | }, 24 | node3: { 25 | url: "http://localhost:20004", 26 | privateKey: 27 | "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f", 28 | }, 29 | }, 30 | }; 31 | -------------------------------------------------------------------------------- /jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": { 3 | "allowUnknownTags": true, 4 | "dictionaries": ["jsdoc"] 5 | }, 6 | "source": { 7 | "include": ["src", "package.json", "README.md"], 8 | "includePattern": ".js$", 9 | "excludePattern": "(node_modules/|docs)" 10 | }, 11 | "plugins": [ 12 | "plugins/markdown" 13 | ], 14 | "opts": { 15 | "destination": "./docs/out", 16 | "encoding": "utf8", 17 | "private": true, 18 | "recurse": true, 19 | "template": "node_modules/docdash", 20 | "tutorials": "docs/migrations" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /solidity/Greeter.sol: -------------------------------------------------------------------------------- 1 | contract Greeter { 2 | /* Define variable greeting of the type string */ 3 | string greeting; 4 | 5 | /* This runs when the contract is executed */ 6 | function Greeter(string _greeting) public { 7 | greeting = _greeting; 8 | } 9 | 10 | /* Main function */ 11 | function greet() constant returns (string) { 12 | return greeting; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /solidity/HumanStandardToken/HumanStandardToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.2; 2 | 3 | /* 4 | This Token Contract implements the standard token functionality (https://github.com/ethereum/EIPs/issues/20) as well as the following OPTIONAL extras intended for use by humans. 5 | 6 | In other words. This is intended for deployment in something like a Token Factory or Mist wallet, and then used by humans. 7 | Imagine coins, currencies, shares, voting weight, etc. 8 | Machine-based, rapid creation of many tokens would not necessarily need these extra features or will be minted in other manners. 9 | 10 | 1) Initial Finite Supply (upon creation one specifies how much is minted). 11 | 2) In the absence of a token registry: Optional Decimal, Symbol & Name. 12 | 3) Optional approveAndCall() functionality to notify a contract if an approval() has occurred. 13 | 14 | .*/ 15 | 16 | import "StandardToken.sol"; 17 | 18 | contract HumanStandardToken is StandardToken { 19 | 20 | function () public { 21 | //if ether is sent to this address, send it back. 22 | revert(); 23 | } 24 | 25 | /* Public variables of the token */ 26 | 27 | /* 28 | NOTE: 29 | The following variables are OPTIONAL vanities. One does not have to include them. 30 | They allow one to customise the token contract & in no way influences the core functionality. 31 | Some wallets/interfaces might not even bother to look at this information. 32 | */ 33 | string public name; //fancy name: eg Simon Bucks 34 | uint8 public decimals; //How many decimals to show. ie. There could 1000 base units with 3 decimals. Meaning 0.980 SBX = 980 base units. It's like comparing 1 wei to 1 ether. 35 | string public symbol; //An identifier: eg SBX 36 | string public version = 'H0.1'; //human 0.1 standard. Just an arbitrary versioning scheme. 37 | 38 | constructor( 39 | uint256 _initialAmount, 40 | string _tokenName, 41 | uint8 _decimalUnits, 42 | string _tokenSymbol 43 | ) public { 44 | balances[msg.sender] = _initialAmount; // Give the creator all initial tokens 45 | totalSupply = _initialAmount; // Update total supply 46 | name = _tokenName; // Set the name for display purposes 47 | decimals = _decimalUnits; // Amount of decimals for display purposes 48 | symbol = _tokenSymbol; // Set the symbol for display purposes 49 | } 50 | 51 | /* Approves and then calls the receiving contract */ 52 | function approveAndCall(address _spender, uint256 _value, bytes _extraData) public returns (bool success) { 53 | allowed[msg.sender][_spender] = _value; 54 | emit Approval(msg.sender, _spender, _value); 55 | 56 | //call the receiveApproval function on the contract you want to be notified. This crafts the function signature manually so one doesn't have to include a contract in here just for this. 57 | //receiveApproval(address _from, uint256 _value, address _tokenContract, bytes _extraData) 58 | //it is assumed that when does this that the call *should* succeed, otherwise one would use vanilla approve instead. 59 | if(!_spender.call(bytes4(bytes32(keccak256("receiveApproval(address,uint256,address,bytes)"))), msg.sender, _value, this, _extraData)) { revert(); } 60 | return true; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /solidity/HumanStandardToken/StandardToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.2; 2 | 3 | /* 4 | You should inherit from StandardToken or, for a token like you would want to 5 | deploy in something like Mist, see HumanStandardToken.sol. 6 | (This implements ONLY the standard functions and NOTHING else. 7 | If you deploy this, you won't have anything useful.) 8 | 9 | Implements ERC 20 Token standard: https://github.com/ethereum/EIPs/issues/20. 10 | */ 11 | 12 | import "Token.sol"; 13 | 14 | contract StandardToken is Token { 15 | 16 | function transfer(address _to, uint256 _value) public returns (bool success) { 17 | //Default assumes totalSupply can't be over max (2^256 - 1). 18 | //If your token leaves out totalSupply and can issue more tokens as time goes on, you need to check if it doesn't wrap. 19 | //Replace the if with this one instead. 20 | //if (balances[msg.sender] >= _value && balances[_to] + _value > balances[_to]) { 21 | if (balances[msg.sender] >= _value && _value > 0) { 22 | balances[msg.sender] -= _value; 23 | balances[_to] += _value; 24 | emit Transfer(msg.sender, _to, _value); 25 | return true; 26 | } else { return false; } 27 | } 28 | 29 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { 30 | //same as above. Replace this line with the following if you want to protect against wrapping uints. 31 | //if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && balances[_to] + _value > balances[_to]) { 32 | if (balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0) { 33 | balances[_to] += _value; 34 | balances[_from] -= _value; 35 | allowed[_from][msg.sender] -= _value; 36 | emit Transfer(_from, _to, _value); 37 | return true; 38 | } else { return false; } 39 | } 40 | 41 | function balanceOf(address _owner) public view returns (uint256 balance) { 42 | return balances[_owner]; 43 | } 44 | 45 | function approve(address _spender, uint256 _value) public returns (bool success) { 46 | allowed[msg.sender][_spender] = _value; 47 | emit Approval(msg.sender, _spender, _value); 48 | return true; 49 | } 50 | 51 | function allowance(address _owner, address _spender) public view returns (uint256 remaining) { 52 | return allowed[_owner][_spender]; 53 | } 54 | 55 | mapping (address => uint256) balances; 56 | mapping (address => mapping (address => uint256)) allowed; 57 | } 58 | -------------------------------------------------------------------------------- /solidity/HumanStandardToken/Token.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.2; 2 | 3 | // Abstract contract for the full ERC 20 Token standard 4 | // https://github.com/ethereum/EIPs/issues/20 5 | 6 | contract Token { 7 | /* This is a slight change to the ERC20 base standard. 8 | function totalSupply() constant returns (uint256 supply); 9 | is replaced with: 10 | uint256 public totalSupply; 11 | This automatically creates a getter function for the totalSupply. 12 | This is moved to the base contract since public getter functions are not 13 | currently recognised as an implementation of the matching abstract 14 | function by the compiler. 15 | */ 16 | /// total amount of tokens 17 | uint256 public totalSupply; 18 | 19 | /// @param _owner The address from which the balance will be retrieved 20 | /// @return The balance 21 | function balanceOf(address _owner) public constant returns (uint256 balance); 22 | 23 | /// @notice send `_value` token to `_to` from `msg.sender` 24 | /// @param _to The address of the recipient 25 | /// @param _value The amount of token to be transferred 26 | /// @return Whether the transfer was successful or not 27 | function transfer(address _to, uint256 _value) public returns (bool success); 28 | 29 | /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` 30 | /// @param _from The address of the sender 31 | /// @param _to The address of the recipient 32 | /// @param _value The amount of token to be transferred 33 | /// @return Whether the transfer was successful or not 34 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); 35 | 36 | /// @notice `msg.sender` approves `_addr` to spend `_value` tokens 37 | /// @param _spender The address of the account able to transfer the tokens 38 | /// @param _value The amount of wei to be approved for transfer 39 | /// @return Whether the approval was successful or not 40 | function approve(address _spender, uint256 _value) public returns (bool success); 41 | 42 | /// @param _owner The address of the account owning tokens 43 | /// @param _spender The address of the account able to transfer the tokens 44 | /// @return Amount of remaining tokens allowed to spent 45 | function allowance(address _owner, address _spender) public view returns (uint256 remaining); 46 | 47 | event Transfer(address indexed _from, address indexed _to, uint256 _value); 48 | event Approval(address indexed _owner, address indexed _spender, uint256 _value); 49 | } 50 | -------------------------------------------------------------------------------- /solidity/PrivacyInterface.abi: -------------------------------------------------------------------------------- 1 | [{"inputs":[{"internalType":"bytes[]","name":"publicEnclaveKeys","type":"bytes[]"}],"name":"addParticipants","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"canExecute","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canUpgrade","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getParticipants","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVersion","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"participant","type":"bytes"}],"name":"removeParticipant","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"}] 2 | -------------------------------------------------------------------------------- /solidity/PrivacyInterface.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/web3js-quorum/8af3f0d09af5e6de8bf5045d61cf8a8287a66b4e/solidity/PrivacyInterface.bin -------------------------------------------------------------------------------- /solidity/PrivacyInterface.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Consensys/web3js-quorum/8af3f0d09af5e6de8bf5045d61cf8a8287a66b4e/solidity/PrivacyInterface.json -------------------------------------------------------------------------------- /solidity/PrivacyInterface.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright ConsenSys Software Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | * 13 | * SPDX-License-Identifier: Apache-2.0 14 | */ 15 | pragma solidity >=0.7.0 <0.9.0; 16 | pragma experimental ABIEncoderV2; 17 | 18 | interface PrivacyInterface { 19 | 20 | function addParticipants(bytes[] calldata publicEnclaveKeys) external returns (bool); 21 | 22 | function removeParticipant(bytes calldata participant) external returns (bool); 23 | 24 | function getParticipants() external view returns (bytes[] memory); 25 | 26 | function lock() external; 27 | 28 | function unlock() external; 29 | 30 | function canExecute() external view returns (bool); 31 | 32 | function getVersion() external view returns (bytes32); 33 | 34 | function canUpgrade() external returns (bool); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /solidity/PrivacyProxy.abi: -------------------------------------------------------------------------------- 1 | [{"inputs":[{"internalType":"address","name":"_implementation","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"publicEnclaveKey","type":"bytes"}],"name":"ParticipantRemoved","type":"event"},{"inputs":[{"internalType":"bytes[]","name":"_publicEnclaveKeys","type":"bytes[]"}],"name":"addParticipants","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"canExecute","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canUpgrade","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getParticipants","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVersion","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_participant","type":"bytes"}],"name":"removeParticipant","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"}] 2 | -------------------------------------------------------------------------------- /solidity/PrivacyProxy.json: -------------------------------------------------------------------------------- 1 | {"compiler":{"version":"0.7.0+commit.9e61f92b"},"language":"Solidity","output":{"abi":[{"inputs":[{"internalType":"address","name":"_implementation","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes","name":"publicEnclaveKey","type":"bytes"}],"name":"ParticipantRemoved","type":"event"},{"inputs":[{"internalType":"bytes[]","name":"_publicEnclaveKeys","type":"bytes[]"}],"name":"addParticipants","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"canExecute","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"canUpgrade","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getParticipants","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVersion","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_participant","type":"bytes"}],"name":"removeParticipant","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"stateMutability":"nonpayable","type":"function"}],"devdoc":{"kind":"dev","methods":{},"version":1},"userdoc":{"kind":"user","methods":{},"version":1}},"settings":{"compilationTarget":{"browser/PrivacyProxy.sol":"PrivacyProxy"},"evmVersion":"istanbul","libraries":{},"metadata":{"bytecodeHash":"ipfs"},"optimizer":{"enabled":false,"runs":200},"remappings":[]},"sources":{"browser/PrivacyInterface.sol":{"keccak256":"0x5ca5bb7032b3b0d7bd88b2489c9ef73476e501fbfd968085e5d7149db5610893","urls":["bzz-raw://b0ba9bf46178158d4e96dd98e2aae94fb403e85ec974c10b87dd113978a4165d","dweb:/ipfs/QmU1bHvDoynLNLyf2D3BEBaYCvbsmbbmfg98QfkQbE5GhL"]},"browser/PrivacyProxy.sol":{"keccak256":"0xc6c9ddc1edaf2b3206c6de5bdedd1bb6a8470ea2f488c7a20a30cf333b8829e8","urls":["bzz-raw://d3d434e4329c5ce18386a7687286aa8c176e8c2d7905be3addea8840960ec410","dweb:/ipfs/QmawcHRMbXxStfS7H5Jk78Z6Cdp144xxYSRHzb54cKnVok"]}},"version":1} 2 | -------------------------------------------------------------------------------- /solidity/PrivacyProxy.sol: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright ConsenSys Software Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | * 13 | * SPDX-License-Identifier: Apache-2.0 14 | */ 15 | pragma solidity >=0.7.0 <0.9.0; 16 | pragma experimental ABIEncoderV2; 17 | import "./PrivacyInterface.sol"; 18 | 19 | contract PrivacyProxy is PrivacyInterface { 20 | 21 | address public implementation; 22 | 23 | constructor(address _implementation) public { 24 | implementation = _implementation; 25 | } 26 | 27 | function _setImplementation(address _newImp) internal { 28 | implementation = _newImp; 29 | } 30 | 31 | function addParticipants(bytes[] calldata _publicEnclaveKeys) public override returns (bool) { 32 | PrivacyInterface privacyInterface = PrivacyInterface(implementation); 33 | return privacyInterface.addParticipants(_publicEnclaveKeys); 34 | } 35 | 36 | function getParticipants() view public override returns (bytes[] memory) { 37 | PrivacyInterface privacyInterface = PrivacyInterface(implementation); 38 | return privacyInterface.getParticipants(); 39 | } 40 | 41 | function removeParticipant(bytes calldata _participant) public override returns (bool) { 42 | PrivacyInterface privacyInterface = PrivacyInterface(implementation); 43 | bool result = privacyInterface.removeParticipant(_participant); 44 | if (result) { 45 | emit ParticipantRemoved(_participant); 46 | } 47 | return result; 48 | } 49 | 50 | function lock() public override { 51 | PrivacyInterface privacyInterface = PrivacyInterface(implementation); 52 | return privacyInterface.lock(); 53 | } 54 | 55 | function unlock() public override { 56 | PrivacyInterface privacyInterface = PrivacyInterface(implementation); 57 | return privacyInterface.unlock(); 58 | } 59 | 60 | function canExecute() public view override returns (bool) { 61 | PrivacyInterface privacyInterface = PrivacyInterface(implementation); 62 | return privacyInterface.canExecute(); 63 | } 64 | 65 | function getVersion() public view override returns (bytes32) { 66 | PrivacyInterface privacyInterface = PrivacyInterface(implementation); 67 | return privacyInterface.getVersion(); 68 | } 69 | 70 | function canUpgrade() external override returns (bool) { 71 | PrivacyInterface privacyInterface = PrivacyInterface(implementation); 72 | return privacyInterface.canUpgrade(); 73 | } 74 | 75 | function upgradeTo(address _newImplementation) external { 76 | require(this.canExecute(), "The contract is locked."); 77 | require(implementation != _newImplementation, "The contract to upgrade to has to be different from the current management contract."); 78 | require(this.canUpgrade(), "Not allowed to upgrade the management contract."); 79 | bytes[] memory participants = this.getParticipants(); 80 | _setImplementation(_newImplementation); 81 | PrivacyInterface privacyInterface = PrivacyInterface(implementation); 82 | privacyInterface.addParticipants(participants); 83 | } 84 | 85 | event ParticipantRemoved(bytes publicEnclaveKey); 86 | 87 | } 88 | -------------------------------------------------------------------------------- /solidity/key: -------------------------------------------------------------------------------- 1 | {"address":"ed9d02e382b34818e88b88a309c7fe71e65f419d","crypto":{"cipher":"aes-128-ctr","ciphertext":"4e77046ba3f699e744acb4a89c36a3ea1158a1bd90a076d36675f4c883864377","cipherparams":{"iv":"a8932af2a3c0225ee8e872bc0e462c11"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"8ca49552b3e92f79c51f2cd3d38dfc723412c212e702bd337a3724e8937aff0f"},"mac":"6d1354fef5aa0418389b1a5d1f5ee0050d7273292a1171c51fd02f9ecff55264"},"id":"a65d1ac3-db7e-445d-a1cc-b6c5eeaa05e0","version":3} 2 | -------------------------------------------------------------------------------- /src/common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Common code. 3 | * @module common 4 | */ 5 | 6 | // Perform the specified operation which requires waiting for a transaction to be minted. 7 | // Returns a promise which waits for the operation to complete. 8 | const waitForTransactionWithRetries = (operation, txHash, retries, delay) => { 9 | /* eslint-disable promise/param-names */ 10 | /* eslint-disable promise/avoid-new */ 11 | 12 | const waitFor = (ms) => { 13 | return new Promise((r) => { 14 | return setTimeout(r, ms); 15 | }); 16 | }; 17 | 18 | let notified = false; 19 | const retryOperation = (operationToRetry, times) => { 20 | return new Promise((resolve, reject) => { 21 | return operationToRetry() 22 | .then((result) => { 23 | if (result == null) { 24 | if (!notified) { 25 | console.log("Waiting for transaction to be mined ..."); 26 | notified = true; 27 | } 28 | if (delay === 0) { 29 | throw new Error( 30 | `Timed out after ${retries} attempts waiting for transaction to be mined` 31 | ); 32 | } else { 33 | const waitInSeconds = (retries * delay) / 1000; 34 | throw new Error( 35 | `Timed out after ${waitInSeconds}s waiting for transaction to be mined` 36 | ); 37 | } 38 | } else { 39 | return resolve(result); 40 | } 41 | }) 42 | .catch((reason) => { 43 | if (times - 1 > 0) { 44 | // eslint-disable-next-line promise/no-nesting 45 | return waitFor(delay) 46 | .then(retryOperation.bind(null, operationToRetry, times - 1)) 47 | .then(resolve) 48 | .catch(reject); 49 | } 50 | return reject(reason); 51 | }); 52 | }); 53 | }; 54 | 55 | return retryOperation(operation, retries); 56 | }; 57 | 58 | exports.waitForTransactionWithRetries = waitForTransactionWithRetries; 59 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright ConsenSys Software Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | * 13 | * SPDX-License-Identifier: Apache-2.0 14 | */ 15 | 16 | const Utils = require("./utils"); 17 | const Priv = require("./priv"); 18 | const FlexiblePrivacyGroup = require("./flexiblePrivacyGroup"); 19 | const Eth = require("./eth"); 20 | const Ptm = require("./ptm"); 21 | const Raft = require("./raft"); 22 | const Istanbul = require("./istanbul"); 23 | const Permission = require("./permission"); 24 | 25 | /** 26 | * Handles elements 27 | * @name Web3Quorum 28 | * @class Web3Quorum 29 | * 30 | * @param {Object} web3 instance of the web3.js library 31 | * @param {Object} [enclaveOptions] configs of the transaction manager required for GoQuorum case only 32 | * @param {string} enclaveOptions.ipcPath absolute file path to the ipc of the transaction manager 33 | * @param {string} enclaveOptions.privateUrl http url to the transaction manager 34 | * @param {Object} enclaveOptions.tlsSettings TLS configuration for the transaction manager when using HTTPS in privateUrl 35 | * @param {Buffer} enclaveOptions.tlsSettings.key client key buffer 36 | * @param {Buffer} enclaveOptions.tlsSettings.clcert client certificate buffer 37 | * @param {Buffer} enclaveOptions.tlsSettings.cacert CA certificate buffer 38 | * @param {Boolean} enclaveOptions.tlsSettings.allowInsecure 39 | * @param {Boolean} [isQuorum=false] indicates if the connected to client is quorum or besu 40 | */ 41 | function Web3Quorum(web3, enclaveOptions = {}, isQuorum = false) { 42 | if (web3.currentProvider == null) { 43 | throw new Error("Missing provider"); 44 | } 45 | // TODO: to be updated by a method call web3_clientVersion 46 | // eslint-disable-next-line no-param-reassign 47 | web3.isQuorum = isQuorum; 48 | 49 | // Extend the utils namespace methods 50 | Utils(web3); 51 | 52 | Ptm(web3, enclaveOptions); 53 | // Extend the priv namespace methods 54 | Priv(web3); 55 | // Extend the flexiblePrivacyGroup namespace methods 56 | FlexiblePrivacyGroup(web3); 57 | 58 | // Extend the eth namespace methods with GoQuorum methods 59 | Eth(web3); 60 | 61 | // Extend the raft namespace methods 62 | Raft(web3); 63 | 64 | // Extend the Istanbul namespace methods 65 | Istanbul(web3); 66 | 67 | // Extend the Permission namespace methods 68 | Permission(web3); 69 | 70 | return web3; 71 | } 72 | 73 | module.exports = Web3Quorum; 74 | -------------------------------------------------------------------------------- /src/privateTransaction.test.js: -------------------------------------------------------------------------------- 1 | const utils = require("ethereumjs-util"); 2 | 3 | const PrivateTransaction = require("./privateTransaction.js"); 4 | const txFixtures = require("./test-utils/txs.json"); 5 | 6 | describe("[Transaction]: Basic functions", () => { 7 | const transactions = []; 8 | 9 | it("should decode transactions", () => { 10 | txFixtures.forEach((tx) => { 11 | const pt = new PrivateTransaction(tx.raw); 12 | expect(`0x${pt.nonce.toString("hex")}`).toEqual(tx.raw[0]); 13 | expect(`0x${pt.gasPrice.toString("hex")}`).toEqual(tx.raw[1]); 14 | expect(`0x${pt.gasLimit.toString("hex")}`).toEqual(tx.raw[2]); 15 | expect(`0x${pt.to.toString("hex")}`).toEqual(tx.raw[3]); 16 | expect(`0x${pt.value.toString("hex")}`).toEqual(tx.raw[4]); 17 | expect(`0x${pt.data.toString("hex")}`).toEqual(tx.raw[5]); 18 | expect(`0x${pt.v.toString("hex")}`).toEqual(tx.raw[6]); 19 | expect(`0x${pt.r.toString("hex")}`).toEqual(tx.raw[7]); 20 | expect(`0x${pt.s.toString("hex")}`).toEqual(tx.raw[8]); 21 | expect(pt.privateFrom.toString("base64")).toEqual(tx.raw[9]); 22 | // eslint-disable-next-line no-plusplus 23 | for (let i = 0; i < tx.raw[10].length; i++) { 24 | expect(pt.privateFor[i].toString("base64")).toEqual(tx.raw[10][i]); 25 | } 26 | expect(pt.privacyGroupId.toString()).toEqual(tx.raw[11]); 27 | expect(pt.restriction.toString()).toEqual(tx.raw[12]); 28 | transactions.push(pt); 29 | }); 30 | }); 31 | 32 | it("should decode rlp", () => { 33 | transactions.forEach((tx, i) => { 34 | expect(transactions[i].serialize()).toEqual( 35 | new PrivateTransaction(txFixtures[i].rlp).serialize() 36 | ); 37 | }); 38 | }); 39 | 40 | it("should serialize", () => { 41 | transactions.forEach((tx, i) => { 42 | expect(`0x${tx.serialize().toString("hex")}`).toEqual(txFixtures[i].rlp); 43 | }); 44 | }); 45 | 46 | it("should sign tx", () => { 47 | transactions.forEach((tx, i) => { 48 | const privKey = Buffer.from(txFixtures[i].privateKey, "hex"); 49 | expect(() => { 50 | tx.sign(privKey); 51 | }).not.toThrowError(); 52 | }); 53 | }); 54 | 55 | it("should get sender's address after signing it", () => { 56 | transactions.forEach((tx, i) => { 57 | expect(tx.getSenderAddress().toString("hex")).toEqual( 58 | txFixtures[i].sendersAddress 59 | ); 60 | }); 61 | }); 62 | 63 | it("should get sender's public key after signing it", () => { 64 | transactions.forEach((tx, i) => { 65 | expect(tx.getSenderPublicKey().toString("hex")).toEqual( 66 | utils 67 | .privateToPublic(Buffer.from(txFixtures[i].privateKey, "hex")) 68 | .toString("hex") 69 | ); 70 | }); 71 | }); 72 | }); 73 | -------------------------------------------------------------------------------- /src/ptm.test.js: -------------------------------------------------------------------------------- 1 | const rp = require("request-promise-native"); 2 | 3 | jest.mock("request-promise-native"); 4 | const Ptm = require("./ptm"); 5 | 6 | describe("Private Transaction Manager", () => { 7 | const privateUrl = "https://localhost:22000"; 8 | const web3 = new Ptm({}, { privateUrl }); 9 | rp.mockResolvedValue({ 10 | key: "asdasd", 11 | }); 12 | 13 | it("should use the ipcPath passed", async () => { 14 | const { ptm } = new Ptm( 15 | {}, 16 | { 17 | ipcPath: "tm.ipc", 18 | } 19 | ); 20 | await ptm.upCheck("payload", "from", "to"); 21 | 22 | expect(rp).toBeCalledWith({ 23 | method: "GET", 24 | uri: "http://unix:tm.ipc:/upcheck", 25 | }); 26 | }); 27 | 28 | it("should use the privateUrl passed", async () => { 29 | const { ptm } = new Ptm( 30 | {}, 31 | { 32 | privateUrl, 33 | } 34 | ); 35 | await ptm.upCheck("payload", "from", "to"); 36 | 37 | expect(rp).toBeCalledWith({ 38 | method: "GET", 39 | uri: `${privateUrl}/upcheck`, 40 | }); 41 | }); 42 | 43 | it("should use the tlsSettings passed", async () => { 44 | const { ptm } = new Ptm( 45 | {}, 46 | { 47 | privateUrl, 48 | tlsSettings: { 49 | key: "key", 50 | cacert: "cacert", 51 | }, 52 | } 53 | ); 54 | await ptm.upCheck("payload", "from", "to"); 55 | 56 | expect(rp).toBeCalledWith({ 57 | method: "GET", 58 | uri: `${privateUrl}/upcheck`, 59 | clientKey: "key", 60 | ca: "cacert", 61 | }); 62 | }); 63 | 64 | it("should call send api with the right payload", async () => { 65 | await web3.ptm.send({ 66 | data: "0x123123123", 67 | privateFrom: "from", 68 | privateFor: "to", 69 | }); 70 | 71 | expect(rp).toBeCalledWith({ 72 | body: { from: "from", payload: "EjEjEg==", to: "to" }, 73 | json: true, 74 | method: "POST", 75 | uri: `${privateUrl}/send`, 76 | }); 77 | }); 78 | 79 | it("should call storeRaw api with the right payload", async () => { 80 | await web3.ptm.storeRaw({ 81 | data: "0x123123123", 82 | privateFrom: "from", 83 | }); 84 | 85 | expect(rp).toBeCalledWith({ 86 | body: { from: "from", payload: "EjEjEg==" }, 87 | json: true, 88 | method: "POST", 89 | uri: `${privateUrl}/storeraw`, 90 | }); 91 | }); 92 | 93 | it("should call keys api", async () => { 94 | await web3.ptm.keys(); 95 | 96 | expect(rp).toBeCalledWith({ 97 | method: "GET", 98 | uri: `${privateUrl}/keys`, 99 | }); 100 | }); 101 | 102 | it("should call party info keys api", async () => { 103 | await web3.ptm.partyInfoKeys(); 104 | 105 | expect(rp).toBeCalledWith({ 106 | method: "GET", 107 | uri: `${privateUrl}/partyinfo/keys`, 108 | }); 109 | }); 110 | 111 | it("should call up-check api", async () => { 112 | await web3.ptm.upCheck(); 113 | 114 | expect(rp).toBeCalledWith({ 115 | method: "GET", 116 | uri: `${privateUrl}/upcheck`, 117 | }); 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /src/raft.js: -------------------------------------------------------------------------------- 1 | /** 2 | * For more details about the {@link https://docs.goquorum.consensys.net/en/stable/Reference/Consensus/Raft-RPC-API raft JSON-RPC APIs} 3 | * @module Raft 4 | */ 5 | function Raft(web3) { 6 | web3.extend({ 7 | property: "raft", 8 | methods: [ 9 | /** 10 | * @function cluster 11 | * @return {raftClusterDetails[]} result 12 | */ 13 | { 14 | name: "cluster", 15 | call: "raft_cluster", 16 | params: 0, 17 | }, 18 | /** 19 | * @function role 20 | * @return {String} Role of the node in Raft GoQuorum 21 | */ 22 | { 23 | name: "role", 24 | call: "raft_role", 25 | params: 0, 26 | }, 27 | /** 28 | * @function leader 29 | * @return {String} enode id of the leader 30 | */ 31 | { 32 | name: "leader", 33 | call: "raft_leader", 34 | params: 0, 35 | }, 36 | /** 37 | * @function addPeer 38 | * @param {String} enodeId enode id of the node to be added to the network 39 | * @return {Number} Raft id for the node being added 40 | */ 41 | { 42 | name: "addPeer", 43 | call: "raft_addPeer", 44 | params: 1, 45 | }, 46 | /** 47 | * @function removePeer 48 | * @param {Number} raftId Raft id of the node to be removed from the cluster 49 | * @return {null} 50 | */ 51 | { 52 | name: "removePeer", 53 | call: "raft_removePeer", 54 | params: 1, 55 | }, 56 | /** 57 | * @function addLearner 58 | * @param {String} enodeId enode id of the learner node to be added to the network 59 | * @return {Number} Raft id for the node being added 60 | */ 61 | { 62 | name: "addLearner", 63 | call: "raft_addLearner", 64 | params: 1, 65 | }, 66 | /** 67 | * @function promoteToPeer 68 | * @param {Number} raftId Raft id of the node to be promoted 69 | * @return {Boolean} 70 | */ 71 | { 72 | name: "promoteToPeer", 73 | call: "raft_promoteToPeer", 74 | params: 1, 75 | }, 76 | ], 77 | }); 78 | return web3; 79 | } 80 | 81 | module.exports = Raft; 82 | -------------------------------------------------------------------------------- /src/test-utils/txs.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "rlp": "0xf9026b808203e8832dc6c08080b901cb608060405234801561001057600080fd5b5060008054600160a060020a03191633179055610199806100326000396000f3fe6080604052600436106100565763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416633fa4f245811461005b5780636057361d1461008257806367e404ce146100ae575b600080fd5b34801561006757600080fd5b506100706100ec565b60408051918252519081900360200190f35b34801561008e57600080fd5b506100ac600480360360208110156100a557600080fd5b50356100f2565b005b3480156100ba57600080fd5b506100c3610151565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60025490565b604080513381526020810183905281517fc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f5929181900390910190a16002556001805473ffffffffffffffffffffffffffffffffffffffff191633179055565b60015473ffffffffffffffffffffffffffffffffffffffff169056fea165627a7a72305820c7f729cb24e05c221f5aa913700793994656f233fe2ce3b9fd9a505ea17e8d8a0029820fe8a0ecf50d295e2d574c0d6ca1025caaeafabd21e1ec479609dddf00a45151625792a049e96d46b82f0019aa2b28a50856b2117941db2367e449c4eb531b4933e479f0a0035695b4cc4b0941e60551d7a19cf30603db5bfc23e5ac43a56f57f25f75486ae1a02a8d9b56a0fe9cd94d60be4413bcb721d3a7be27ed8e28b3a6346df874ee141b8a72657374726963746564", 4 | "privateKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 5 | "sendersAddress": "fe3b557e8fb62b89f4916b721be55ceb828dbd73", 6 | "type": "contract", 7 | "cost": 680, 8 | "raw": [ 9 | "0x", 10 | "0x03e8", 11 | "0x2dc6c0", 12 | "0x", 13 | "0x", 14 | "0x608060405234801561001057600080fd5b5060008054600160a060020a03191633179055610199806100326000396000f3fe6080604052600436106100565763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416633fa4f245811461005b5780636057361d1461008257806367e404ce146100ae575b600080fd5b34801561006757600080fd5b506100706100ec565b60408051918252519081900360200190f35b34801561008e57600080fd5b506100ac600480360360208110156100a557600080fd5b50356100f2565b005b3480156100ba57600080fd5b506100c3610151565b6040805173ffffffffffffffffffffffffffffffffffffffff9092168252519081900360200190f35b60025490565b604080513381526020810183905281517fc9db20adedc6cf2b5d25252b101ab03e124902a73fcb12b753f3d1aaa2d8f9f5929181900390910190a16002556001805473ffffffffffffffffffffffffffffffffffffffff191633179055565b60015473ffffffffffffffffffffffffffffffffffffffff169056fea165627a7a72305820c7f729cb24e05c221f5aa913700793994656f233fe2ce3b9fd9a505ea17e8d8a0029", 15 | "0x0fe8", 16 | "0xecf50d295e2d574c0d6ca1025caaeafabd21e1ec479609dddf00a45151625792", 17 | "0x49e96d46b82f0019aa2b28a50856b2117941db2367e449c4eb531b4933e479f0", 18 | "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=", 19 | [ 20 | "Ko2bVqD+nNlNYL5EE7y3IdOnviftjiizpjRt+HTuFBs=" 21 | ], 22 | "restricted" 23 | ] 24 | } 25 | ] -------------------------------------------------------------------------------- /src/typedefs.doc.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef raftClusterDetails 3 | * @property {String} hostName: DNS name or the host IP address 4 | * @property {Boolean} nodeActive: true if the node is active in Raft cluster else false 5 | * @property {String} nodeId: enode id of the node 6 | * @property {Number} p2pPort: p2p port 7 | * @property {Number} raftId: Raft id of the node 8 | * @property {Number} raftPort: Raft port 9 | * @property {String} role: role of the node in Raft GoQuorum. Can be minter/ verifier/ learner. In case there is no leader at network level it will be returned as "" 10 | */ 11 | -------------------------------------------------------------------------------- /src/util/index.js: -------------------------------------------------------------------------------- 1 | const hexToBase64 = (str) => { 2 | return Buffer.from(str, "hex").toString("base64"); 3 | }; 4 | 5 | const base64toHex = (str) => { 6 | return Buffer.from(str, "base64").toString("hex"); 7 | }; 8 | 9 | const intToHex = (int) => { 10 | return `0x${int.toString(16)}`; 11 | }; 12 | 13 | module.exports = { hexToBase64, base64toHex, intToHex }; 14 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright ConsenSys Software Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with 5 | * the License. You may obtain a copy of the License at 6 | * 7 | * http://www.apache.org/licenses/LICENSE-2.0 8 | * 9 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on 10 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the 11 | * specific language governing permissions and limitations under the License. 12 | * 13 | * SPDX-License-Identifier: Apache-2.0 14 | */ 15 | 16 | const RLP = require("rlp"); 17 | const _ = require("lodash"); 18 | const { keccak256 } = require("./util/custom-ethjs-util"); 19 | 20 | /** 21 | * @module utils 22 | */ 23 | function Utils(web3) { 24 | /** 25 | * Generate a privacyGroupId 26 | * @function generatePrivacyGroup 27 | * @param {Object} options 28 | * @param {string} options.privateFor 29 | * @param {string} options.privateFrom 30 | * 31 | * @returns String 32 | */ 33 | const generatePrivacyGroup = (options) => { 34 | const participants = _.chain(options.privateFor || []) 35 | .concat(options.privateFrom) 36 | .uniq() 37 | .map((publicKey) => { 38 | const buffer = Buffer.from(publicKey, "base64"); 39 | let result = 1; 40 | buffer.forEach((value) => { 41 | // eslint-disable-next-line no-bitwise 42 | result = (31 * result + ((value << 24) >> 24)) & 0xffffffff; 43 | }); 44 | return { b64: publicKey, buf: buffer, hash: result }; 45 | }) 46 | .sort((a, b) => { 47 | return a.hash - b.hash; 48 | }) 49 | .map((x) => { 50 | return x.buf; 51 | }) 52 | .value(); 53 | 54 | const rlp = RLP.encode(participants); 55 | 56 | return Buffer.from(keccak256(rlp)).toString("base64"); 57 | }; 58 | 59 | /** 60 | * @function setPrivate 61 | * @param {String} rawTransaction 62 | * @return {Buffer} encoded data 63 | */ 64 | const setPrivate = (rawTransaction) => { 65 | const decoded = RLP.decode(rawTransaction); 66 | const compareTo = Buffer.from("1c", "hex"); 67 | if (decoded[6].compare(compareTo) === 0) 68 | decoded[6] = Buffer.from("26", "hex"); 69 | else decoded[6] = Buffer.from("25", "hex"); 70 | return RLP.encode(decoded); 71 | }; 72 | 73 | Object.assign(web3.utils, { 74 | generatePrivacyGroup, 75 | setPrivate, 76 | }); 77 | return web3; 78 | } 79 | module.exports = Utils; 80 | -------------------------------------------------------------------------------- /src/utils.test.js: -------------------------------------------------------------------------------- 1 | const Utils = require("./utils"); 2 | const txFixtures = require("./test-utils/keySets.json"); 3 | 4 | describe("Utils", () => { 5 | describe("Privacy Group Generation", () => { 6 | it("should generate correct privacy group id", () => { 7 | const { generatePrivacyGroup } = new Utils({ utils: {} }).utils; 8 | txFixtures.forEach((pg) => { 9 | const expected = pg.privacyGroupId; 10 | const input = pg.privacyGroup; 11 | expect(generatePrivacyGroup({ privateFrom: input })).toEqual(expected); 12 | }); 13 | }); 14 | }); 15 | 16 | describe("web3.utils.setPrivate", () => { 17 | it("should set the raw transaction to private", () => { 18 | const { setPrivate } = new Utils({ utils: {} }).utils; 19 | const res = setPrivate( 20 | "0xf88e81b18083419ce08080b840184a2b03d4be85960a2cd856eb98e00f99a0f3919c3dc2554f292a1e6cd3a47c5f6fd4da56896f81fd545d2093561f2aaaf89d268d9f7386cd88853a6b68fed61ca04395f7291f430b0643fcb136f9015673b76e94e1fd86bde494af8b814f335c85a0183a86724513fcd1788e2db25cb219a8c2643c3c678c3ee4972134c67474b238" 21 | ).toString("hex"); 22 | expect(res).toEqual( 23 | "f88e81b18083419ce08080b840184a2b03d4be85960a2cd856eb98e00f99a0f3919c3dc2554f292a1e6cd3a47c5f6fd4da56896f81fd545d2093561f2aaaf89d268d9f7386cd88853a6b68fed626a04395f7291f430b0643fcb136f9015673b76e94e1fd86bde494af8b814f335c85a0183a86724513fcd1788e2db25cb219a8c2643c3c678c3ee4972134c67474b238" 24 | ); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /tests/tests-utils/constants.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | URL: "http://localhost:20000", 3 | CHAIN_ID: "0x7E2", 4 | BLOCK_NUMBER: "0xb", 5 | PRIVACY_GROUP_ID: "xQdJRZp0ejzJRqU6N3+EsFo+qTIcdWX50V6YTyvBepw=", 6 | ADDRESS: "0xfe3b557e8fb62b89f4916b721be55ceb828dbd73", 7 | ENCLAVE_ADDRESS: "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=", 8 | FILTER_ID: "0x4a35b92809d73f4f53a2355d62125442", 9 | SIGNED_RLP: "0xf869018203e882520894f", 10 | TRANSACTION_COUNT: "0xa", 11 | TRANSACTION_HASH: 12 | "0x623c4ce5275a87b91f4f1c521012d39ca19311c787bde405490f4c0426a71498", 13 | PRIVATE_KEY: 14 | "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", 15 | TRANSACTION_OBJECT: { 16 | from: "0x2daf5374fce5edbc8e2a8697c15331677e6ebf0b", 17 | to: "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b", 18 | data: "0x23455654", 19 | value: 1, 20 | gas: 11, 21 | gasPrice: 11, 22 | }, 23 | LOG_OBJECT: { 24 | logIndex: "0xb", 25 | removed: false, 26 | blockNumber: "0xb", 27 | blockHash: 28 | "0x1c8200667a869e99b945374c37277b5ee7a7ae67943e13c82563381387553dbb", 29 | transactionHash: 30 | "0x623c4ce5275a87b91f4f1c521012d39ca19311c787bde405490f4c0426a71498", 31 | transactionIndex: "0x0", 32 | address: "0x991cc548c154b2953cc48c02f782e1314097dfbb", 33 | data: "0x", 34 | topics: [ 35 | "0x85bea11d86cefb165374e0f727bacf21dc2f4ea816493981ecf72dcfb212a410", 36 | "0x0000000000000000000000000000000000000000000000000000000000000002", 37 | ], 38 | }, 39 | TRANSACTION_RECEIPT: { 40 | status: "0x1", 41 | transactionHash: 42 | "0x623c4ce5275a87b91f4f1c521012d39ca19311c787bde405490f4c0426a71498", 43 | transactionIndex: 0, 44 | blockHash: 45 | "0xef95f2f1ed3ca60b048b4bf67cde2195961e0bba6f70bcbea9a2c4e133e34b46", 46 | blockNumber: 3, 47 | contractAddress: "0x11f4d0A3c12e86B4b5F39B213F7E19D048276DAe", 48 | cumulativeGasUsed: 314159, 49 | gasUsed: 30234, 50 | }, 51 | CREATE_PRIVACY_GROUP_OBJECT: { 52 | addresses: [ 53 | "sTZpbQhcOfd9ZaFDnC00e/N2Ofv9p4/ZTBbEeVtXJ3E=", 54 | "quhb1pQPGN1w8ZSZSyiIfncEAlVY/M/rauSyQ5wVMRE=", 55 | ], 56 | name: "Group A", 57 | description: "Description Group A", 58 | }, 59 | }; 60 | -------------------------------------------------------------------------------- /tests/tests-utils/httpMock.js: -------------------------------------------------------------------------------- 1 | const nock = require("nock"); 2 | const { URL } = require("./constants"); 3 | 4 | const mockHttpPost = (fn, results, n = 1) => { 5 | nock(URL) 6 | .post("/") 7 | .times(n) 8 | .reply((_, body) => { 9 | if (fn) { 10 | fn(body); 11 | } 12 | const result = Array.isArray(results) ? results.shift() : results; 13 | return [ 14 | 201, 15 | { 16 | jsonrpc: "2.0", 17 | id: body.id, 18 | result: result || "0x0", 19 | }, 20 | ]; 21 | }); 22 | }; 23 | 24 | const resetMock = () => { 25 | nock.cleanAll(); 26 | }; 27 | 28 | module.exports = { 29 | mockHttpPost, 30 | resetMock, 31 | }; 32 | -------------------------------------------------------------------------------- /tests/web3.istanbul.test.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../src/index"); 3 | const { mockHttpPost, resetMock } = require("./tests-utils/httpMock"); 4 | const { URL } = require("./tests-utils/constants"); 5 | 6 | describe("web3.istanbul", () => { 7 | const web3 = new Web3Quorum(new Web3(URL)); 8 | 9 | afterEach(() => { 10 | resetMock(); 11 | }); 12 | 13 | describe.each(["candidates", "nodeAddress"])("istanbul", (method) => { 14 | it(`should call istanbul_${method}`, async () => { 15 | let request; 16 | mockHttpPost((data) => { 17 | request = data; 18 | }); 19 | 20 | await web3.istanbul[method](); 21 | 22 | expect(request.jsonrpc).toEqual("2.0"); 23 | expect(request.method).toEqual(`istanbul_${method}`); 24 | expect(request.params).toEqual([]); 25 | }); 26 | }); 27 | 28 | describe.each([ 29 | "discard", 30 | "getSnapshotAtHash", 31 | "getValidatorsAtHash", 32 | "getSignersFromBlockByHash", 33 | ])("istanbul", (method) => { 34 | it(`should call istanbul_${method}`, async () => { 35 | let request; 36 | mockHttpPost((data) => { 37 | request = data; 38 | }); 39 | 40 | await web3.istanbul[method]("0x123"); 41 | 42 | expect(request.jsonrpc).toEqual("2.0"); 43 | expect(request.method).toEqual(`istanbul_${method}`); 44 | expect(request.params).toEqual(["0x123"]); 45 | }); 46 | 47 | it(`throw error when call istanbul_${method} with no param`, async () => { 48 | await expect(() => { 49 | return web3.istanbul[method](); 50 | }).toThrow("Invalid number of parameters"); 51 | }); 52 | }); 53 | 54 | describe.each(["getSnapshot", "getValidators", "isValidator"])( 55 | "istanbul", 56 | (method) => { 57 | it(`should call istanbul_${method}`, async () => { 58 | let request; 59 | mockHttpPost((data) => { 60 | request = data; 61 | }); 62 | 63 | await web3.istanbul[method](123); 64 | 65 | expect(request.jsonrpc).toEqual("2.0"); 66 | expect(request.method).toEqual(`istanbul_${method}`); 67 | expect(request.params).toEqual(["0x7b"]); 68 | }); 69 | 70 | it(`should call istanbul_${method} with latest as block number`, async () => { 71 | let request; 72 | mockHttpPost((data) => { 73 | request = data; 74 | }); 75 | 76 | await web3.istanbul[method](); 77 | 78 | expect(request.jsonrpc).toEqual("2.0"); 79 | expect(request.method).toEqual(`istanbul_${method}`); 80 | expect(request.params).toEqual(["latest"]); 81 | }); 82 | } 83 | ); 84 | 85 | it(`should call istanbul_getSignersFromBlock`, async () => { 86 | let request; 87 | mockHttpPost((data) => { 88 | request = data; 89 | }); 90 | 91 | await web3.istanbul.getSignersFromBlock(123); 92 | 93 | expect(request.jsonrpc).toEqual("2.0"); 94 | expect(request.method).toEqual(`istanbul_getSignersFromBlock`); 95 | expect(request.params).toEqual(["0x7b"]); 96 | }); 97 | }); 98 | -------------------------------------------------------------------------------- /tests/web3.raft.test.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../src/index"); 3 | const { mockHttpPost, resetMock } = require("./tests-utils/httpMock"); 4 | const { URL } = require("./tests-utils/constants"); 5 | 6 | describe("web3.raft", () => { 7 | const web3 = new Web3Quorum(new Web3(URL)); 8 | 9 | afterEach(() => { 10 | resetMock(); 11 | }); 12 | 13 | describe.each(["cluster", "role", "leader"])("raft", (method) => { 14 | it(`should call raft_${method}`, async () => { 15 | let request; 16 | mockHttpPost((data) => { 17 | request = data; 18 | }); 19 | 20 | await web3.raft[method](); 21 | 22 | expect(request.jsonrpc).toEqual("2.0"); 23 | expect(request.method).toEqual(`raft_${method}`); 24 | expect(request.params).toEqual([]); 25 | }); 26 | }); 27 | 28 | describe.each(["addPeer", "removePeer", "addLearner", "promoteToPeer"])( 29 | "raft", 30 | (method) => { 31 | it(`should call raft_${method}`, async () => { 32 | let request; 33 | mockHttpPost((data) => { 34 | request = data; 35 | }); 36 | 37 | await web3.raft[method]("param"); 38 | 39 | expect(request.jsonrpc).toEqual("2.0"); 40 | expect(request.method).toEqual(`raft_${method}`); 41 | expect(request.params).toEqual(["param"]); 42 | }); 43 | 44 | it(`throw error when call raft_${method} with no param`, async () => { 45 | await expect(() => { 46 | return web3.raft[method](); 47 | }).toThrow("Invalid number of parameters"); 48 | }); 49 | } 50 | ); 51 | }); 52 | -------------------------------------------------------------------------------- /tests/web3Quorum.test.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | const Web3Quorum = require("../src"); 3 | const { URL } = require("./tests-utils/constants"); 4 | 5 | describe("web3Quorum", () => { 6 | const web3 = new Web3Quorum(new Web3(URL)); 7 | 8 | it("should match the web3Quorum priv namespace snapshot", () => { 9 | expect(web3.priv).toMatchSnapshot(); 10 | }); 11 | 12 | it("should match the web3Quorum eth.flexiblePrivacyGroup namespace snapshot", () => { 13 | expect(web3.eth.flexiblePrivacyGroup).toMatchSnapshot(); 14 | expect(typeof web3.eth.flexiblePrivacyGroup.find).toEqual("function"); 15 | }); 16 | 17 | it("should match the web3Quorum utils namespace snapshot", () => { 18 | expect(web3.utils).toMatchSnapshot(); 19 | expect(typeof web3.utils.generatePrivacyGroup).toEqual("function"); 20 | }); 21 | 22 | it("should match the web3Quorum eth namespace snapshot", () => { 23 | const { 24 | fillTransaction, 25 | storageRoot, 26 | getQuorumPayload, 27 | sendTransactionAsync, 28 | getContractPrivacyMetadata, 29 | } = web3.eth; 30 | expect({ 31 | fillTransaction, 32 | storageRoot, 33 | getQuorumPayload, 34 | sendTransactionAsync, 35 | getContractPrivacyMetadata, 36 | }).toMatchSnapshot(); 37 | expect(typeof fillTransaction).toEqual("function"); 38 | expect(typeof storageRoot).toEqual("function"); 39 | expect(typeof getQuorumPayload).toEqual("function"); 40 | expect(typeof sendTransactionAsync).toEqual("function"); 41 | expect(typeof getContractPrivacyMetadata).toEqual("function"); 42 | }); 43 | 44 | it("should match the web3Quorum raft namespace snapshot", () => { 45 | expect(web3.raft).toMatchSnapshot(); 46 | }); 47 | 48 | it("should match the web3Quorum Istanbul namespace snapshot", () => { 49 | expect(web3.istanbul).toMatchSnapshot(); 50 | }); 51 | 52 | it("should match the web3Quorum Permission namespace snapshot", () => { 53 | expect(web3.permission).toMatchSnapshot(); 54 | }); 55 | }); 56 | --------------------------------------------------------------------------------