├── .eslintrc.js ├── .gitattributes ├── .github └── workflows │ └── main.yml ├── .gitignore ├── .solcover.js ├── LICENSE ├── README.md ├── circuits ├── balancesupdater.circom ├── checkfees.circom ├── decodefloat.circom ├── decodetx.circom ├── feeplandecoder.circom ├── feetableselector.circom ├── feeupdater.circom ├── requiredtxverifier.circom ├── rollup.circom ├── rolluptx.circom ├── rolluptxstates.circom ├── statepacker.circom └── unpackax.circom ├── cli-pob ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── cli-pob.js ├── config-example.json ├── index.js ├── package-lock.json ├── package.json ├── src │ ├── list-errors.js │ └── utils.js └── test │ ├── bid.test.js │ ├── helpers │ ├── add-blocks.js │ ├── build-configs.test.js │ └── info.md │ ├── multibid.test.js │ └── withdraw.test.js ├── cli-pos ├── .eslintrc ├── .gitignore ├── LICENSE ├── cli-pos.js ├── config-example.json ├── index.js ├── package-lock.json ├── package.json ├── src │ ├── list-errors.js │ └── utils.js └── test │ ├── helpers │ ├── add-blocks.js │ ├── build-configs.test.js │ └── info.md │ ├── register.test.js │ ├── unregister.test.js │ └── withdraw.test.js ├── contracts ├── Migrations.sol ├── Rollup.sol ├── RollupBurnAuction.sol ├── RollupInterface.sol ├── RollupPoB.sol ├── RollupPoS.sol ├── VerifierInterface.sol ├── lib │ ├── Memory.sol │ ├── Poseidon.sol │ ├── RollupHelpers.sol │ ├── RollupPoBHelpers.sol │ └── RollupPoSHelpers.sol ├── test │ ├── EventTest.sol │ ├── RollupBurnAuctionTest.sol │ ├── RollupHelpersTest.sol │ ├── RollupPoBTest.sol │ ├── RollupPoSHelpersTest.sol │ ├── RollupPoSTest.sol │ ├── RollupTest.sol │ ├── TokenRollup.sol │ ├── TokenTest.sol │ └── VerifierHelper.sol └── verifiers │ ├── Verifier_128_24.sol │ └── Verifier_16_24.sol ├── doc ├── feeSelector.monopic ├── hash-specs.md ├── info.md ├── repo-organization.md ├── rollap_tree.txt ├── rollup_tree.monopic ├── rollup_tx.monopic ├── rollup_tx.txt └── rqtxverifier.monopic ├── docker ├── copy-config.sh ├── docker-cli-pob │ ├── .env.example │ ├── Dockerfile │ ├── config-cli-pob │ │ └── build-config.js │ └── docker-compose.yml ├── docker-cli-pos │ ├── Dockerfile │ ├── config-cli-pos │ │ └── build-config.js │ └── docker-compose.yml ├── docker-contracts │ ├── .env.example │ ├── Dockerfile │ ├── config-contracts │ │ └── deployment-script.js │ └── docker-compose.yml ├── docker-operator │ ├── .env.example │ ├── docker-compose.yml │ ├── operator │ │ ├── Dockerfile │ │ ├── config-operator │ │ │ ├── build-config.js │ │ │ └── create-config-env.sh │ │ └── config.env.example │ ├── server-proof │ │ ├── Dockerfile │ │ └── config.env.example │ └── synch-pool │ │ ├── Dockerfile │ │ ├── config-pool │ │ ├── build-config.js │ │ ├── create-config-env.sh │ │ └── custom.json │ │ └── config.env.example └── docker-webapp │ ├── .env.example │ ├── Dockerfile │ ├── config-webapp │ └── build-config.js │ └── docker-compose.yml ├── index.js ├── js ├── batchbuilder.js ├── constants.js ├── rollup-state.js ├── rollupaccount.js ├── rollupdb.js ├── smttmpdb.js ├── tmpstate.js ├── tx.js ├── txpool.js └── utils.js ├── migrations ├── 1_initial_migration.js └── 2_deploy_contracts.js ├── package-lock.json ├── package.json ├── rollup-cli ├── .eslintrc ├── .gitignore ├── LICENSE ├── README.md ├── cli.js ├── config-example.json ├── docs │ └── cli-commands.md ├── helpers │ ├── abis │ │ ├── ERC20Abi.json │ │ └── RollupAbi.json │ ├── list-errors.js │ └── utils.js ├── index.js ├── keys.json ├── package-lock.json ├── package.json ├── src │ ├── actions │ │ ├── offchain │ │ │ ├── offchain.js │ │ │ └── send.js │ │ └── onchain │ │ │ ├── approve.js │ │ │ ├── deposit-and-transfer.js │ │ │ ├── deposit-on-top.js │ │ │ ├── deposit.js │ │ │ ├── force-withdraw.js │ │ │ ├── onchain.js │ │ │ ├── transfer.js │ │ │ ├── utils.js │ │ │ └── withdraw.js │ └── utils │ │ ├── cli-utils.js │ │ ├── db.js │ │ ├── ethereum-wallet.js │ │ └── wallet.js ├── test │ ├── actions │ │ ├── onchain.test.js │ │ └── send.test.js │ ├── helpers │ │ ├── api-client.js │ │ └── helpers.js │ ├── integration-test │ │ ├── cli.test.js │ │ └── config │ │ │ ├── build-resources.js │ │ │ ├── build-resources.test.js │ │ │ ├── info.md │ │ │ └── setup-cli.test.js │ └── utils │ │ ├── db.test.js │ │ ├── ethereum-wallet.test.js │ │ └── wallet.test.js └── tools │ ├── gunners │ ├── checkRollupOnChain.js │ ├── depositGunner.js │ ├── depositOffchainGunner.js │ ├── info.md │ ├── sendGunner.js │ └── utils.js │ ├── slasher.js │ └── slasher.test.js ├── rollup-operator ├── .eslintrc.js ├── .gitignore ├── LICENSE ├── README.md ├── doc │ └── overview.md ├── index.js ├── package-lock.json ├── package.json ├── src │ ├── cli-external-operator.js │ ├── cli-proof-server.js │ ├── constants.js │ ├── proof-of-burn │ │ ├── interface-pob.js │ │ ├── loop-bids.js │ │ ├── loop-manager-pob.js │ │ └── synch-pob.js │ ├── proof-of-stake │ │ ├── interface-pos.js │ │ ├── loop-manager-pos.js │ │ └── synch-pos.js │ ├── server-proof.js │ ├── server │ │ ├── proof-of-burn │ │ │ ├── http-methods-pob.js │ │ │ └── operator-pob.js │ │ ├── proof-of-stake │ │ │ ├── http-methods-pos.js │ │ │ └── operator-pos.js │ │ └── utils.js │ ├── synch-pool-service │ │ ├── README.md │ │ ├── api-bitfinex.js │ │ ├── erc20-abi.js │ │ ├── run-synch-pool.js │ │ └── synch-pool-service.js │ ├── synch-pool.js │ ├── synch-tokens.js │ ├── synch.js │ └── utils.js └── test │ ├── config │ ├── config.env-example │ ├── custom-token-example.json │ ├── pool-config-example.json │ └── synch-config-example.json │ ├── helpers │ ├── add-blocks.js │ └── utils-test.js │ ├── proof-of-burn │ ├── interface-pob.test.js │ ├── loop-bids.test.js │ ├── loop-manager-pob.test.js │ └── synch-pob.test.js │ ├── proof-of-stake │ ├── interface-pos.test.js │ ├── loop-manager-pos.test.js │ └── synch-pos.test.js │ ├── server-proof.test.js │ ├── server │ ├── helpers │ │ └── utils-test.js │ ├── proof-of-burn │ │ ├── build-configs-levelDb-pob.test.js │ │ ├── build-configs-memDb-pob.test.js │ │ ├── create-config-env.sh │ │ ├── operator-server-pob.test.js │ │ └── test-pob.md │ └── proof-of-stake │ │ ├── build-configs-levelDb-pos.test.js │ │ ├── build-configs-memDb-pos.test.js │ │ ├── create-config-env.sh │ │ ├── operator-server-pos.test.js │ │ └── test-pos.md │ ├── synch-pool-service │ ├── api-bitfinex.test.js │ ├── build-configs-levelDb.test.js │ ├── build-configs-memDb.test.js │ ├── config │ │ ├── config.env-example │ │ └── create-config-env.sh │ ├── run-synch-pool.test.js │ ├── synch-pool-service.test.js │ └── test.md │ ├── synch-pool.test.js │ ├── synch-tokens.test.js │ ├── synch.test.js │ └── test.md ├── rollup-utils ├── babyjub-hd-keys.js ├── babyjub-wallet-utils.js ├── babyjub-wallet.js ├── eddsa-babyjub.js ├── index.js ├── level-db.js ├── mem-db.js ├── rollup-tree-utils.js ├── rollup-tree.js ├── rollup-utils.js ├── rollup-wallet-utils.js ├── rollup-wallet.js ├── smt-leveldb.js └── utils.js ├── simple-webapp ├── .eslintrc.js ├── .gitignore ├── package-lock.json ├── package.json ├── public │ ├── index.html │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── App.test.js │ ├── index.css │ ├── index.js │ ├── serviceWorker.js │ ├── state │ ├── general │ │ ├── actions.js │ │ ├── constants.js │ │ ├── index.js │ │ └── reducer.js │ ├── reducers.js │ └── tx │ │ ├── actions.js │ │ ├── constants.js │ │ ├── index.js │ │ └── reducer.js │ ├── store.js │ ├── utils │ ├── config-example.json │ ├── disable-eslint.sh │ └── utils.js │ └── views │ ├── action-view │ ├── components │ │ ├── constants.js │ │ ├── gmButtons.js │ │ ├── info-wallet.js │ │ ├── menu-actions.js │ │ ├── menu.js │ │ ├── message-tx.js │ │ ├── modal-deposit.js │ │ ├── modal-error.js │ │ ├── modal-info-id.js │ │ ├── modal-send.js │ │ └── modal-withdraw.js │ ├── containers │ │ └── action-view.js │ └── index.js │ └── init-view │ ├── components │ ├── modal-create.js │ └── modal-import.js │ ├── containers │ └── init-view.js │ └── index.js ├── test ├── circuits │ ├── balancesupdater.test.js │ ├── checkfees.test.js │ ├── circuits-test │ │ ├── balancesupdater_test.circom │ │ ├── checkfees_test.circom │ │ ├── decodefloat_test.circom │ │ ├── decodetx_test.circom │ │ ├── feeplandecoder_test.circom │ │ ├── feetableselector_test.circom │ │ ├── feeupdater_test.circom │ │ ├── input.json │ │ ├── requiredtxverifier_test.circom │ │ ├── rollup_pool_test.circom │ │ ├── rollup_test.circom │ │ ├── rolluptx_test.circom │ │ ├── rolluptxstates_test.circom │ │ ├── statepacker_test.circom │ │ └── unpackax_test.circom │ ├── compile-subcircuits.test.js │ ├── decode-tx.test.js │ ├── decodefloat.test.js │ ├── feeplandecoder.test.js │ ├── feetableselector.test.js │ ├── feeupdater.test.js │ ├── helpers │ │ ├── checkbatch.js │ │ └── utils-circuit.js │ ├── rollup-circuit.test.js │ ├── rollup-pool.test.js │ └── unpack-ax.test.js ├── contracts │ ├── Rollup-PoB-integration.test.js │ ├── Rollup-PoS-integration.test.js │ ├── Rollup.test.js │ ├── RollupBurnAuction.test.js │ ├── RollupHelpers.test.js │ ├── RollupPoB-functionalities.test.js │ ├── RollupPoS-functionalities.test.js │ ├── RollupPoS-tree.test.js │ ├── RollupPoSHelpers.test.js │ ├── TokenRollup.test.js │ ├── Verifier.test.js │ └── helpers │ │ ├── helpers.js │ │ └── timeTravel.js ├── js │ ├── batchbuilder.test.js │ ├── helpers │ │ └── utils-test.js │ ├── rollupDb.test.js │ ├── rollupaccount.test.js │ ├── txpool.test.js │ └── utils.test.js ├── performance │ └── batchbuilder-performance.test.js └── rollup-utils │ ├── babyjub-hd-keys.test.js │ ├── babyjub-wallet.test.js │ ├── level-db.test.js │ ├── rollup-account.test.js │ ├── rollup-tree-utils.test.js │ ├── rollup-tree.test.js │ ├── rollup-utils.test.js │ ├── rollup-wallet.test.js │ └── smt-leveldb.test.js ├── tools ├── calculate_iden3rollupmagic.js └── compute-fee-table.js └── truffle-config.js /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "mocha": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2017 9 | }, 10 | "extends": "eslint:recommended", 11 | "rules": { 12 | "indent": [ 13 | "error", 14 | 4 15 | ], 16 | "linebreak-style": [ 17 | "error", 18 | "unix" 19 | ], 20 | "quotes": [ 21 | "error", 22 | "double" 23 | ], 24 | "semi": [ 25 | "error", 26 | "always" 27 | ] 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.sol linguist-language=Solidity -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [10.15.3] 20 | 21 | steps: 22 | - name: Use Node.js ${{ matrix.node-version }} 23 | uses: actions/setup-node@v1 24 | with: 25 | node-version: ${{ matrix.node-version }} 26 | - name: Checkout code 27 | uses: actions/checkout@v2 28 | - run: npm ci 29 | - name: Test circuits 30 | run: npm run test:circuits 31 | - name: Test utils 32 | run: npm run test:utils 33 | - name: Test js 34 | run: npm run test:js 35 | - name: Test contracts 36 | run: npm run test:contracts 37 | - name: Test operator 38 | run: cd rollup-operator; npm ci; 39 | - name: Test operator_js 40 | run: cd rollup-operator; npm run test:js-pob 41 | - name: Test operator_pool 42 | run: cd rollup-operator; npm run test:pool 43 | - name: Test operator_server 44 | run: cd rollup-operator; npm run test:server-pob 45 | - name: Test cli 46 | run: cd rollup-cli; npm ci; 47 | - name: Test cli_utils 48 | run: cd rollup-cli; npm run test:utils 49 | - name: Test cli_actions 50 | run: cd rollup-cli; npm run test:actions 51 | - name: Test cli_integration 52 | run: cd rollup-cli; npm run test:integration 53 | - name: Test cli_pob 54 | run: cd cli-pob; npm ci 55 | - name: Test cli_pob_all 56 | run: cd cli-pob; npm run test:all -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Debug files 2 | test/circuits/circuit.json 3 | test.txt 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | coverage.json 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # Typescript v1 declaration files 45 | typings/ 46 | 47 | # Optional npm cache directory 48 | .npm 49 | 50 | # Optional eslint cache 51 | .eslintcache 52 | 53 | # Optional REPL history 54 | .node_repl_history 55 | 56 | # Output of 'npm pack' 57 | *.tgz 58 | 59 | # Yarn Integrity file 60 | .yarn-integrity 61 | 62 | # dotenv environment variables file 63 | *.env 64 | 65 | # next.js build output 66 | .next 67 | 68 | tmp 69 | 70 | # truffle 71 | build 72 | rollup-cli/src/wallet-rollup-cli.json 73 | 74 | # vscode configuration 75 | .vscode 76 | rollup-cli/tools/resourcesBot/ 77 | rollup-cli/tools/walletSlash.json 78 | rollup-cli/src/resources/ 79 | rollup-cli/nonceJson.json 80 | rollup-cli/tools/gunners/config/ 81 | 82 | synch-config.json 83 | pool-config.json 84 | wallet.json 85 | 86 | # circuits tmp 87 | test/circuit-example.json 88 | 89 | # docker databases 90 | docker/docker-operator/leveldb-* -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | client: require('ganache-cli'), 3 | providerOptions: { 4 | total_accounts: 100, 5 | default_balance_ether: 1000000, 6 | }, 7 | skipFiles: [ 8 | './contracts/Migrations.sol', 9 | './lib/Memory.sol', 10 | './lib/Poseidon.sol', 11 | './verifiers/Verifier_16_24.sol', 12 | './verifiers/Verifier_128_24.sol', 13 | './test/EventTest.sol', 14 | './test/TokenTest.sol', 15 | ] 16 | } -------------------------------------------------------------------------------- /circuits/balancesupdater.circom: -------------------------------------------------------------------------------- 1 | 2 | /* This circuit will check there is enough balance in the oldStAmount to transfer 3 | the amount and the fee. newAmount is the amount remaining into the state. 4 | effectiveAmount is the amount that actually is transfered. 5 | 6 | In an onChain Transaction, no errors are allowed, so in case it is not enough money, 7 | it will do a 0 transfer. 8 | 9 | In case of an offChain TX the system does not allow it. 10 | 11 | Overflow of < 2^192 is not feasible since deposits amounts can not be above 2^128 12 | */ 13 | 14 | include "../node_modules/circomlib/circuits/bitify.circom"; 15 | include "../node_modules/circomlib/circuits/comparators.circom"; 16 | include "./feetableselector.circom"; 17 | 18 | template BalancesUpdater() { 19 | signal input oldStAmountSender; 20 | signal input oldStAmountReceiver; 21 | signal input amount; 22 | signal input loadAmount; 23 | signal input fee; 24 | signal input onChain; 25 | signal input nop; 26 | 27 | signal output newStAmountSender; 28 | signal output newStAmountReceiver; 29 | signal output update2; 30 | signal output fee2Charge; // amount of fee that needs to be discounted 31 | 32 | signal feeApplies; // 1 If fee applies (offChain), 0 if not applies (onChain) 33 | 34 | signal limitsOk; // 1 if from is > 0 35 | signal txOk; // If both are ok. 36 | 37 | signal effectiveAmount1; 38 | signal effectiveAmount2; 39 | signal effectiveLoadAmount; 40 | 41 | component n2bSender = Num2Bits(193); 42 | component effectiveAmountIsZero = IsZero(); 43 | 44 | component n2bFee = Num2Bits(193 + 32); 45 | 46 | feeApplies <== (1-onChain)*(1-nop); // Fee applies only on offChainTx and is not a NOP 47 | 48 | component feeTableSelector = FeeTableSelector(); 49 | feeTableSelector.feeSel <== fee*feeApplies; 50 | 51 | n2bFee.in <== amount * feeTableSelector.feeOut; 52 | 53 | component b2nFee = Bits2Num(193); 54 | for (var i = 0; i < 193; i++) { 55 | b2nFee.in[i] <== n2bFee.out[i + 32]; 56 | } 57 | b2nFee.out ==> fee2Charge; 58 | 59 | effectiveLoadAmount <== loadAmount*onChain; 60 | effectiveAmount1 <== amount*(1-nop); 61 | 62 | // Check limits and fees on limits 63 | n2bSender.in <== (1<<192) + oldStAmountSender + effectiveLoadAmount - effectiveAmount1 - fee2Charge; 64 | 65 | limitsOk <== n2bSender.out[192]; 66 | 67 | txOk <== limitsOk; 68 | 69 | // if not onChain and not txOk => error 70 | (1-txOk)*(1-onChain) === 0; 71 | 72 | effectiveAmount2 <== txOk*effectiveAmount1; 73 | 74 | effectiveAmountIsZero.in <== effectiveAmount2; 75 | 76 | // if !txOk then return 0; 77 | newStAmountSender <== oldStAmountSender + effectiveLoadAmount - effectiveAmount2 - fee2Charge; 78 | newStAmountReceiver <== oldStAmountReceiver + effectiveAmount2 79 | 80 | update2 <== 1-effectiveAmountIsZero.out; 81 | } 82 | -------------------------------------------------------------------------------- /circuits/checkfees.circom: -------------------------------------------------------------------------------- 1 | include "../node_modules/circomlib/circuits/bitify.circom"; 2 | include "../node_modules/circomlib/circuits/gates.circom"; 3 | include "../node_modules/circomlib/circuits/comparators.circom"; 4 | include "decodefloat.circom"; 5 | 6 | template CheckFees() { 7 | signal input accFee[16]; 8 | signal input feeTotals; 9 | 10 | signal output feeTotalOk; 11 | 12 | signal feeTotal[16]; 13 | 14 | component n2bfeeTotal = Num2Bits(253); 15 | n2bfeeTotal.in <== feeTotals; 16 | 17 | component decodeFloat[16]; 18 | 19 | var i; 20 | var j; 21 | var nb; 22 | 23 | for (i = 0; i < 16; i++) { 24 | nb = i < 15 ? 16 : 13; 25 | decodeFloat[i] = DecodeFloatBin(); 26 | for (j = 0; j < 16; j++) { 27 | if (j < nb) { 28 | decodeFloat[i].in[j] <== n2bfeeTotal.out[i*16+j]; 29 | } else { 30 | decodeFloat[i].in[j] <== 0; 31 | } 32 | } 33 | feeTotal[i] <== decodeFloat[i].out; 34 | } 35 | 36 | component checkFeeLt[16]; 37 | for (i = 0; i < 16; i ++){ 38 | checkFeeLt[i] = LessEqThan(253); 39 | checkFeeLt[i].in[0] <== feeTotal[i]; 40 | checkFeeLt[i].in[1] <== accFee[i]; 41 | } 42 | 43 | component feesOk = MultiAND(16); 44 | for (i = 0; i < 16; i++){ 45 | feesOk.in[i] <== checkFeeLt[i].out; 46 | } 47 | 48 | feeTotalOk <== feesOk.out; 49 | } -------------------------------------------------------------------------------- /circuits/decodefloat.circom: -------------------------------------------------------------------------------- 1 | include "../node_modules/circomlib/circuits/bitify.circom"; 2 | include "../node_modules/circomlib/circuits/comparators.circom"; 3 | 4 | 5 | template DecodeFloatBin() { 6 | signal input in[16]; 7 | signal output out; 8 | 9 | signal m[10]; // Mantisa bits 10 | signal e[5]; // Exponent bits 11 | signal d; // Half digit bit 12 | 13 | signal pe[5]; // Intermediary steps for multiplying the exponents. 14 | signal allow5; // Allows have digit (exp >0) 15 | signal scale10; // 10^exp 16 | signal scale5; // scale10/2 17 | signal outAux; // Intermediary state for the output 18 | 19 | var i; 20 | var lcm; 21 | 22 | // Mapping 23 | d <== in[10] 24 | for (i=0; i<10; i++) m[i] <== in[i]; 25 | for (i=0; i<5; i++) e[i] <== in[i+11]; 26 | 27 | pe[0] <== 9*e[0]+1; 28 | var e10 = 100; 29 | for (i=1; i<5; i++) { 30 | pe[i] <== (pe[i-1] * (10**(2**i)) - pe[i-1]) * e[i] + pe[i-1]; 31 | e10 = e10*e10; 32 | } 33 | 34 | scale10 <== pe[4]; 35 | 36 | component isZero = IsZero(); 37 | 38 | isZero.in <== e[0] + e[1] + e[2] + e[3] + e[4] 39 | 40 | allow5 <== 1 - isZero.out; 41 | 42 | scale5 <-- scale10 \ 2; 43 | scale5*2 === scale10*allow5; 44 | 45 | lcm =0; 46 | var e2 = 1; 47 | for (i=0; i<10; i++) { 48 | lcm += e2*m[i]; 49 | e2 = e2 + e2; 50 | } 51 | 52 | outAux <== lcm*scale10; 53 | 54 | out <== outAux + d*scale5; 55 | } 56 | 57 | 58 | template DecodeFloat() { 59 | signal input in; 60 | signal output out; 61 | 62 | component n2b = Num2Bits(16); 63 | component decoder = DecodeFloatBin(); 64 | 65 | n2b.in <== in; 66 | 67 | for (var i=0; i<16; i++) { 68 | decoder.in[i] <== n2b.out[i]; 69 | } 70 | 71 | decoder.out ==> out; 72 | } 73 | -------------------------------------------------------------------------------- /circuits/feeplandecoder.circom: -------------------------------------------------------------------------------- 1 | include "../node_modules/circomlib/circuits/bitify.circom"; 2 | include "decodefloat.circom"; 3 | 4 | template FeePlanDecoder() { 5 | signal input feePlanCoins; 6 | signal output feePlanCoin[16]; 7 | 8 | var i; 9 | var j; 10 | var nb; 11 | 12 | component n2bCoins = Num2Bits(253); 13 | 14 | n2bCoins.in <== feePlanCoins; 15 | 16 | component b2nCoins[16]; 17 | for (i=0; i<16; i++) { 18 | nb = i<15 ? 16 : 13; 19 | b2nCoins[i] = Bits2Num(16); 20 | for (j=0; j<16; j++) { 21 | if (j accFeeOut; 28 | } 29 | 30 | template FeeUpdater(){ 31 | signal input coin; 32 | signal input fee2Charge; 33 | signal input feePlanCoin[16]; 34 | signal input accFeeIn[16]; 35 | 36 | signal output accFeeOut[16]; 37 | 38 | component chain[16]; 39 | 40 | var i; 41 | 42 | for (i = 0; i < 16; i++){ 43 | chain[i] = FeeUpdaterStep(); 44 | if (i == 0){ 45 | chain[i].isSelectedIn <== 0; 46 | } else { 47 | chain[i].isSelectedIn <== chain[i-1].isSelectedOut; 48 | } 49 | chain[i].coin <== coin; 50 | chain[i].fee2Charge <== fee2Charge; 51 | chain[i].feePlanCoin <== feePlanCoin[i]; 52 | chain[i].accFeeIn <== accFeeIn[i]; 53 | } 54 | 55 | for (i = 0; i < 16; i++ ){ 56 | accFeeOut[i] <== chain[i].accFeeOut; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /circuits/requiredtxverifier.circom: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | RqTxVerifier 4 | ============ 5 | 6 | 7 | ┌─────────────┐ 8 | rqOffset │ │ 9 | ───────────▶│ num2bin │ 10 | │ │ 11 | └──┬─┬─┬──────┘ 12 | │ │ │ 13 | ╲ │ │ │ 14 | TxData -1 ╲ │ │ │ 15 | ──────────▶ ╲▼ │ │ 16 | TxData -2 ╲ │ │ 17 | ──────────▶ ╲▼ │ 18 | TxData -3 ╲ │ 19 | ──────────▶ ╲▼ 20 | TxData -4 ╲ 21 | ──────────▶ ╲ 22 | TxData +3 Mux3 ──────────┐ 23 | ──────────▶ ╱ │ 24 | TxData +2 ╱ │ ┌────────┐ 25 | ──────────▶ ╱ │ │ │ 26 | TxData +1 ╱ └─────────▶│ │ 27 | ──────────▶ ╱ │ === │ 28 | 0 ╱ │ │ 29 | ──────────▶ ╱ ┌────────────────────▶│ │ 30 | ╱ │ └────────┘ 31 | ╱ │ 32 | │ 33 | rqTxData │ 34 | ────────────────────┘ 35 | 36 | */ 37 | 38 | include "../node_modules/circomlib/circuits/bitify.circom"; 39 | include "../node_modules/circomlib/circuits/mux3.circom"; 40 | 41 | template RequiredTxVerifier() { 42 | signal input pastTxData[4]; 43 | signal input futureTxData[3]; 44 | signal input rqTxData; 45 | signal input rqTxOffset; 46 | 47 | component mux = Mux3(); 48 | 49 | mux.c[0] <== 0; 50 | mux.c[1] <== futureTxData[0]; 51 | mux.c[2] <== futureTxData[1]; 52 | mux.c[3] <== futureTxData[2]; 53 | mux.c[4] <== pastTxData[3]; 54 | mux.c[5] <== pastTxData[2]; 55 | mux.c[6] <== pastTxData[1]; 56 | mux.c[7] <== pastTxData[0]; 57 | 58 | component n2b = Num2Bits(3); 59 | 60 | n2b.in <== rqTxOffset; 61 | n2b.out[0] ==> mux.s[0]; 62 | n2b.out[1] ==> mux.s[1]; 63 | n2b.out[2] ==> mux.s[2]; 64 | 65 | mux.out === rqTxData; 66 | } 67 | -------------------------------------------------------------------------------- /circuits/rolluptxstates.circom: -------------------------------------------------------------------------------- 1 | include "../node_modules/circomlib/circuits/comparators.circom"; 2 | include "../node_modules/circomlib/circuits/mux2.circom"; 3 | include "../node_modules/circomlib/circuits/poseidon.circom"; 4 | 5 | 6 | template RollupTXStates() { 7 | signal input fromIdx; 8 | signal input toIdx; 9 | signal input amount; 10 | signal input amount2; 11 | signal input loadAmount; 12 | signal input newAccount; 13 | signal input onChain; 14 | 15 | signal output s1; 16 | signal output s2; 17 | signal output key1; 18 | signal output key2; 19 | signal output P1_fnc0; 20 | signal output P1_fnc1; 21 | signal output P2_fnc0; 22 | signal output P2_fnc1; 23 | signal output isExit; 24 | signal output verifySignEnabled; 25 | signal output nop; 26 | 27 | component fromIdxIsZero = IsZero(); 28 | fromIdxIsZero.in <== fromIdx; 29 | signal isFromIdx; 30 | isFromIdx <== 1 - fromIdxIsZero.out; 31 | 32 | component toIdxIsZero = IsZero(); 33 | toIdxIsZero.in <== toIdx; 34 | isExit <== toIdxIsZero.out; 35 | 36 | component loadAmountIsZero = IsZero(); 37 | loadAmountIsZero.in <== loadAmount; 38 | signal isLoadAmount; 39 | isLoadAmount <== 1 - loadAmountIsZero.out; 40 | 41 | component amountIsZero = IsZero(); 42 | amountIsZero.in <== amount; 43 | signal isAmount; 44 | isAmount <== 1 - amountIsZero.out; 45 | 46 | component amount2IsZero = IsZero(); 47 | amount2IsZero.in <== amount2; 48 | signal isAmount2; 49 | isAmount2 <== 1 - amount2IsZero.out; 50 | 51 | // loadAmount MUST be 0 if it an offChain Tx 52 | (1-onChain)*isLoadAmount === 0; 53 | 54 | // newAccount MUST be 0 if it an offChain Tx 55 | (1-onChain)*newAccount === 0; 56 | 57 | s1 <== onChain*newAccount; // The first Processor is in insert 58 | 59 | s2 <== isExit*(1-isAmount2) // The second Processor is initialized 60 | 61 | P1_fnc0 <== s1*isFromIdx; 62 | P1_fnc1 <== (1-s1)*isFromIdx; 63 | component mux1 = Mux2(); 64 | mux1.c[0] <== 0; 65 | mux1.c[1] <== fromIdx; 66 | mux1.c[2] <== fromIdx; 67 | mux1.c[3] <== fromIdx; 68 | mux1.s[0] <== P1_fnc0; 69 | mux1.s[1] <== P1_fnc1; 70 | mux1.out ==> key1; 71 | 72 | P2_fnc0 <== s2*isFromIdx; 73 | P2_fnc1 <== (1-s2)*isFromIdx; 74 | component mux2 = Mux2(); 75 | mux2.c[0] <== 0; 76 | mux2.c[1] <== toIdx; 77 | mux2.c[2] <== 0; 78 | mux2.c[3] <== fromIdx; 79 | mux2.s[0] <== isAmount; 80 | mux2.s[1] <== isExit; 81 | mux2.out ==> key2; 82 | 83 | verifySignEnabled <== (1-onChain)*isFromIdx; 84 | 85 | nop <== fromIdxIsZero.out; 86 | 87 | } 88 | -------------------------------------------------------------------------------- /circuits/statepacker.circom: -------------------------------------------------------------------------------- 1 | include "../node_modules/circomlib/circuits/poseidon.circom"; 2 | 3 | template StatePacker() { 4 | 5 | signal input ax; 6 | signal input ay; 7 | signal input amount; 8 | signal input nonce; 9 | signal input coin; 10 | signal input ethAddr; 11 | 12 | signal output out; 13 | 14 | signal data; 15 | data <== coin + nonce * (1<<32); 16 | component hash = Poseidon(5, 6, 8, 57); 17 | 18 | hash.inputs[0] <== data; 19 | hash.inputs[1] <== amount; 20 | hash.inputs[2] <== ax; 21 | hash.inputs[3] <== ay; 22 | hash.inputs[4] <== ethAddr; 23 | 24 | hash.out ==> out; 25 | } 26 | -------------------------------------------------------------------------------- /circuits/unpackax.circom: -------------------------------------------------------------------------------- 1 | include "../node_modules/circomlib/circuits/bitify.circom"; 2 | include "../node_modules/circomlib/circuits/pointbits.circom"; 3 | 4 | template UnpackAx() { 5 | signal input Ax; 6 | signal output Ay; 7 | 8 | var tmpAx = Ax; 9 | var tmpAy = 0; 10 | 11 | var a = 168700; 12 | var d = 168696; 13 | 14 | var x2 = tmpAx*tmpAx; 15 | var y = sqrt((a*x2 - 1)/(d*x2 - 1)); 16 | 17 | var negy = -y; 18 | 19 | if (inSubgroup(tmpAx, y)){ 20 | tmpAy = y; 21 | } else if (inSubgroup(tmpAx, negy)){ 22 | tmpAy = negy; 23 | } 24 | 25 | Ay <-- tmpAy; 26 | } 27 | 28 | function inCurve(x, y) { 29 | var a = 168700; 30 | var d = 168696; 31 | 32 | var x2 = x*x; 33 | var y2 = y*y; 34 | 35 | var leftSide = a*x2 + y2; 36 | var rigthSide = 1 + d*x2*y2; 37 | 38 | var ret = 0; 39 | 40 | if (leftSide == rigthSide) { 41 | ret = 1; 42 | } 43 | return ret; 44 | } 45 | 46 | function addPoint(x1, y1, x2, y2) { 47 | var a = 168700; 48 | var d = 168696; 49 | 50 | var beta; 51 | var gamma; 52 | var delta; 53 | var tau; 54 | 55 | beta = x1*y2; 56 | gamma = y1*x2; 57 | delta = (-a*x1+y1)*(x2 + y2); 58 | tau = beta * gamma; 59 | 60 | var xout = (beta + gamma) / (1+ d*tau); 61 | var yout = (delta + a*beta - gamma) / (1-d*tau); 62 | 63 | return [xout, yout]; 64 | } 65 | 66 | function mulPointEscalar(base, e) { 67 | var res[2] = [0, 1]; 68 | var rem = e; 69 | var exp[2] = base; 70 | 71 | while (rem != 0) { 72 | if ((rem & 1) == 1) { 73 | res = addPoint(res[0], res[1], exp[0], exp[1]); 74 | } 75 | exp = addPoint(exp[0], exp[1], exp[0], exp[1]); 76 | rem = rem >> 1; 77 | } 78 | return res; 79 | } 80 | 81 | function inSubgroup(x, y) { 82 | var ret = 0; 83 | 84 | if (inCurve(x, y) == 0) { 85 | return ret; 86 | } 87 | 88 | var subOrder = 2736030358979909402780800718157159386076813972158567259200215660948447373041; 89 | 90 | var res[2] = mulPointEscalar([x, y], subOrder); 91 | 92 | if ((res[0] == 0) && (res[1] == 1)) { 93 | ret = 1; 94 | } 95 | return ret; 96 | } -------------------------------------------------------------------------------- /cli-pob/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "mocha" 4 | ], 5 | "env": { 6 | "node": true, 7 | "mocha": true 8 | }, 9 | "extends": "airbnb-base", 10 | "rules": { 11 | "indent": ["error", 4], 12 | "mocha/no-exclusive-tests": "error", 13 | "max-len": ["error", { "code": 140, "comments": 200, "ignoreStrings": true, "ignoreTemplateLiterals": true }], 14 | "no-unused-vars": [2, { "varsIgnorePattern": "export^" }], 15 | "no-return-assign": [0], 16 | "no-underscore-dangle": [0], 17 | "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], 18 | "func-names": [0], 19 | "class-methods-use-this": [0], 20 | "no-bitwise": [0], 21 | "no-param-reassign": "off", 22 | "no-console": [2, { "allow": ["warn", "error"] }], 23 | "import/prefer-default-export": [0], 24 | "lines-between-class-members": [ "error", "always", { "exceptAfterSingleLine": true }] 25 | } 26 | } -------------------------------------------------------------------------------- /cli-pob/.gitignore: -------------------------------------------------------------------------------- 1 | wallet-test.json 2 | config.json -------------------------------------------------------------------------------- /cli-pob/config-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodeUrl":"http://127.0.0.1:8545", 3 | "pobAddress":"0x0000000000000000000000000000000000000000", 4 | "pobAbi": ["abi object"] 5 | } -------------------------------------------------------------------------------- /cli-pob/index.js: -------------------------------------------------------------------------------- 1 | const utilsPoB = require('./src/utils'); 2 | 3 | module.exports = { 4 | utilsPoB, 5 | }; 6 | -------------------------------------------------------------------------------- /cli-pob/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli-pob", 3 | "version": "0.0.1", 4 | "description": "light client to interact with RollupPoB contract", 5 | "main": "index.js", 6 | "scripts": { 7 | "ganache": "npx ganache-cli -b 1 --mnemonic 'jaguar exhaust token lounge clerk gun metal vacant raven roast youth jealous' --defaultBalanceEther 100000 > ganache-output.log &", 8 | "test:all": "npm run ganache && npx truffle test test/helpers/build-configs.test.js && mocha test/bid.test.js && mocha test/multibid.test.js && mocha test/withdraw.test.js ; rm *output.log ; pkill -x 'node*'" 9 | }, 10 | "bin": { 11 | "rollup-cli-pob": "cli-pob.js" 12 | }, 13 | "keywords": [ 14 | "client", 15 | "rollup", 16 | "rollupPoB", 17 | "proof-of-burn" 18 | ], 19 | "license": "AGPL-3.0", 20 | "dependencies": { 21 | "ethers": "^4.0.47", 22 | "ffjavascript": "^0.1.3", 23 | "ganache-cli": "^6.9.1", 24 | "web3": "^1.2.5" 25 | }, 26 | "devDependencies": { 27 | "acorn": "^7.1.1", 28 | "chai": "^4.2.0", 29 | "circomlib": "^0.2.3", 30 | "eslint": "^5.16.0", 31 | "eslint-config-airbnb-base": "^14.1.0", 32 | "eslint-plugin-import": "^2.20.1", 33 | "eslint-plugin-mocha": "^6.3.0", 34 | "truffle": "^5.1.23" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /cli-pob/src/list-errors.js: -------------------------------------------------------------------------------- 1 | // Enum client errors 2 | const error = { 3 | ERROR: 0, 4 | NO_CONFIG_FILE: 1, 5 | INVALID_COMMAND: 2, 6 | INVALID_PATH: 3, 7 | NO_PARAM: 4, 8 | INVALID_WALLET: 5, 9 | }; 10 | 11 | module.exports = { error }; 12 | -------------------------------------------------------------------------------- /cli-pob/test/helpers/add-blocks.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-await-in-loop */ 2 | /* eslint-disable no-console */ 3 | /* global contract */ 4 | const { addBlocks } = require('../../../test/contracts/helpers/timeTravel'); 5 | const { timeout } = require('../../../rollup-utils/utils'); 6 | 7 | const slotsPerEra = 20; 8 | const blockPerSlots = 100; 9 | 10 | const erasToAdd = 4; 11 | 12 | const blocksToAdd = erasToAdd * slotsPerEra * blockPerSlots; 13 | 14 | contract('Add blocks', () => { 15 | it('Should add blocks', async () => { 16 | for (let i = 0; i < erasToAdd; i++) { 17 | await addBlocks(slotsPerEra * blockPerSlots); 18 | if (i !== erasToAdd - 1) await timeout(5000); 19 | console.log('Move forward 1 era'); 20 | } 21 | console.log(`Add ${blocksToAdd} blocks`); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /cli-pob/test/helpers/build-configs.test.js: -------------------------------------------------------------------------------- 1 | /* global artifacts */ 2 | /* global contract */ 3 | /* global web3 */ 4 | const poseidonUnit = require('circomlib/src/poseidon_gencontract'); 5 | const path = require('path'); 6 | 7 | const Verifier = artifacts.require('../contracts/test/VerifierHelper'); 8 | const RollupPoB = artifacts.require('../contracts/RollupPoB'); 9 | const Rollup = artifacts.require('../contracts/test/Rollup'); 10 | const fs = require('fs'); 11 | const ethers = require('ethers'); 12 | 13 | const mnemonic = 'jaguar exhaust token lounge clerk gun metal vacant raven roast youth jealous'; 14 | const passString = 'foo'; 15 | 16 | 17 | const walletPath = path.join(__dirname, '../../wallet-test.json'); 18 | const configPath = path.join(__dirname, '../../config.json'); 19 | 20 | contract('Build configuration files for cli-pob', (accounts) => { 21 | const { 22 | 0: owner, 23 | 1: feeTokenAddress, 24 | 2: defaultOperator, 25 | } = accounts; 26 | 27 | const maxTx = 10; 28 | const maxOnChainTx = 5; 29 | const burnAddress = '0x0000000000000000000000000000000000000000'; 30 | const url = 'localhost'; 31 | 32 | let insPoseidonUnit; 33 | let insRollupPoB; 34 | let insRollup; 35 | let insVerifier; 36 | 37 | before(async () => { 38 | // Deploy poseidon 39 | const C = new web3.eth.Contract(poseidonUnit.abi); 40 | insPoseidonUnit = await C.deploy({ data: poseidonUnit.createCode() }) 41 | .send({ gas: 2500000, from: owner }); 42 | 43 | // Deploy Verifier 44 | insVerifier = await Verifier.new(); 45 | 46 | // Deploy Rollup test 47 | insRollup = await Rollup.new(insVerifier.address, insPoseidonUnit._address, 48 | maxTx, maxOnChainTx, feeTokenAddress); 49 | 50 | // Deploy Staker manager 51 | insRollupPoB = await RollupPoB.new(insRollup.address, maxTx, burnAddress, defaultOperator, url); 52 | 53 | // load forge batch mechanism 54 | await insRollup.loadForgeBatchMechanism(insRollupPoB.address); 55 | }); 56 | 57 | it('Should save wallet', async () => { 58 | const wallet = await ethers.Wallet.fromMnemonic(mnemonic); 59 | const encWallet = await wallet.encrypt(passString); 60 | fs.writeFileSync(walletPath, JSON.stringify(JSON.parse(encWallet), null, 1), 'utf-8'); 61 | }); 62 | 63 | it('Should load configuration file', async () => { 64 | const nodeUrl = 'http://127.0.0.1:8545'; 65 | const pobAddress = insRollupPoB.address; 66 | const pobAbi = RollupPoB.abi; 67 | const config = { nodeUrl, pobAddress, pobAbi }; 68 | fs.writeFileSync(configPath, JSON.stringify(config), 'utf-8'); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /cli-pob/test/helpers/info.md: -------------------------------------------------------------------------------- 1 | # Run CLI-PoB test 2 | Commands are called from repository `rollup/cli-pob` directory 3 | 4 | - Start local ethereum blockchain 5 | `ganache-cli -b 1 --mnemonic "jaguar exhaust token lounge clerk gun metal vacant raven roast youth jealous" --defaultBalanceEther 100000` 6 | 7 | - Run build-configs 8 | `npx truffle test test/helpers/build-configs.test.js` 9 | 10 | - Run Test Bid 11 | `npx mocha test/bid.test.js` 12 | 13 | - Run Test Multibid 14 | `npx mocha test/multibid.test.js` 15 | 16 | - Run Test Withdraw 17 | `npx mocha test/withdraw.test.js` -------------------------------------------------------------------------------- /cli-pos/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "mocha" 4 | ], 5 | "env": { 6 | "node": true, 7 | "mocha": true 8 | }, 9 | "extends": "airbnb-base", 10 | "rules": { 11 | "indent": ["error", 4], 12 | "mocha/no-exclusive-tests": "error", 13 | "max-len": ["error", { "code": 140, "comments": 200, "ignoreStrings": true, "ignoreTemplateLiterals": true }], 14 | "no-unused-vars": [2, { "varsIgnorePattern": "export^" }], 15 | "no-return-assign": [0], 16 | "no-underscore-dangle": [0], 17 | "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], 18 | "func-names": [0], 19 | "class-methods-use-this": [0], 20 | "no-bitwise": [0], 21 | "no-param-reassign": "off", 22 | "no-console": [2, { "allow": ["warn", "error"] }], 23 | "import/prefer-default-export": [0], 24 | "lines-between-class-members": [ "error", "always", { "exceptAfterSingleLine": true }] 25 | } 26 | } -------------------------------------------------------------------------------- /cli-pos/.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /tmp 4 | /out-tsc 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | *.pid.lock 11 | 12 | # Directory for instrumented libs generated by jscoverage/JSCover 13 | lib-cov 14 | 15 | # Coverage directory used by tools like istanbul 16 | coverage 17 | 18 | # nyc test coverage 19 | .nyc_output 20 | 21 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 22 | .grunt 23 | 24 | # Bower dependency directory (https://bower.io/) 25 | bower_components 26 | 27 | # node-waf configuration 28 | .lock-wscript 29 | 30 | # IDEs and editors 31 | .idea 32 | .project 33 | .classpath 34 | .c9/ 35 | *.launch 36 | .settings/ 37 | *.sublime-workspace 38 | 39 | # IDE - VSCode 40 | .vscode/* 41 | !.vscode/settings.json 42 | !.vscode/tasks.json 43 | !.vscode/launch.json 44 | !.vscode/extensions.json 45 | 46 | # misc 47 | .sass-cache 48 | connect.lock 49 | typings 50 | 51 | # Logs 52 | logs 53 | *.log 54 | npm-debug.log* 55 | yarn-debug.log* 56 | yarn-error.log* 57 | 58 | 59 | # Dependency directories 60 | node_modules/ 61 | jspm_packages/ 62 | 63 | # Optional npm cache directory 64 | .npm 65 | 66 | # Optional eslint cache 67 | .eslintcache 68 | 69 | # Optional REPL history 70 | .node_repl_history 71 | 72 | # Output of 'npm pack' 73 | *.tgz 74 | 75 | # Yarn Integrity file 76 | .yarn-integrity 77 | 78 | # dotenv environment variables file 79 | .env 80 | 81 | # next.js build output 82 | .next 83 | 84 | # Lerna 85 | lerna-debug.log 86 | 87 | # System Files 88 | .DS_Store 89 | Thumbs.db 90 | 91 | # Config files 92 | config.json 93 | 94 | # Test files 95 | wallet-test.json -------------------------------------------------------------------------------- /cli-pos/config-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "nodeUrl":"http://127.0.0.1:8545", 3 | "posAddress":"0x0000000000000000000000000000000000000000", 4 | "posAbi": ["abi object"] 5 | } -------------------------------------------------------------------------------- /cli-pos/index.js: -------------------------------------------------------------------------------- 1 | const utilsPoS = require('./src/utils'); 2 | 3 | module.exports = { 4 | utilsPoS, 5 | }; 6 | -------------------------------------------------------------------------------- /cli-pos/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cli-pos", 3 | "version": "0.0.1", 4 | "description": "light client to interact with RollupPoS contract", 5 | "main": "index.js", 6 | "bin": { 7 | "rollup-cli-pos": "cli-pos.js" 8 | }, 9 | "scripts": { 10 | "ganache": "npx ganache-cli -b 1 --mnemonic 'jaguar exhaust token lounge clerk gun metal vacant raven roast youth jealous' --defaultBalanceEther 100000 > ganache-output.log &", 11 | "test:integation": "npm run ganache && npx truffle test test/helpers/build-configs.test.js && npx mocha ./test/*register.test.js && npx truffle test test/helpers/add-blocks.js && npx mocha test/withdraw.test.js; rm './ganache-output.log' ; pkill -f 'ganache-cli'" 12 | }, 13 | "keywords": [ 14 | "client", 15 | "rollup", 16 | "rollupPoS", 17 | "proof-of-stake" 18 | ], 19 | "license": "AGPL-3.0", 20 | "dependencies": { 21 | "ethers": "^4.0.47", 22 | "ffjavascript": "^0.1.3", 23 | "web3": "^1.2.5" 24 | }, 25 | "devDependencies": { 26 | "acorn": "^7.1.1", 27 | "chai": "^4.2.0", 28 | "circomlib": "^0.2.3", 29 | "eslint": "^5.16.0", 30 | "eslint-config-airbnb-base": "^14.1.0", 31 | "eslint-plugin-import": "^2.20.1", 32 | "eslint-plugin-mocha": "^6.3.0", 33 | "truffle": "^5.1.23" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cli-pos/src/list-errors.js: -------------------------------------------------------------------------------- 1 | // Enum client errors 2 | const error = { 3 | ERROR: 0, 4 | NO_CONFIG_FILE: 1, 5 | INVALID_COMMAND: 2, 6 | INVALID_PATH: 3, 7 | NO_PARAM: 4, 8 | INVALID_WALLET: 5, 9 | }; 10 | 11 | module.exports = { error }; 12 | -------------------------------------------------------------------------------- /cli-pos/test/helpers/add-blocks.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-await-in-loop */ 2 | /* eslint-disable no-console */ 3 | /* global contract */ 4 | const { addBlocks } = require('../../../test/contracts/helpers/timeTravel'); 5 | const { timeout } = require('../../../rollup-utils/utils'); 6 | 7 | const slotsPerEra = 20; 8 | const blockPerSlots = 100; 9 | 10 | const erasToAdd = 4; 11 | 12 | const blocksToAdd = erasToAdd * slotsPerEra * blockPerSlots; 13 | 14 | contract('Add blocks', () => { 15 | it('Should add blocks', async () => { 16 | for (let i = 0; i < erasToAdd; i++) { 17 | await addBlocks(slotsPerEra * blockPerSlots); 18 | if (i !== erasToAdd - 1) await timeout(5000); 19 | console.log('Move forward 1 era'); 20 | } 21 | console.log(`Add ${blocksToAdd} blocks`); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /cli-pos/test/helpers/build-configs.test.js: -------------------------------------------------------------------------------- 1 | /* global artifacts */ 2 | /* global contract */ 3 | /* global web3 */ 4 | const poseidonUnit = require('circomlib/src/poseidon_gencontract'); 5 | const path = require('path'); 6 | 7 | const TokenRollup = artifacts.require('../contracts/test/TokenRollup'); 8 | const Verifier = artifacts.require('../contracts/test/VerifierHelper'); 9 | const RollupPoS = artifacts.require('../contracts/RollupPoS'); 10 | const Rollup = artifacts.require('../contracts/test/Rollup'); 11 | const fs = require('fs'); 12 | const ethers = require('ethers'); 13 | 14 | const mnemonic = 'jaguar exhaust token lounge clerk gun metal vacant raven roast youth jealous'; 15 | const passString = 'foo'; 16 | 17 | const walletPath = path.join(__dirname, '../wallet-test.json'); 18 | const configPath = path.join(__dirname, '../config.json'); 19 | 20 | contract('Build configuration files for cli-pos', (accounts) => { 21 | const { 22 | 0: owner, 23 | 1: tokenId, 24 | 2: feeTokenAddress, 25 | } = accounts; 26 | 27 | const maxTx = 10; 28 | const maxOnChainTx = 5; 29 | const tokenInitialAmount = 1000; 30 | 31 | let insPoseidonUnit; 32 | let insTokenRollup; 33 | let insRollupPoS; 34 | let insRollup; 35 | let insVerifier; 36 | 37 | before(async () => { 38 | // Deploy poseidon 39 | const C = new web3.eth.Contract(poseidonUnit.abi); 40 | insPoseidonUnit = await C.deploy({ data: poseidonUnit.createCode() }) 41 | .send({ gas: 2500000, from: owner }); 42 | 43 | // Deploy TokenRollup 44 | insTokenRollup = await TokenRollup.new(tokenId, tokenInitialAmount); 45 | 46 | // Deploy Verifier 47 | insVerifier = await Verifier.new(); 48 | 49 | // Deploy Rollup test 50 | insRollup = await Rollup.new(insVerifier.address, insPoseidonUnit._address, 51 | maxTx, maxOnChainTx, feeTokenAddress); 52 | 53 | // Deploy Staker manager 54 | insRollupPoS = await RollupPoS.new(insRollup.address, maxTx); 55 | 56 | // load forge batch mechanism 57 | await insRollup.loadForgeBatchMechanism(insRollupPoS.address); 58 | 59 | // add token to Rollup 60 | await insRollup.addToken(insTokenRollup.address, 61 | { from: tokenId, value: web3.utils.toWei('1', 'ether') }); 62 | }); 63 | 64 | it('Should save wallet', async () => { 65 | const wallet = await ethers.Wallet.fromMnemonic(mnemonic); 66 | const encWallet = await wallet.encrypt(passString); 67 | fs.writeFileSync(walletPath, JSON.stringify(JSON.parse(encWallet), null, 1), 'utf-8'); 68 | }); 69 | 70 | it('Should load configuration file', async () => { 71 | const nodeUrl = 'http://127.0.0.1:8545'; 72 | const posAddress = insRollupPoS.address; 73 | const posAbi = RollupPoS.abi; 74 | const config = { nodeUrl, posAddress, posAbi }; 75 | fs.writeFileSync(configPath, JSON.stringify(config), 'utf-8'); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /cli-pos/test/helpers/info.md: -------------------------------------------------------------------------------- 1 | # Run CLI-PoS test 2 | Commands are called from repository `rollup/cli-pos/test` directory 3 | 4 | - Start local ethereum blockchain 5 | `ganache-cli -b 1 --mnemonic "jaguar exhaust token lounge clerk gun metal vacant raven roast youth jealous" --defaultBalanceEther 100000` 6 | 7 | - Run build-configs 8 | `truffle test helpers/build-configs.test.js` 9 | 10 | - Run Test Register 11 | `mocha register.test.js` 12 | 13 | - Run Test Unregister 14 | `mocha unregister.test.js` 15 | 16 | - Move some eras 17 | `truffle test helpers/add-blocks.js` 18 | 19 | - Run Test Withdraw 20 | `mocha withdraw.test.js` -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.7.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/RollupInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.1; 2 | 3 | /** 4 | * @dev Define interface Rollup smart contract 5 | */ 6 | interface RollupInterface { 7 | function forgeBatch( 8 | uint[2] calldata proofA, 9 | uint[2][2] calldata proofB, 10 | uint[2] calldata proofC, 11 | uint[10] calldata input, 12 | bytes calldata compressedOnChainTx 13 | ) external payable; 14 | } -------------------------------------------------------------------------------- /contracts/VerifierInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.1; 2 | 3 | /** 4 | * @dev Define interface verifier 5 | */ 6 | interface VerifierInterface { 7 | function verifyProof( 8 | uint[2] calldata proofA, 9 | uint[2][2] calldata proofB, 10 | uint[2] calldata proofC, 11 | uint[10] calldata input 12 | ) external view returns (bool); 13 | } -------------------------------------------------------------------------------- /contracts/lib/Poseidon.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.1; 2 | 3 | contract PoseidonUnit { 4 | function poseidon(uint256[] memory) public pure returns(uint256) {} 5 | } 6 | 7 | contract Poseidon { 8 | PoseidonUnit poseidonUnit; 9 | 10 | constructor( address _poseidonContractAddr) public { 11 | poseidonUnit = PoseidonUnit(_poseidonContractAddr); 12 | } 13 | 14 | function hash(uint256[] memory inputs) public view returns(uint256) { 15 | return poseidonUnit.poseidon(inputs); 16 | } 17 | } -------------------------------------------------------------------------------- /contracts/lib/RollupPoBHelpers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.1; 2 | 3 | import './Memory.sol'; 4 | 5 | /** 6 | * @dev RollupPoS helper functions 7 | */ 8 | contract RollupPoBHelpers { 9 | 10 | using Memory for *; 11 | 12 | uint constant bitsTx = 24 + 24 + 16 + 4; 13 | uint constant rField = 21888242871839275222246405745257275088548364400416034343698204186575808495617; 14 | 15 | constructor () public {} 16 | 17 | /** 18 | * @dev hash all off-chain transactions 19 | * @param offChainTx off-chain transaction compressed format 20 | * @param maxTx Maxtransactions that fits in one batch 21 | * @return hash of all off-chain transactions 22 | */ 23 | function hashOffChainTx(bytes memory offChainTx, uint256 maxTx) internal pure returns (uint256) { 24 | uint256 totalLength = maxTx*bitsTx; 25 | 26 | if (maxTx % 2 != 0) { 27 | totalLength += 4; 28 | } 29 | 30 | bytes memory hashOffTx = new bytes(totalLength/8); 31 | Memory.Cursor memory c = Memory.read(offChainTx); 32 | uint256 ptr = totalLength/8 - offChainTx.length; 33 | 34 | while (!c.eof()) { 35 | // add off-chain transactions at the end 36 | bytes1 iTx = c.readBytes1(); 37 | hashOffTx[ptr] = iTx; 38 | ptr++; 39 | } 40 | return uint256(sha256(hashOffTx)) % rField; 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /contracts/lib/RollupPoSHelpers.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.1; 2 | 3 | import './Memory.sol'; 4 | 5 | /** 6 | * @dev RollupPoS helper functions 7 | */ 8 | contract RollupPoSHelpers { 9 | 10 | using Memory for *; 11 | 12 | uint constant bitsTx = 24 + 24 + 16 + 4; 13 | uint constant rField = 21888242871839275222246405745257275088548364400416034343698204186575808495617; 14 | uint constant FOURTH_ROOT_FINNEY = 5623; // 4th root of finney in weis 15 | 16 | constructor () public {} 17 | 18 | // function getHeaderLen(uint bytesLen) internal pure returns (uint){ 19 | // uint counter = 0; 20 | // uint tmpBytesLen = bytesLen; 21 | // tmpBytesLen = tmpBytesLen - 1; 22 | // if (tmpBytesLen > 8*bytesOffChainTx) 23 | // counter = getHeaderLen(tmpBytesLen - 8*bytesOffChainTx); 24 | // return (counter + 1); 25 | // } 26 | 27 | /** 28 | * @dev hash all off-chain transactions 29 | * @param offChainTx off-chain transaction compressed format 30 | * @param maxTx Maxtransactions that fits in one batch 31 | * @return hash of all off-chain transactions 32 | */ 33 | function hashOffChainTx(bytes memory offChainTx, uint256 maxTx) internal pure returns (uint256) { 34 | uint256 totalLength = maxTx*bitsTx; 35 | 36 | if (maxTx % 2 != 0) { 37 | totalLength += 4; 38 | } 39 | 40 | bytes memory hashOffTx = new bytes(totalLength/8); 41 | Memory.Cursor memory c = Memory.read(offChainTx); 42 | uint256 ptr = totalLength/8 - offChainTx.length; 43 | 44 | while (!c.eof()) { 45 | // add off-chain transactions at the end 46 | bytes1 iTx = c.readBytes1(); 47 | hashOffTx[ptr] = iTx; 48 | ptr++; 49 | } 50 | return uint256(sha256(hashOffTx)) % rField; 51 | } 52 | 53 | /** 54 | * @dev Calculate the effective stake, which is: stake^1.25, it can also be noted as stake*stake^1/4 55 | * @param stake number to get the exponentiation 56 | * @return stake^1.25 57 | */ 58 | function effectiveStake(uint stake) internal pure returns (uint64) { 59 | return uint64((stake*sqrt(sqrt(stake)))/(1 finney * FOURTH_ROOT_FINNEY)); 60 | } 61 | 62 | /** 63 | * @dev perform the babylonian method to calculate in a simple and efficient way the square root 64 | * @param x number to calculate the square root 65 | * @return y square root of x 66 | */ 67 | function sqrt(uint x) internal pure returns (uint y) { 68 | uint z = (x + 1) / 2; 69 | y = x; 70 | while (z < y) { 71 | y = z; 72 | z = (x / z + z) / 2; 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /contracts/test/EventTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.1; 2 | 3 | contract EventTest { 4 | 5 | event Deposit(uint batchNumber ,uint idBalanceTree, uint depositAmount, uint tokenId, uint Ax, uint Ay, 6 | address withdrawAddress ); 7 | 8 | event ForgeBatch(uint batchNumber, bytes offChainTx); 9 | 10 | bytes32[] stateRoots; 11 | uint24 lastBalanceTreeIndex = 1; 12 | 13 | function deposit( 14 | uint16 depositAmount, 15 | uint16 tokenId, 16 | uint256[2] memory babyPubKey, 17 | address withdrawAddress 18 | ) public { 19 | 20 | uint currentBatch = getStateDepth() - 1; 21 | 22 | emit Deposit(currentBatch, lastBalanceTreeIndex, depositAmount, tokenId, babyPubKey[0], babyPubKey[1], 23 | withdrawAddress); 24 | lastBalanceTreeIndex++; 25 | } 26 | 27 | function forgeBatch( 28 | uint newRoot, 29 | bytes memory compressedTxs 30 | ) public { 31 | 32 | stateRoots.push(bytes32(newRoot)); 33 | emit ForgeBatch(getStateDepth(), compressedTxs); 34 | } 35 | 36 | function getStateRoot(uint numBatch) public view returns (bytes32) { 37 | require(numBatch <= stateRoots.length - 1, 'Batch number does not exist'); 38 | return stateRoots[numBatch]; 39 | } 40 | 41 | function getStateDepth() public view returns (uint) { 42 | return stateRoots.length; 43 | } 44 | } -------------------------------------------------------------------------------- /contracts/test/RollupBurnAuctionTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.1; 2 | 3 | import "../RollupBurnAuction.sol"; 4 | 5 | contract RollupBurnAuctionTest is RollupBurnAuction { 6 | 7 | constructor(address _rollup) RollupBurnAuction(_rollup) public {} 8 | 9 | uint public blockNumber; 10 | 11 | function getBlockNumber() public view override returns (uint) { 12 | return blockNumber; 13 | } 14 | 15 | function setBlockNumber(uint bn) public { 16 | blockNumber = bn; 17 | } 18 | 19 | function forgeBatch( 20 | address payable beneficiaryAddress, 21 | uint[2] calldata proofA, 22 | uint[2][2] calldata proofB, 23 | uint[2] calldata proofC, 24 | uint[10] calldata input, 25 | bytes calldata compressedOnChainTx 26 | ) external override { 27 | require(auction[currentSlot()].initialized, "Auction has not been initialized"); 28 | require(msg.sender == auction[currentSlot()].operator, "Sender is not current winner"); 29 | } 30 | } -------------------------------------------------------------------------------- /contracts/test/RollupPoSHelpersTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.1; 2 | 3 | import "../lib/RollupPoSHelpers.sol"; 4 | 5 | contract RollupPoSHelpersTest is RollupPoSHelpers{ 6 | 7 | constructor() public {} 8 | 9 | function hashOffChainTxTest(bytes memory compressedTxs, uint256 maxTx) public pure returns (uint256) { 10 | return hashOffChainTx(compressedTxs, maxTx); 11 | } 12 | 13 | function effectiveStakeTest(uint stake) public pure returns (uint64) { 14 | return effectiveStake(stake); 15 | } 16 | 17 | // function testGetHeaderLen(uint bytesLen) public pure returns(uint256) { 18 | // return getHeaderLen(bytesLen); 19 | // } 20 | } -------------------------------------------------------------------------------- /contracts/test/TokenRollup.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.1; 2 | 3 | import '../../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol'; 4 | 5 | contract TokenRollup is ERC20{ 6 | 7 | constructor(address initialAccount, uint256 initialBalance) public { 8 | require(initialBalance > 0, "initial balance has to be greater than 0"); 9 | _mint(initialAccount, initialBalance); 10 | } 11 | } -------------------------------------------------------------------------------- /contracts/test/TokenTest.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.1; 2 | 3 | import '../../node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol'; 4 | import '../../node_modules/@openzeppelin/contracts/token/ERC20/ERC20Detailed.sol'; 5 | 6 | contract TokenTest is ERC20, ERC20Detailed { 7 | 8 | constructor( 9 | address initialAccount, 10 | uint256 initialBalance, 11 | string memory name, 12 | string memory symbol, 13 | uint8 decimals) 14 | public ERC20Detailed(name, symbol, decimals) { 15 | require(initialBalance > 0, "initial balance has to be greater than 0"); 16 | _mint(initialAccount, initialBalance); 17 | } 18 | } -------------------------------------------------------------------------------- /contracts/test/VerifierHelper.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.6.1; 2 | 3 | import '../VerifierInterface.sol'; 4 | 5 | contract VerifierHelper { 6 | function verifyProof( 7 | uint[2] memory a, 8 | uint[2][2] memory b, 9 | uint[2] memory c, 10 | uint[10] memory input 11 | ) public view returns (bool) { 12 | return true; 13 | } 14 | } -------------------------------------------------------------------------------- /doc/feeSelector.monopic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iden3/rollup/1048dfe09a9042362ee6c36eba4951bf63b3e062/doc/feeSelector.monopic -------------------------------------------------------------------------------- /doc/repo-organization.md: -------------------------------------------------------------------------------- 1 | # Rollup repository overview 2 | 3 | ## Core 4 | ### circuits 5 | `./circuits`: contains all rollup circuits 6 | 7 | ### contracts 8 | `./contracts`: all rollup smart contracts 9 | - `./Rollup.sol`: manages the rollup itself 10 | - `./RollupPoS.sol`: implements proof-of-stake as a forge batch mechanism 11 | - `./RollupPoB.sol`: implements proof-of-burn as a forge batch mechanism 12 | - `./lib`: helper contracts ans functionalities 13 | - `./test`: contracts for testing purposes 14 | - `./verifiers`: verifier rollup circuit 15 | 16 | ## Tooling 17 | 18 | ### cli-pob 19 | `./cli-pob`: client to interact with proof-of-burn mechanism 20 | 21 | ### cli-pos 22 | `./cli-pos`: client to interact with proof-of-stake mechanism 23 | 24 | ### docker 25 | `./docker`: 26 | 27 | ### js 28 | `./js`: core rollup pieces implementation 29 | 30 | ### rollup-cli 31 | `./rollup-cli`: command line client to interact with rollup 32 | 33 | ### rollup-operator 34 | `./rollup-operator`: rollup node to synchronize and manage forging batches 35 | 36 | ### rollup-utils 37 | `./rollup-utils`: rollup utils implementation 38 | 39 | ### tools 40 | `./tools`: scripts helpers 41 | 42 | ## Documentation 43 | `./doc`: gather rollup documentation 44 | 45 | ## Test 46 | `.test`: 47 | - `./circuit`: test circuits 48 | - `./contracts`: test contracts 49 | - `./js`: test core rollup implementation 50 | - `./performance`: scripts to get performance data 51 | - `./rollup-utils`: test utils code implementation -------------------------------------------------------------------------------- /doc/rollup_tree.monopic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iden3/rollup/1048dfe09a9042362ee6c36eba4951bf63b3e062/doc/rollup_tree.monopic -------------------------------------------------------------------------------- /doc/rollup_tx.monopic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iden3/rollup/1048dfe09a9042362ee6c36eba4951bf63b3e062/doc/rollup_tx.monopic -------------------------------------------------------------------------------- /doc/rqtxverifier.monopic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iden3/rollup/1048dfe09a9042362ee6c36eba4951bf63b3e062/doc/rqtxverifier.monopic -------------------------------------------------------------------------------- /docker/copy-config.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mkdir -p docker-webapp/config-webapp 3 | mkdir -p docker-operator/operator/config-operator 4 | mkdir -p docker-cli-pos/config-cli-pos 5 | cp -R docker-contracts/config-contracts/*.json docker-webapp/config-webapp 6 | cp -R docker-contracts/config-contracts/*.json docker-operator/operator/config-operator 7 | cp -R docker-contracts/config-contracts/*.json docker-cli-pos/config-cli-pos 8 | cp -R docker-contracts/config-contracts/*.json docker-cli-pob/config-cli-pob 9 | cp docker-contracts/config-contracts/synch-config.json docker-operator/synch-pool/config-pool -------------------------------------------------------------------------------- /docker/docker-cli-pob/.env.example: -------------------------------------------------------------------------------- 1 | # Global settings 2 | BRANCH=master -------------------------------------------------------------------------------- /docker/docker-cli-pob/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | ARG branch=master 3 | RUN git clone https://github.com/iden3/rollup.git 4 | WORKDIR /rollup 5 | RUN git checkout $branch 6 | RUN npm install 7 | COPY config-cli-pob/* ./ 8 | RUN node build-config.js 9 | RUN mv config.json ./cli-pob 10 | RUN mv wallet.json ./cli-pob 11 | WORKDIR "./cli-pob" 12 | 13 | CMD ["sh", "-c", ""] -------------------------------------------------------------------------------- /docker/docker-cli-pob/config-cli-pob/build-config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | const configSynchPath = path.join(__dirname, "./synch-config.json"); 4 | const configPath = path.join(__dirname, "./config.json"); 5 | 6 | const configSynch = JSON.parse(fs.readFileSync(configSynchPath, "utf-8")); 7 | const config = { 8 | nodeUrl: configSynch.ethNodeUrl, 9 | pobAddress: configSynch.rollupPoB.address, 10 | pobAbi: configSynch.rollupPoB.abi 11 | }; 12 | 13 | fs.writeFileSync(configPath, JSON.stringify(config)); -------------------------------------------------------------------------------- /docker/docker-cli-pob/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | cli-pob: 4 | container_name: cli-pob 5 | network_mode: "host" 6 | build: 7 | context: . 8 | args: 9 | - branch=${BRANCH} 10 | env_file: 11 | - ./.env 12 | stdin_open: true 13 | tty: true -------------------------------------------------------------------------------- /docker/docker-cli-pos/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | RUN git clone https://github.com/iden3/rollup.git 3 | WORKDIR "./rollup" 4 | RUN npm install 5 | COPY config-cli-pos/* ./ 6 | RUN node build-config.js 7 | RUN mv config.json ./cli-pos 8 | RUN mv wallet.json ./cli-pos 9 | WORKDIR "./cli-pos" 10 | 11 | CMD ["sh", "-c", ""] -------------------------------------------------------------------------------- /docker/docker-cli-pos/config-cli-pos/build-config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | const configSynchPath = path.join(__dirname, "./synch-config.json"); 4 | const configPath = path.join(__dirname, "./config.json"); 5 | 6 | const configSynch = JSON.parse(fs.readFileSync(configSynchPath, "utf-8")); 7 | const config = { 8 | nodeUrl: configSynch.ethNodeUrl, 9 | posAddress: configSynch.rollupPoS.address, 10 | posAbi: configSynch.rollupPoS.abi 11 | }; 12 | 13 | fs.writeFileSync(configPath, JSON.stringify(config)); -------------------------------------------------------------------------------- /docker/docker-cli-pos/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | cli-pos: 4 | container_name: cli-pos 5 | network_mode: "host" 6 | build: . 7 | stdin_open: true 8 | tty: true -------------------------------------------------------------------------------- /docker/docker-contracts/.env.example: -------------------------------------------------------------------------------- 1 | # Global settings 2 | ETH_NODE_URL=http://url.io 3 | MNEMONIC=your mnemonic here 4 | INDEX_ACCOUNT=0 5 | PASSWORD=password 6 | BRANCH=master 7 | GAS_MULTIPLIER=1 8 | 9 | # ROLLUP SETTINGS 10 | MAX_TX=128 11 | MAX_ONCHAIN_TX=64 12 | ROLLUP_TOKEN_FEE_ADDRESS=0x4c94eA1Ba7ce1021A7c5EBe60C5B5D28f8717e1B 13 | ROLLUP_ADD_TOKEN_ADDRESS=0xB3A8C813104705506201E0F63A25B77C60D8C8DA 14 | MAX_DEPOSITS=18 15 | 16 | # ROLLUP POB SETTINGS 17 | POB_BURN_ADDRESS=0x4ACe18d2ba9F545FE85DC1467bA2933A1729373C 18 | POB_URL_DEFAULT=urloperator.io 19 | 20 | # POOL SETTINGS 21 | MAX_OFFCHAIN_TX=64 22 | POOL_TIMEOUT=10800 23 | PATH_CONVERSION_TABLE=/table-conversion/table-conversion.json 24 | 25 | # POSEIDON_ADDRESS=0x29132119dC226998cdF18a2A75685603DF05daf9 26 | # VERIFIER_ADDRESS=debug -------------------------------------------------------------------------------- /docker/docker-contracts/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | ARG branch=master 3 | RUN git clone https://github.com/iden3/rollup.git 4 | WORKDIR /rollup 5 | RUN git checkout $branch 6 | RUN npm install 7 | COPY ./config-contracts/* ./ 8 | 9 | CMD ["sh", "-c", "npx truffle compile && node deployment-script.js && cp synch-config.json ../config-contracts/ && cp pool-config.json ../config-contracts/ && cp wallet.json ../config-contracts/ "] -------------------------------------------------------------------------------- /docker/docker-contracts/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | contracts: 4 | container_name: contracts 5 | build: 6 | context: . 7 | args: 8 | - branch=${BRANCH} 9 | volumes: 10 | - ./config-contracts:/config-contracts 11 | env_file: 12 | - ./.env -------------------------------------------------------------------------------- /docker/docker-operator/.env.example: -------------------------------------------------------------------------------- 1 | BRANCH_OPERATOR=master 2 | BRANCH_SYNCH_POOL=master 3 | BRANCH_SERVER_PROOF=master -------------------------------------------------------------------------------- /docker/docker-operator/docker-compose.yml: -------------------------------------------------------------------------------- 1 | 2 | version: '3' 3 | services: 4 | operator: 5 | container_name: operator 6 | ports: 7 | - '9000:9000' 8 | build: 9 | context: ./operator/ 10 | args: 11 | - branch=${BRANCH_OPERATOR} 12 | env_file: 13 | - ./operator/config.env 14 | volumes: 15 | - pool-volume:/table-conversion 16 | - ./leveldb-operator:/leveldb-operator 17 | 18 | synch-pool: 19 | container_name: synch-pool 20 | build: 21 | context: ./synch-pool/ 22 | args: 23 | - branch=${BRANCH_SYNCH_POOL} 24 | env_file: 25 | - ./synch-pool/config.env 26 | volumes: 27 | - pool-volume:/table-conversion 28 | - ./leveldb-synch-pool:/leveldb-synch-pool 29 | 30 | server-proof: 31 | container_name: server-proof 32 | build: 33 | context: ./server-proof/ 34 | args: 35 | - branch=${BRANCH_SERVER_PROOF} 36 | env_file: 37 | - ./server-proof/config.env 38 | expose: 39 | - '10001' 40 | volumes: 41 | pool-volume: -------------------------------------------------------------------------------- /docker/docker-operator/operator/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | ARG branch=master 3 | RUN git clone https://github.com/iden3/rollup.git 4 | RUN mkdir table-conversion 5 | 6 | # Download and install dependencies 7 | WORKDIR /rollup 8 | RUN git checkout $branch 9 | RUN npm install 10 | WORKDIR /rollup/rollup-operator 11 | RUN npm install 12 | WORKDIR /rollup 13 | 14 | # Build configuration files 15 | COPY config-operator/* ./ 16 | RUN node build-config.js 17 | RUN ./create-config-env.sh 18 | RUN mv pool-config.json ./rollup-operator/src/server/proof-of-burn 19 | RUN mv synch-config.json ./rollup-operator/src/server/proof-of-burn 20 | RUN mv wallet.json ./rollup-operator/src/server/proof-of-burn 21 | RUN mv config.env ./rollup-operator/src/server/proof-of-burn 22 | 23 | CMD ["sh","-c","node rollup-operator/src/server/proof-of-burn/operator-pob.js --clear $CLEAR_DB --onlysynch $ONLY_SYNCH"] 24 | -------------------------------------------------------------------------------- /docker/docker-operator/operator/config-operator/build-config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | const configSynchPath = path.join(__dirname, "./synch-config.json"); 4 | 5 | const configSynch = JSON.parse(fs.readFileSync(configSynchPath, "utf-8")); 6 | 7 | configSynch.rollup.synchDb = "/leveldb-operator/tmp-0"; 8 | configSynch.rollup.treeDb = "/leveldb-operator/tmp-1"; 9 | configSynch.rollupPoB.synchDb = "/leveldb-operator/tmp-2"; 10 | 11 | fs.writeFileSync(configSynchPath, JSON.stringify(configSynch)); -------------------------------------------------------------------------------- /docker/docker-operator/operator/config-operator/create-config-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | path_synch="$(pwd)/rollup-operator/src/server/proof-of-burn/synch-config.json" 4 | path_pool="$(pwd)/rollup-operator/src/server/proof-of-burn/pool-config.json" 5 | path_wallet="$(pwd)/rollup-operator/src/server/proof-of-burn/wallet.json" 6 | 7 | echo "WALLET_PATH=$path_wallet 8 | CONFIG_SYNCH=$path_synch 9 | CONFIG_POOL=$path_pool" > config.env -------------------------------------------------------------------------------- /docker/docker-operator/operator/config.env.example: -------------------------------------------------------------------------------- 1 | OPERATOR_PORT_EXTERNAL=port 2 | URL_SERVER_PROOF=http://server-proof:10001 3 | LOG_LEVEL=debug 4 | OPERATOR_MODE=archive 5 | GAS_MULTIPLIER=1 6 | GAS_LIMIT=default 7 | PASSWORD=password 8 | EXPOSE_API_SERVER=true 9 | LAN=true 10 | ONLY_SYNCH=false 11 | CLEAR_DB=false 12 | -------------------------------------------------------------------------------- /docker/docker-operator/server-proof/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | ARG branch=master 3 | RUN git clone https://github.com/iden3/rollup.git 4 | WORKDIR /rollup 5 | RUN git checkout $branch 6 | RUN npm install 7 | WORKDIR /rollup/rollup-operator/src 8 | RUN npm install 9 | 10 | CMD ["sh","-c","node server-proof.js $PROOF_TIME"] -------------------------------------------------------------------------------- /docker/docker-operator/server-proof/config.env.example: -------------------------------------------------------------------------------- 1 | PROOF_TIME=30000 -------------------------------------------------------------------------------- /docker/docker-operator/synch-pool/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | ARG branch=master 3 | RUN git clone https://github.com/iden3/rollup.git 4 | RUN mkdir table-conversion 5 | 6 | # Download and install dependencies 7 | WORKDIR /rollup 8 | RUN git checkout $branch 9 | RUN npm install 10 | WORKDIR /rollup/rollup-operator/src 11 | RUN npm install 12 | WORKDIR /rollup 13 | 14 | # Build configuration files 15 | COPY config-pool/* ./ 16 | RUN node build-config.js 17 | RUN ./create-config-env.sh 18 | RUN mv config.env ./rollup-operator/src/synch-pool-service/ 19 | 20 | CMD ["sh", "-c", "node rollup-operator/src/synch-pool-service/run-synch-pool --clear $CLEAR_DB"] -------------------------------------------------------------------------------- /docker/docker-operator/synch-pool/config-pool/build-config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const fs = require("fs"); 3 | const configSynchPath = path.join(__dirname, "./synch-config.json"); 4 | const configPath = path.join(__dirname, "./config.json"); 5 | const customPath = path.join(__dirname, "./custom.json"); 6 | 7 | const configSynch = JSON.parse(fs.readFileSync(configSynchPath, "utf-8")); 8 | 9 | const config = { 10 | pathDb: "/leveldb-synch-pool/tmp-0", 11 | ethNodeUrl: configSynch.ethNodeUrl, 12 | ethAddress: configSynch.ethAddress, 13 | rollupAddress: configSynch.rollup.address, 14 | rollupAbi: configSynch.rollup.abi, 15 | logLevel: process.env.LOG_LEVEL, 16 | pathConversionTable: path.join(__dirname, "../table-conversion/table-conversion.json"), 17 | pathCustomTokens: customPath, 18 | timeouts: {"ERROR":6000, "NEXT_LOOP":60000} 19 | }; 20 | 21 | fs.writeFileSync(configPath, JSON.stringify(config)); -------------------------------------------------------------------------------- /docker/docker-operator/synch-pool/config-pool/create-config-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | path_config="$(pwd)/config.json" 4 | 5 | echo "CONFIG_PATH = $path_config" > config.env -------------------------------------------------------------------------------- /docker/docker-operator/synch-pool/config-pool/custom.json: -------------------------------------------------------------------------------- 1 | { 2 | "0xaFF4481D10270F50f203E0763e2597776068CBc5": 3 | { 4 | "price": 1, 5 | "decimals": 18 6 | } 7 | } -------------------------------------------------------------------------------- /docker/docker-operator/synch-pool/config.env.example: -------------------------------------------------------------------------------- 1 | LOG_LEVEL=info 2 | CLEAR_DB=false -------------------------------------------------------------------------------- /docker/docker-webapp/.env.example: -------------------------------------------------------------------------------- 1 | URL_OPERATOR=http://url:port 2 | TOKEN_ADDRESS=0xaFF4481D10270F50f203E0763e2597776068CBc5 3 | BRANCH=master -------------------------------------------------------------------------------- /docker/docker-webapp/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | ARG branch=master 3 | RUN npm install -g browserify 4 | RUN git clone https://github.com/iden3/rollup.git 5 | WORKDIR "./rollup" 6 | RUN git checkout $branch 7 | RUN npm install 8 | RUN npm run build:webapp 9 | COPY config-webapp/* ./ 10 | RUN node build-config.js 11 | RUN mv config.json ./simple-webapp/src/utils 12 | WORKDIR "./simple-webapp" 13 | RUN npm install 14 | 15 | CMD ["npm", "start"] -------------------------------------------------------------------------------- /docker/docker-webapp/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | webapp: 4 | container_name: webapp 5 | build: 6 | context: . 7 | args: 8 | - branch=${BRANCH} 9 | env_file: 10 | - ./.env 11 | ports: 12 | - '3000:3000' -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const cli = require("./rollup-cli/index.js"); 2 | const utils = require("./rollup-utils/index.js"); 3 | 4 | module.exports = { 5 | cli, 6 | utils, 7 | }; -------------------------------------------------------------------------------- /js/constants.js: -------------------------------------------------------------------------------- 1 | const poseidon = require("circomlib").poseidon; 2 | const utils = require("ffjavascript").utils; 3 | const Scalar = require("ffjavascript").Scalar; 4 | 5 | function string2Int(str) { 6 | return utils.leBuff2int(Buffer.from(str)); 7 | } 8 | 9 | const hash = poseidon.createHash(1, 8, 57); 10 | const hash6 = poseidon.createHash(6, 8, 57); 11 | 12 | module.exports.DB_Master = hash([string2Int("Rollup_DB_Master")]); 13 | module.exports.DB_Batch = hash([string2Int("Rollup_DB_Batch")]); 14 | module.exports.DB_Idx = hash([string2Int("Rollup_DB_Idx")]); 15 | module.exports.DB_AxAy = hash([string2Int("Rollup_DB_AxAy")]); 16 | module.exports.DB_EthAddr = hash([string2Int("Rollup_DB_EthAddr")]); 17 | module.exports.DB_TxPoolSlotsMap = hash([string2Int("Rollup_DB_TxPoolSlots")]); 18 | module.exports.DB_TxPollTx = hash([string2Int("Rollup_DB_TxPollTx")]); 19 | module.exports.DB_TxPoolDepositTx = hash([string2Int("Rollup_DB_TxPoolDepositTx")]); 20 | module.exports.DB_NumBatch_Idx = hash([string2Int("Rollup_DB_NumBatch_Idx")]); 21 | module.exports.DB_NumBatch_AxAy = hash([string2Int("Rollup_DB_NumBatch_AxAy")]); 22 | module.exports.DB_NumBatch_EthAddr = hash([string2Int("Rollup_DB_NumBatch_EthAddr")]); 23 | module.exports.DB_InitialIdx = hash([string2Int("Rollup_DB_Initial_Idx")]); 24 | 25 | module.exports.exitAx = "0x0000000000000000000000000000000000000000000000000000000000000000"; 26 | module.exports.exitAy = "0x0000000000000000000000000000000000000000000000000000000000000000"; 27 | module.exports.exitEthAddr = "0x0000000000000000000000000000000000000000"; 28 | module.exports.exitAccount = hash6([Scalar.fromString(this.exitAx, 16), Scalar.fromString(this.exitAy, 16)]); 29 | 30 | module.exports.fee = { 31 | "0%" : 0, 32 | "0.001%" : 1, 33 | "0.002%": 2, 34 | "0.005%": 3, 35 | "0.01%": 4, 36 | "0.02%": 5, 37 | "0.05%": 6, 38 | "0.1%": 7, 39 | "0.2%": 8, 40 | "0.5%": 9, 41 | "1%": 10, 42 | "2%": 11, 43 | "5%": 12, 44 | "10%": 13, 45 | "20%": 14, 46 | "50%" : 15, 47 | }; 48 | 49 | module.exports.tableAdjustedFee = [ 50 | 0, 51 | 42949, 52 | 85899, 53 | 214748, 54 | 429496, 55 | 858993, 56 | 2147483, 57 | 4294967, 58 | 8589934, 59 | 21474836, 60 | 42949672, 61 | 85899345, 62 | 214748364, 63 | 429496729, 64 | 858993459, 65 | 2147483648, 66 | ]; 67 | -------------------------------------------------------------------------------- /js/rollup-state.js: -------------------------------------------------------------------------------- 1 | const Scalar = require("ffjavascript").Scalar; 2 | const poseidon = require("circomlib").poseidon; 3 | 4 | const utils = require("./utils"); 5 | 6 | class RollupState { 7 | 8 | constructor(idx, state){ 9 | // Key leaf 10 | this.idx = Scalar.e(idx); 11 | // idleaf 12 | this.coin = Scalar.e(state.coin); 13 | this.nonce = Scalar.e(state.nonce); 14 | this.amount = Scalar.e(state.amount); 15 | this.ax = Scalar.fromString(state.ax); 16 | this.ay = Scalar.fromString(state.ay); 17 | this.ethAddr = Scalar.e(state.ethAddr); 18 | } 19 | 20 | /** 21 | * Create a new RollupState from a id and a state array 22 | * @param {Object} id - Leaf identifier 23 | * @param {Array} stateArray - Array containing the leaf state information. 24 | * @returns {Object} RollupState object 25 | */ 26 | static newFromArray(id, stateArray){ 27 | const state = {}; 28 | state.coin = Scalar.toNumber(utils.extract(stateArray[0], 0, 32)); 29 | state.nonce = Scalar.toNumber(utils.extract(stateArray[0], 32, 48)); 30 | state.amount = Scalar.e(stateArray[1]); 31 | state.ax = Scalar.e(stateArray[2]).toString(16); 32 | state.ay = Scalar.e(stateArray[3]).toString(16); 33 | state.ethAddress = "0x" + utils.padZeros(Scalar.e(stateArray[4]).toString(16), 40); 34 | return new RollupState(id, state); 35 | } 36 | 37 | /** 38 | * Encode a state object into an array 39 | * @returns {Array} Resulting array 40 | */ 41 | toArray(){ 42 | const data = Scalar.add(this.coin, Scalar.shl(this.nonce, 32)); 43 | return [ 44 | data, 45 | this.amount, 46 | this.ax, 47 | this.ay, 48 | this.ethAddress, 49 | ]; 50 | } 51 | 52 | 53 | /** 54 | * Return the hash of a state object 55 | * @returns {Scalar} Resulting poseidon hash 56 | */ 57 | getHash(){ 58 | const hash = poseidon.createHash(6, 8, 57); 59 | return hash(this.toArray()); 60 | } 61 | 62 | /** 63 | * Return the hash of the identifier, and babyjub publick key 64 | * @returns {Scalar} Resulting poseidon hash 65 | */ 66 | getUniqueId(){ 67 | const hash = poseidon.createHash(6, 8, 57); 68 | return hash([this.idx, this.ax, this.ay]); 69 | } 70 | } 71 | 72 | module.exports = RollupState; -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | /* global artifacts */ 2 | const Migrations = artifacts.require("Migrations"); 3 | 4 | module.exports = function(deployer) { 5 | deployer.deploy(Migrations); 6 | }; 7 | -------------------------------------------------------------------------------- /migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | /* global artifacts */ 2 | /* global web3 */ 3 | 4 | const poseidonUnit = require("../node_modules/circomlib/src/poseidon_gencontract.js"); 5 | const Verifier = artifacts.require("../contracts/test/VerifierHelper"); 6 | const Rollup = artifacts.require("../contracts/Rollup"); 7 | const TokenRollup = artifacts.require("../contracts/test/TokenRollup"); 8 | const RollupPoS = artifacts.require("../contracts/RollupPoS"); 9 | 10 | const maxTx = 4; 11 | const maxOnChainTx = 2; 12 | const initialAmount = 1000; 13 | let insPoseidonUnit; 14 | let feeTokenAddress = "0x29a9b6486667E67e4ef9e586E3622d35b19E3E7E"; 15 | 16 | module.exports = async function (deployer, network, accounts) { 17 | const C = new web3.eth.Contract(poseidonUnit.abi); 18 | insPoseidonUnit = await C.deploy({ data: poseidonUnit.createCode() }) 19 | .send({ gas: 2500000, from: accounts[0] }); 20 | // console.log("Poseidon address:" + insPoseidonUnit._address); 21 | await deployer.deploy(TokenRollup, accounts[0], initialAmount); 22 | // console.log("Token rollup address:" + TokenRollup.address); 23 | await deployer.deploy(Verifier); 24 | // console.log("Verifier address:" + Verifier.address); 25 | await deployer.deploy(Rollup, Verifier.address, insPoseidonUnit._address, 26 | maxTx, maxOnChainTx, feeTokenAddress); 27 | // console.log("Rollup address:" + Rollup.address); 28 | await deployer.deploy(RollupPoS, Rollup.address, maxTx); 29 | // console.log("RollupPoS address:" + RollupPoS.address); 30 | // load forge batch mechanism 31 | const insRollup = await Rollup.deployed(); 32 | await insRollup.loadForgeBatchMechanism(RollupPoS.address); 33 | }; 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollup", 3 | "version": "0.0.1", 4 | "description": "Rollup Implementation", 5 | "main": "index.js", 6 | "directories": { 7 | "doc": "doc" 8 | }, 9 | "scripts": { 10 | "ganache": "npx ganache-cli -a 100 --defaultBalanceEther 1000000 > ganache-output.log &", 11 | "test:circuits": "npx mocha --max-old-space-size=4096 --timeout 20000 ./test/circuits", 12 | "test:utils": "npx mocha --timeout 20000 ./test/rollup-utils", 13 | "test:js": "npx mocha --timeout 20000 ./test/js", 14 | "test:contracts": "npm run ganache && npx truffle test ./test/contracts/*.test.js ; rm './ganache-output.log' ; pkill -x 'node*'", 15 | "test:rollup": "npm run test:circuits && npm run test:utils && npm run test:js && npm run test:contracts", 16 | "test:coverage": "npx truffle run coverage --file='./test/contracts/*.test.js'", 17 | "build:webapp": "browserify rollup-cli/index.js -s rollup > simple-webapp/src/utils/bundle-cli.js && browserify rollup-operator/index.js -s operator > simple-webapp/src/utils/bundle-op.js && simple-webapp/src/utils/disable-eslint.sh", 18 | "test:all": "npm run test:rollup && cd rollup-cli && npm run test:all && cd ../rollup-operator && npm run test:all && cd ../cli-pob && npm run test:all" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/iden3/rollup.git" 23 | }, 24 | "keywords": [ 25 | "rollup", 26 | "blockchain", 27 | "scalability", 28 | "ethereum", 29 | "zksnarks", 30 | "zero", 31 | "knowledge", 32 | "circom" 33 | ], 34 | "author": "Jordi Baylina", 35 | "license": "AGPL-3.0", 36 | "bugs": { 37 | "url": "https://github.com/iden3/rollup/issues" 38 | }, 39 | "homepage": "https://github.com/iden3/rollup#readme", 40 | "devDependencies": { 41 | "acorn": "^7.1.1", 42 | "eslint": "^6.5.1", 43 | "eslint-config-airbnb-base": "^14.0.0", 44 | "eslint-plugin-import": "^2.18.2", 45 | "eslint-plugin-mocha": "^6.2.0", 46 | "ganache-cli": "^6.9.1", 47 | "mocha": "^6.2.0", 48 | "solidity-coverage": "^0.7.2" 49 | }, 50 | "dependencies": { 51 | "@openzeppelin/contracts": "^3.0.0-beta.0", 52 | "abi-decoder": "^2.2.0", 53 | "axios": "^0.19.0", 54 | "big-integer": "^1.6.48", 55 | "bip39": "^3.0.2", 56 | "blake-hash": "^1.1.0", 57 | "blakejs": "^1.1.0", 58 | "chai": "^4.2.0", 59 | "circom": "^0.5.10", 60 | "circomlib": "^0.2.3", 61 | "dotenv": "^8.2.0", 62 | "elliptic": "^6.5.1", 63 | "ethereumjs-util": "^6.1.0", 64 | "ethers": "^4.0.38", 65 | "ffjavascript": "^0.1.3", 66 | "hdkey": "^1.1.1", 67 | "ip": "^1.1.5", 68 | "js-sha3": "^0.8.0", 69 | "level": "^5.0.1", 70 | "morgan": "^1.9.1", 71 | "snarkjs": "^0.1.31", 72 | "solc": "^0.6.1", 73 | "tmp-promise": "^3.0.2", 74 | "truffle": "^5.1.16", 75 | "truffle-hdwallet-provider": "^1.0.17", 76 | "v8-compile-cache": "^2.1.0", 77 | "web3": "^1.2.5" 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /rollup-cli/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": [ 3 | "mocha" 4 | ], 5 | "env": { 6 | "node": true, 7 | "mocha": true 8 | }, 9 | "extends": "airbnb-base", 10 | "rules": { 11 | "indent": ["error", 4], 12 | "mocha/no-exclusive-tests": "error", 13 | "max-len": ["error", { "code": 140, "comments": 200, "ignoreStrings": true, "ignoreTemplateLiterals": true }], 14 | "no-unused-vars": [2, { "varsIgnorePattern": "export^" }], 15 | "no-return-assign": [0], 16 | "no-underscore-dangle": [0], 17 | "no-plusplus": ["error", { "allowForLoopAfterthoughts": true }], 18 | "func-names": [0], 19 | "class-methods-use-this": [0], 20 | "no-bitwise": [0], 21 | "no-param-reassign": "off", 22 | "no-console": [2, { "allow": ["warn", "error"] }], 23 | "import/prefer-default-export": [0], 24 | "lines-between-class-members": [ "error", "always", { "exceptAfterSingleLine": true }] 25 | } 26 | } -------------------------------------------------------------------------------- /rollup-cli/.gitignore: -------------------------------------------------------------------------------- 1 | getIden3.sh 2 | node_modules 3 | /test/resources/ 4 | /wallet.json 5 | /config.json 6 | /*.test.json -------------------------------------------------------------------------------- /rollup-cli/config-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "urlOperator": "https://zkrollup.iden3.net", 3 | "nodeEth": "https://goerli.infura.io/v3/135e56bb9eaa42c59e73481fcb0f9b4a", 4 | "addressRollup": "0xbC0fd0Bd2e5B5CC7FE947A829067D207381E03FA", 5 | "abiRollupPath": "./helpers/abis/RollupAbi.json", 6 | "controllerAddress": 0, 7 | "abiTokensPath": "./helpers/abis/ERC20Abi.json", 8 | "addressTokens": "0xaFF4481D10270F50f203E0763e2597776068CBc5", 9 | "wallet": "./wallet.json" 10 | } -------------------------------------------------------------------------------- /rollup-cli/helpers/list-errors.js: -------------------------------------------------------------------------------- 1 | const error = { 2 | ERROR: 0, 3 | INVALID_KEY_TYPE: 1, 4 | PASSWORD_NOT_MATCH: 2, 5 | INVALID_MNEMONIC: 3, 6 | INVALID_PATH: 4, 7 | NO_PARAM: 5, 8 | NO_VALUE: 6, 9 | INVALID_PARAM: 7, 10 | INVALID_TYPE: 8, 11 | INVALID_COMMAND: 9, 12 | NO_PARAMS_FILE: 10, 13 | NO_TYPE: 11, 14 | NO_WALLET: 12, 15 | INVALID_FILTER: 13, 16 | INVALID_FEE: 14, 17 | }; 18 | 19 | module.exports = { error }; 20 | -------------------------------------------------------------------------------- /rollup-cli/helpers/utils.js: -------------------------------------------------------------------------------- 1 | const { babyJub } = require('circomlib'); 2 | const { Scalar } = require('ffjavascript'); 3 | 4 | function pointHexToCompress(pointHex) { 5 | if (!pointHex[0].startsWith('0x')) { 6 | pointHex[0] = `0x${pointHex[0]}`; 7 | } 8 | if (!pointHex[1].startsWith('0x')) { 9 | pointHex[1] = `0x${pointHex[1]}`; 10 | } 11 | const point = [ 12 | Scalar.e(pointHex[0]), Scalar.e(pointHex[1]), 13 | ]; 14 | const buf = babyJub.packPoint(point); 15 | 16 | return buf.toString('hex'); 17 | } 18 | 19 | function hexToPoint(compress) { 20 | let compressHex; 21 | if (compress.startsWith('0x')) compressHex = compress.slice(2); 22 | else compressHex = compress; 23 | const buf = Buffer.from(compressHex, 'hex'); 24 | const point = babyJub.unpackPoint(buf); 25 | const pointHexAx = `0x${point[0].toString(16)}`; 26 | const pointHexAy = `0x${point[1].toString(16)}`; 27 | const pointHex = [pointHexAx, pointHexAy]; 28 | 29 | return pointHex; 30 | } 31 | 32 | module.exports = { 33 | pointHexToCompress, 34 | hexToPoint, 35 | }; 36 | -------------------------------------------------------------------------------- /rollup-cli/index.js: -------------------------------------------------------------------------------- 1 | const Db = require('./src/utils/db'); 2 | const ethereumWallet = require('./src/utils/ethereum-wallet'); 3 | const wallet = require('./src/utils/wallet'); 4 | const onchain = require('./src/actions/onchain/onchain'); 5 | const offchain = require('./src/actions/offchain/offchain'); 6 | 7 | module.exports = { 8 | Db, 9 | ethereumWallet, 10 | wallet, 11 | onchain, 12 | offchain, 13 | }; 14 | -------------------------------------------------------------------------------- /rollup-cli/keys.json: -------------------------------------------------------------------------------- 1 | {"address":"00b08c6d3bdf12ede8f236f828abb3822152fff9","crypto":{"cipher":"aes-128-ctr","ciphertext":"3385c765a3706f6b329b3e50eac4fab57562e1d76e90487c2abe139dafdff63a","cipherparams":{"iv":"83b2a66f12590a68d2284a00c9360087"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"633aec7a1af680f877d0b0fd9c2ff9b48ce4541f5ca615f3d71f64a67c919dff"},"mac":"8e22f776ee50b0a71e4edd2c78474b80dae362bede5d06f6ba500c71102696d6"},"id":"ea80e1ca-e8f5-41e2-923e-12f7e5ef8662","version":3} -------------------------------------------------------------------------------- /rollup-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollup-cli", 3 | "version": "0.0.1", 4 | "description": "light client rollup", 5 | "main": "index.js", 6 | "scripts": { 7 | "ganache": "npx ganache-cli -a 100 --defaultBalanceEther 1000000 > ganache-output.log &", 8 | "operator-dummy": "node ./test/helpers/api-client.js > operator-output.log &", 9 | "test": "mocha", 10 | "lint": "eslint ./src", 11 | "test:utils": "npm run operator-dummy && npx mocha ./test/utils; rm 'operator-output.log' ; pkill -x 'node*'", 12 | "test:actions": "npm run operator-dummy && npm run ganache && npx mocha ./test/actions/send.test.js && npx truffle test ./test/actions/onchain.test.js ; rm *output.log ; pkill -x 'node*'", 13 | "test:integration": "npm run operator-dummy && npm run ganache && npx truffle test ./test/integration-test/config/setup-cli.test.js && npx mocha test/integration-test/cli.test.js ; rm *output.log ; pkill -x 'node*'", 14 | "test:all": "npm run test:utils && npm run test:actions && npm run test:integration" 15 | }, 16 | "bin": { 17 | "rollup-cli": "cli.js" 18 | }, 19 | "keywords": [ 20 | "client", 21 | "rollup", 22 | "cli" 23 | ], 24 | "author": "krlosMata", 25 | "license": "AGPL-3.0", 26 | "devDependencies": { 27 | "chai": "^4.2.0", 28 | "eslint": "^6.5.1", 29 | "eslint-config-airbnb-base": "^14.0.0", 30 | "eslint-plugin-import": "^2.18.2", 31 | "eslint-plugin-mocha": "^6.2.0", 32 | "ganache-cli": "^6.9.1", 33 | "mocha": "^6.2.0" 34 | }, 35 | "dependencies": { 36 | "body-parser": "^1.19.0", 37 | "chalk": "^4.0.0", 38 | "circomlib": "^0.2.3", 39 | "ethers": "^4.0.46", 40 | "express": "^4.17.1", 41 | "ffjavascript": "^0.1.3", 42 | "fs-extra": "^8.1.0", 43 | "readline": "^1.3.0", 44 | "snarkjs": "^0.1.31", 45 | "stream": "0.0.2", 46 | "yargs": "^14.2.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /rollup-cli/src/actions/offchain/offchain.js: -------------------------------------------------------------------------------- 1 | const send = require('./send'); 2 | 3 | module.exports = { 4 | send, 5 | }; 6 | -------------------------------------------------------------------------------- /rollup-cli/src/actions/onchain/approve.js: -------------------------------------------------------------------------------- 1 | const ethers = require('ethers'); 2 | 3 | const { getGasPrice } = require('./utils'); 4 | 5 | /** 6 | * approve tokens to be spent by rollup smart contract, also 7 | * if the tokens address match the goerli faucet WEENUS, get tokens from it. 8 | * @param {String} nodeEth - URL of the ethereum node 9 | * @param {String} addressTokens - ERC20 Smart contract 10 | * @param {String} amount - amount to approve 11 | * @param {Number} spender - rollup address 12 | * @param {Object} walletRollup - ethAddress and babyPubKey together 13 | * @param {String} abi - abi of ERC20 contract 14 | * @param {Number} gasLimit - transaction gas limit 15 | * @param {Number} gasMultiplier - multiply gas price 16 | * @returns {Promise} - promise will resolve when the Tx is sent, and return the Tx itself with the Tx Hash. 17 | */ 18 | async function approve(nodeEth, addressTokens, amount, spender, walletRollup, 19 | abi, gasLimit = 5000000, gasMultiplier = 1) { 20 | addressTokens = addressTokens || '0xaFF4481D10270F50f203E0763e2597776068CBc5'; // test token already added in goerli Rollup 21 | let walletEth = walletRollup.ethWallet.wallet; 22 | const provider = new ethers.providers.JsonRpcProvider(nodeEth); 23 | walletEth = walletEth.connect(provider); 24 | const contractWithSigner = new ethers.Contract(addressTokens, abi, walletEth); 25 | 26 | const overrides = { 27 | gasLimit, 28 | gasPrice: await getGasPrice(gasMultiplier, provider), 29 | }; 30 | 31 | if (addressTokens === '0xaFF4481D10270F50f203E0763e2597776068CBc5') { // get test ERC20 tokens form goerli ERC20 faucet. 32 | const tx = { 33 | to: addressTokens, 34 | value: ethers.utils.parseEther('0'), 35 | }; 36 | await walletEth.sendTransaction(tx); 37 | } 38 | 39 | try { 40 | return await contractWithSigner.approve(spender, amount, overrides); 41 | } catch (error) { 42 | throw new Error(`Message error: ${error.message}`); 43 | } 44 | } 45 | 46 | module.exports = { 47 | approve, 48 | }; 49 | -------------------------------------------------------------------------------- /rollup-cli/src/actions/onchain/deposit-and-transfer.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | const ethers = require('ethers'); 3 | const { Scalar } = require('ffjavascript'); 4 | 5 | const { fix2float } = require('../../../../js/utils'); 6 | const { getGasPrice } = require('./utils'); 7 | 8 | /** 9 | * leaf to balance tree and initializes it with a load amount then transfer 10 | * some amount to an account already defined in the balance tree 11 | * @param {String} nodeEth - URL of the ethereum node 12 | * @param {String} addressSC - rollup address 13 | * @param {String} loadAmount - initial Amount on balance tree 14 | * @param {String} amount - amount to transfer 15 | * @param {Number} tokenId - token type identifier 16 | * @param {String[2]} babyjubTo - babyjub public key receiver 17 | * @param {Object} walletRollup - ethAddress and babyPubKey together 18 | * @param {String} ethAddress - allowed address to control new balance tree leaf 19 | * @param {String} abi - abi of rollup contract 20 | * @param {Number} gasLimit - transaction gas limit 21 | * @param {Number} gasMultiplier - multiply gas price 22 | * @returns {Promise} - promise will resolve when the Tx is sent, and return the Tx itself with the Tx Hash. 23 | */ 24 | async function depositAndTransfer(nodeEth, addressSC, loadAmount, amount, tokenId, babyjubTo, walletRollup, 25 | ethAddress, abi, gasLimit = 5000000, gasMultiplier = 1) { 26 | const walletBaby = walletRollup.babyjubWallet; 27 | const pubKeyBabyjub = [walletBaby.publicKey[0].toString(), walletBaby.publicKey[1].toString()]; 28 | 29 | let walletEth = walletRollup.ethWallet.wallet; 30 | const provider = new ethers.providers.JsonRpcProvider(nodeEth); 31 | walletEth = walletEth.connect(provider); 32 | const contractWithSigner = new ethers.Contract(addressSC, abi, walletEth); 33 | 34 | const address = ethAddress || await walletEth.getAddress(); 35 | const amountF = fix2float(amount); 36 | 37 | const feeOnchainTx = await contractWithSigner.feeOnchainTx(); 38 | const feeDeposit = await contractWithSigner.depositFee(); 39 | 40 | const overrides = { 41 | gasLimit, 42 | gasPrice: await getGasPrice(gasMultiplier, provider), 43 | value: `0x${(Scalar.add(feeOnchainTx, feeDeposit)).toString(16)}`, 44 | }; 45 | 46 | try { 47 | return await contractWithSigner.depositAndTransfer(loadAmount, tokenId, 48 | address, pubKeyBabyjub, babyjubTo, amountF, overrides); 49 | } catch (error) { 50 | throw new Error(`Message error: ${error.message}`); 51 | } 52 | } 53 | 54 | module.exports = { 55 | depositAndTransfer, 56 | }; 57 | -------------------------------------------------------------------------------- /rollup-cli/src/actions/onchain/deposit-on-top.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | const ethers = require('ethers'); 3 | 4 | const { getGasPrice } = require('./utils'); 5 | 6 | /** 7 | * deposit on an existing balance tree leaf 8 | * @param {String} nodeEth - URL of the ethereum node 9 | * @param {String} addressSC - rollup address 10 | * @param {String} loadAmount - initial balance on balance tree 11 | * @param {Number} tokenId - token type identifier 12 | * @param {String[2]} babyjubTo - babyjub public key receiver 13 | * @param {Object} walletRollup - ethAddress and babyPubKey together 14 | * @param {String} abi - abi of rollup contract 15 | * @param {Number} gasLimit - transaction gas limit 16 | * @param {Number} gasMultiplier - multiply gas price 17 | * @returns {Promise} - promise will resolve when the Tx is sent, and return the Tx itself with the Tx Hash. 18 | */ 19 | async function depositOnTop(nodeEth, addressSC, loadAmount, tokenId, babyjubTo, walletRollup, 20 | abi, gasLimit = 5000000, gasMultiplier = 1) { 21 | let walletEth = walletRollup.ethWallet.wallet; 22 | const provider = new ethers.providers.JsonRpcProvider(nodeEth); 23 | walletEth = walletEth.connect(provider); 24 | const contractWithSigner = new ethers.Contract(addressSC, abi, walletEth); 25 | 26 | const feeOnchainTx = await contractWithSigner.feeOnchainTx(); 27 | const overrides = { 28 | gasLimit, 29 | gasPrice: await getGasPrice(gasMultiplier, provider), 30 | value: feeOnchainTx, 31 | }; 32 | 33 | try { 34 | return await contractWithSigner.depositOnTop(babyjubTo, loadAmount, tokenId, overrides); 35 | } catch (error) { 36 | throw new Error(`Message error: ${error.message}`); 37 | } 38 | } 39 | 40 | module.exports = { 41 | depositOnTop, 42 | }; 43 | -------------------------------------------------------------------------------- /rollup-cli/src/actions/onchain/deposit.js: -------------------------------------------------------------------------------- 1 | const ethers = require('ethers'); 2 | const { Scalar } = require('ffjavascript'); 3 | 4 | const { getGasPrice } = require('./utils'); 5 | 6 | /** 7 | * deposit on-chain transaction 8 | * add new leaf to balance tree and initializes it with a load amount 9 | * @param {String} nodeEth - URL of the ethereum node 10 | * @param {String} addressSC - rollup address 11 | * @param {String} loadAmount - initial balance on balance tree 12 | * @param {Number} tokenId - token type identifier 13 | * @param {Object} walletRollup - ethAddress and babyPubKey together 14 | * @param {String} ethAddress - allowed address to control new balance tree leaf 15 | * @param {String} abi - abi of rollup contract 16 | * @param {Number} gasLimit - transaction gas limit 17 | * @param {Number} gasMultiplier - multiply gas price 18 | * @returns {Promise} - promise will resolve when the Tx is sent, and return the Tx itself with the Tx Hash. 19 | */ 20 | async function deposit(nodeEth, addressSC, loadAmount, tokenId, walletRollup, 21 | ethAddress, abi, gasLimit = 5000000, gasMultiplier = 1) { 22 | const walletBaby = walletRollup.babyjubWallet; 23 | const pubKeyBabyjub = [walletBaby.publicKey[0].toString(), walletBaby.publicKey[1].toString()]; 24 | 25 | let walletEth = walletRollup.ethWallet.wallet; 26 | const provider = new ethers.providers.JsonRpcProvider(nodeEth); 27 | walletEth = walletEth.connect(provider); 28 | const contractWithSigner = new ethers.Contract(addressSC, abi, walletEth); 29 | 30 | const address = ethAddress || await walletEth.getAddress(); 31 | 32 | const feeOnchainTx = await contractWithSigner.feeOnchainTx(); 33 | const feeDeposit = await contractWithSigner.depositFee(); 34 | 35 | const overrides = { 36 | gasLimit, 37 | gasPrice: await getGasPrice(gasMultiplier, provider), 38 | value: `0x${(Scalar.add(feeOnchainTx, feeDeposit)).toString(16)}`, 39 | }; 40 | try { 41 | return await contractWithSigner.deposit(loadAmount, tokenId, address, pubKeyBabyjub, overrides); 42 | } catch (error) { 43 | throw new Error(`Message error: ${error.message}`); 44 | } 45 | } 46 | 47 | module.exports = { 48 | deposit, 49 | }; 50 | -------------------------------------------------------------------------------- /rollup-cli/src/actions/onchain/force-withdraw.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | const ethers = require('ethers'); 3 | 4 | const { fix2float } = require('../../../../js/utils'); 5 | const { getGasPrice } = require('./utils'); 6 | 7 | /** 8 | * on-chain transaction to build a leaf on exit tree 9 | * @param {String} nodeEth - URL of the ethereum node 10 | * @param {String} addressSC - rollup address 11 | * @param {Number} tokenId - token type identifier 12 | * @param {String} amount - amount to transfer to the leaf of exit tree 13 | * @param {Obejct} walletRollup - ethAddress and babyPubKey together 14 | * @param {String} abi - abi of rollup contract 15 | * @param {Number} gasLimit - transaction gas limit 16 | * @param {Number} gasMultiplier - multiply gas price 17 | * @returns {Promise} - promise will resolve when the Tx is sent, and return the Tx itself with the Tx Hash. 18 | */ 19 | async function forceWithdraw(nodeEth, addressSC, tokenId, amount, walletRollup, abi, gasLimit = 5000000, gasMultiplier = 1) { 20 | const walletBaby = walletRollup.babyjubWallet; 21 | const pubKeyBabyjub = [walletBaby.publicKey[0].toString(), walletBaby.publicKey[1].toString()]; 22 | 23 | let walletEth = walletRollup.ethWallet.wallet; 24 | const provider = new ethers.providers.JsonRpcProvider(nodeEth); 25 | walletEth = walletEth.connect(provider); 26 | const contractWithSigner = new ethers.Contract(addressSC, abi, walletEth); 27 | 28 | const feeOnchainTx = await contractWithSigner.feeOnchainTx(); 29 | const overrides = { 30 | gasLimit, 31 | gasPrice: await getGasPrice(gasMultiplier, provider), 32 | value: feeOnchainTx, 33 | }; 34 | 35 | const amountF = fix2float(amount); 36 | try { 37 | return await contractWithSigner.forceWithdraw(pubKeyBabyjub, tokenId, amountF, overrides); 38 | } catch (error) { 39 | throw new Error(`Message error: ${error.message}`); 40 | } 41 | } 42 | 43 | module.exports = { 44 | forceWithdraw, 45 | }; 46 | -------------------------------------------------------------------------------- /rollup-cli/src/actions/onchain/onchain.js: -------------------------------------------------------------------------------- 1 | const deposit = require('./deposit'); 2 | const depositOnTop = require('./deposit-on-top'); 3 | const forceWithdraw = require('./force-withdraw'); 4 | const withdraw = require('./withdraw'); 5 | const depositAndTransfer = require('./deposit-and-transfer'); 6 | const transfer = require('./transfer'); 7 | const utils = require('./utils'); 8 | 9 | module.exports = { 10 | deposit, 11 | depositOnTop, 12 | forceWithdraw, 13 | withdraw, 14 | depositAndTransfer, 15 | transfer, 16 | utils, 17 | }; 18 | -------------------------------------------------------------------------------- /rollup-cli/src/actions/onchain/transfer.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | const ethers = require('ethers'); 3 | 4 | const { fix2float } = require('../../../../js/utils'); 5 | const { getGasPrice } = require('./utils'); 6 | 7 | /** 8 | * transfer between two accounts already defined in tree leaf 9 | * @param {String} nodeEth - URL of the ethereum node 10 | * @param {String} addressSC - rollup address 11 | * @param {String} amount - initial balance on balance tree 12 | * @param {Number} tokenId - token type identifier 13 | * @param {String[2]} babyjubTo - babyjub public key receiver 14 | * @param {Object} walletRollup - ethAddress and babyPubKey together sender 15 | * @param {String} abi - abi of rollup contract 16 | * @param {Number} gasLimit - transaction gas limit 17 | * @param {Number} gasMultiplier - multiply gas price 18 | * @returns {Promise} - promise will resolve when the Tx is sent, and return the Tx itself with the Tx Hash. 19 | */ 20 | async function transfer(nodeEth, addressSC, amount, tokenId, babyjubTo, walletRollup, 21 | abi, gasLimit = 5000000, gasMultiplier = 1) { 22 | const walletBaby = walletRollup.babyjubWallet; 23 | const pubKeyBabyjub = [walletBaby.publicKey[0].toString(), walletBaby.publicKey[1].toString()]; 24 | 25 | let walletEth = walletRollup.ethWallet.wallet; 26 | const provider = new ethers.providers.JsonRpcProvider(nodeEth); 27 | walletEth = walletEth.connect(provider); 28 | const contractWithSigner = new ethers.Contract(addressSC, abi, walletEth); 29 | 30 | const feeOnchainTx = await contractWithSigner.feeOnchainTx(); 31 | const overrides = { 32 | gasLimit, 33 | gasPrice: await getGasPrice(gasMultiplier, provider), 34 | value: feeOnchainTx, 35 | }; 36 | 37 | const amountF = fix2float(amount); 38 | try { 39 | return contractWithSigner.transfer(pubKeyBabyjub, babyjubTo, amountF, tokenId, overrides); 40 | } catch (error) { 41 | throw new Error(`Message error: ${error.message}`); 42 | } 43 | } 44 | 45 | module.exports = { 46 | transfer, 47 | }; 48 | -------------------------------------------------------------------------------- /rollup-cli/src/actions/onchain/utils.js: -------------------------------------------------------------------------------- 1 | const ethers = require('ethers'); 2 | const { Scalar } = require('ffjavascript'); 3 | 4 | /** 5 | * Get current average gas price from the last ethereum blocks and multiply it 6 | * @param {Number} multiplier - multiply the average gas price by this parameter 7 | * @param {Object} provider - ethereum provider object 8 | * @returns {Promise} - promise will return the gas price obtained. 9 | */ 10 | async function getGasPrice(multiplier, provider) { 11 | const strAvgGas = await provider.getGasPrice(); 12 | const avgGas = Scalar.e(strAvgGas); 13 | const res = (avgGas * Scalar.e(multiplier)); 14 | const retValue = await ethers.utils.bigNumberify(res.toString()); 15 | return retValue; 16 | } 17 | 18 | module.exports = { 19 | getGasPrice, 20 | }; 21 | -------------------------------------------------------------------------------- /rollup-cli/src/actions/onchain/withdraw.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-restricted-syntax */ 2 | const ethers = require('ethers'); 3 | 4 | const CliExternalOperator = require('../../../../rollup-operator/src/cli-external-operator'); 5 | const { getGasPrice } = require('./utils'); 6 | 7 | /** 8 | * withdraw on-chain transaction to get retrieve the users balance from exit tree 9 | * before this call an off-chain transaction must be done to Id 0 or an onchain forceWithdraw 10 | * that transactions will build a leaf on exit tree 11 | * @param {String} nodeEth - URL of the ethereum node 12 | * @param {String} addressSC - rollup address 13 | * @param {Number} tokenId - token type identifier 14 | * @param {Object} walletRollup - ethAddress and babyPubKey together 15 | * @param {String} abi - abi of rollup contract' 16 | * @param {String} urlOperator - URl from operator 17 | * @param {String} numExitRoot - exit tree root depth to look for exit tree account 18 | * @param {Number} gasLimit - transaction gas limit 19 | * @param {Number} gasMultiplier - multiply gas price 20 | * @returns {Promise} - promise will resolve when the Tx is sent, and return the Tx itself with the Tx Hash. 21 | */ 22 | async function withdraw(nodeEth, addressSC, tokenId, walletRollup, abi, urlOperator, 23 | numExitRoot, gasLimit = 5000000, gasMultiplier = 1) { 24 | const walletBaby = walletRollup.babyjubWallet; 25 | const pubKeyBabyjub = [walletBaby.publicKey[0].toString(16), walletBaby.publicKey[1].toString(16)]; 26 | const pubKeyBabyjubEthCall = [walletBaby.publicKey[0].toString(), walletBaby.publicKey[1].toString()]; 27 | 28 | let walletEth = walletRollup.ethWallet.wallet; 29 | const provider = new ethers.providers.JsonRpcProvider(nodeEth); 30 | walletEth = walletEth.connect(provider); 31 | const contractWithSigner = new ethers.Contract(addressSC, abi, walletEth); 32 | 33 | const apiOperator = new CliExternalOperator(urlOperator); 34 | 35 | const overrides = { 36 | gasLimit, 37 | gasPrice: await getGasPrice(gasMultiplier, provider), 38 | }; 39 | 40 | try { 41 | const res = await apiOperator.getExitInfo(tokenId, pubKeyBabyjub[0], pubKeyBabyjub[1], numExitRoot); 42 | const infoExitTree = res.data; 43 | if (infoExitTree.found) { 44 | return await contractWithSigner.withdraw(infoExitTree.state.amount, numExitRoot, 45 | infoExitTree.siblings, pubKeyBabyjubEthCall, tokenId, overrides); 46 | } 47 | throw new Error(`No exit tree leaf was found in batch: ${numExitRoot} with babyjub: ${pubKeyBabyjub}`); 48 | } catch (error) { 49 | throw new Error(`Message error: ${error.message}`); 50 | } 51 | } 52 | 53 | module.exports = { 54 | withdraw, 55 | }; 56 | -------------------------------------------------------------------------------- /rollup-cli/src/utils/db.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Database interface for memory allocation 3 | */ 4 | class MemorydB { 5 | /** 6 | * @param {Bool} prefix - Database prefix 'i3db-' added to key values 7 | */ 8 | constructor() { 9 | this.prefix = 'db-'; 10 | this.db = new Map(); 11 | } 12 | 13 | /** 14 | * new database from object 15 | * @param {Object} dbObj - database as object representation 16 | * @return {Object} - MemorydB class object 17 | */ 18 | static newFromObj(dbObj) { 19 | const db = new MemorydB(); 20 | Object.keys(dbObj).forEach((key) => { 21 | db.insert(key.replace(db.prefix, ''), dbObj[key]); 22 | }); 23 | return db; 24 | } 25 | 26 | /** 27 | * Method to store [key - value] on database 28 | * @param {String} key 29 | * @param {String} value 30 | */ 31 | insert(key, value) { 32 | this.db.set(this.prefix + key, value); 33 | } 34 | 35 | /** 36 | * Method to retrieve a value given a key 37 | * @param {String} key 38 | * @returns {String} 39 | */ 40 | get(key) { 41 | const value = this.db.get(this.prefix + key); 42 | if (value === undefined) { return null; } 43 | return value; 44 | } 45 | 46 | /** 47 | * Method to retrieve a value given a key 48 | * @param {String} key 49 | * @returns {String} 50 | */ 51 | listKeys(prefix) { 52 | const keyList = []; 53 | this.db.forEach((value, key) => { 54 | if (key.indexOf(this.prefix + prefix) !== -1) { 55 | keyList.push(key.replace(this.prefix, '')); 56 | } 57 | }); 58 | return keyList; 59 | } 60 | 61 | /** 62 | * Method to delete a value given a key 63 | * @param {String} key 64 | */ 65 | delete(key) { 66 | this.db.delete(this.prefix + key); 67 | } 68 | 69 | /** 70 | * Method to delete all the [key - value] items 71 | */ 72 | deleteAll() { 73 | this.db.clear(); 74 | } 75 | 76 | /** 77 | * export MemoryDb into an object 78 | * @return {Object} - Database as object representation 79 | */ 80 | exportObj() { 81 | const obj = {}; 82 | this.db.forEach((value, key) => { 83 | obj[key] = value; 84 | }); 85 | return obj; 86 | } 87 | } 88 | 89 | module.exports = MemorydB; 90 | -------------------------------------------------------------------------------- /rollup-cli/src/utils/ethereum-wallet.js: -------------------------------------------------------------------------------- 1 | const ethers = require('ethers'); 2 | 3 | class EthereumWallet { 4 | /** 5 | * Initialization Ethereum Wallet 6 | * @param {Object} wallet 7 | */ 8 | constructor(wallet) { 9 | this.wallet = wallet; 10 | 11 | this.mnemonic = wallet.mnemonic; 12 | this.address = wallet.signingKey.address; 13 | this.privateKey = wallet.privateKey; 14 | this.publicKey = wallet.signingKey.publicKey; 15 | this.publicKeyCompressed = wallet.signingKey.keyPair.compressedPublicKey; 16 | } 17 | 18 | /** 19 | * Create a new wallet from mnemonic 20 | * @param {String} mnemonic 21 | * @returns {Object} wallet 22 | */ 23 | static fromMnemonic(mnemonic, index = 0) { 24 | const path = `m/44'/60'/0'/0/${index}`; 25 | return new EthereumWallet(ethers.Wallet.fromMnemonic(mnemonic, path)); 26 | } 27 | 28 | /** 29 | * Create a new random wallet 30 | * @returns {Object} wallet 31 | */ 32 | static createRandom() { 33 | return new EthereumWallet(ethers.Wallet.createRandom()); 34 | } 35 | 36 | /** 37 | * Create a new wallet from encrypted json 38 | * @param {Object} json - encrypted wallet 39 | * @param {String} pass - password 40 | * @returns {Object} wallet 41 | */ 42 | static async fromEncryptedJson(json, pass) { 43 | return new EthereumWallet(await ethers.Wallet.fromEncryptedJson(json, pass)); 44 | } 45 | 46 | /** 47 | * To sign message 48 | * @param {String} messageStr - message to sign 49 | * @returns {String} signature 50 | */ 51 | signMessage(messageStr) { 52 | return this.wallet.signMessage(messageStr); 53 | } 54 | 55 | /** 56 | * To encrypt wallet 57 | * @param {String} pass - password 58 | * @returns {Object} encrypt wallet 59 | */ 60 | toEncryptedJson(pass) { 61 | return this.wallet.encrypt(pass); 62 | } 63 | } 64 | 65 | /** 66 | * To verify ethereum signature 67 | * @param {String} publicKey 68 | * @param {String} messStr 69 | * @param {String} signatureHex 70 | * @returns {Boolean} 71 | */ 72 | function verifyEthereum(publicKey, messStr, signatureHex) { 73 | const address = ethers.utils.verifyMessage(messStr, signatureHex); 74 | return address === ethers.utils.computeAddress(publicKey); 75 | } 76 | 77 | module.exports = { 78 | EthereumWallet, 79 | verifyEthereum, 80 | }; 81 | -------------------------------------------------------------------------------- /rollup-cli/test/actions/send.test.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const { expect } = require('chai'); 3 | const path = require('path'); 4 | 5 | const tableFee = require('../../../js/constants').fee; 6 | const { send } = require('../../src/actions/offchain/send.js'); 7 | const { createWallet, deleteResources } = require('../integration-test/config/build-resources'); 8 | const { Wallet } = require('../../src/utils/wallet'); 9 | 10 | const walletPathDefault = path.join(__dirname, '../integration-test/resources/wallet-test.json'); 11 | 12 | describe('Send', async function () { 13 | this.timeout(10000); 14 | const UrlOperator = 'http://127.0.0.1:9000'; 15 | const babyjubTo = ['0x12', '0x23']; 16 | const amount = 10; 17 | let wallet; 18 | const password = 'foo'; 19 | const tokenId = 0; 20 | const fee = tableFee['10%']; 21 | 22 | before(async () => { 23 | await createWallet(); 24 | wallet = JSON.parse(await fs.readFileSync(walletPathDefault, 'utf8')); 25 | }); 26 | 27 | it('Should call send', async () => { 28 | const walletRollup = await Wallet.fromEncryptedJson(wallet, password); 29 | const res = await send(UrlOperator, babyjubTo, amount, walletRollup, tokenId, fee); 30 | expect(res.status).to.be.equal(200); 31 | }); 32 | 33 | after(async () => { 34 | await deleteResources(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /rollup-cli/test/helpers/helpers.js: -------------------------------------------------------------------------------- 1 | 2 | /* global expect */ 3 | /* eslint-disable require-atomic-updates */ 4 | 5 | const { Scalar } = require('ffjavascript'); 6 | const { buildPublicInputsSm, manageEvent } = require('../../../rollup-operator/src/utils'); 7 | 8 | const proofA = ['0', '0']; 9 | const proofB = [['0', '0'], ['0', '0']]; 10 | const proofC = ['0', '0']; 11 | 12 | 13 | function buildFullInputSm(bb, beneficiary) { 14 | const input = buildPublicInputsSm(bb); 15 | return { 16 | beneficiary, 17 | proofA, 18 | proofB, 19 | proofC, 20 | input, 21 | }; 22 | } 23 | 24 | class ForgerTest { 25 | constructor(rollupDB, maxTx, nLevels, beneficiary, insRollupTest) { 26 | this.rollupDB = rollupDB; 27 | this.maxTx = maxTx; 28 | this.nLevels = nLevels; 29 | this.beneficiary = beneficiary; 30 | this.insRollupTest = insRollupTest; 31 | } 32 | 33 | async forgeBatch(events = undefined, compressedOnChainTx = []) { 34 | const batch = await this.rollupDB.buildBatch(this.maxTx, this.nLevels); 35 | 36 | if (events) { 37 | const addTxPromises = events.map(async (elem) => new Promise((resolve) => { 38 | const batchTx = manageEvent(elem); 39 | batch.addTx(batchTx); 40 | resolve(); 41 | })); 42 | await Promise.all(addTxPromises); 43 | } 44 | batch.addBeneficiaryAddress(this.beneficiary); 45 | 46 | await batch.build(); 47 | const inputSm = buildFullInputSm(batch, this.beneficiary); 48 | await this.insRollupTest.forgeBatch(inputSm.proofA, 49 | inputSm.proofB, inputSm.proofC, inputSm.input, compressedOnChainTx); 50 | await this.rollupDB.consolidate(batch); 51 | } 52 | 53 | checkBatchNumber(events) { 54 | events.forEach((elem) => { 55 | const eventBatch = Scalar.e(elem.args.batchNumber); 56 | expect(Scalar.add(eventBatch, 2)).to.be.equal(Scalar.e(this.rollupDB.lastBatch)); 57 | }); 58 | } 59 | } 60 | 61 | 62 | module.exports = { 63 | buildFullInputSm, 64 | ForgerTest, 65 | }; 66 | -------------------------------------------------------------------------------- /rollup-cli/test/integration-test/config/build-resources.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const fs = require('fs'); 3 | const fse = require('fs-extra'); 4 | const { Wallet } = require('../../../src/utils/wallet'); 5 | 6 | const resources = path.join(__dirname, '../resources'); 7 | const configTestPath = path.join(__dirname, '../resources/config-test.json'); 8 | const configPath = path.join(__dirname, '../../../config.json'); 9 | const abiRollupPath = path.join(__dirname, '../resources/rollupabi.json'); 10 | const walletPathDefault = path.join(__dirname, '../../../wallet.json'); 11 | const walletPath = path.join(__dirname, '../resources/wallet-test.json'); 12 | const walletEthPath = path.join(__dirname, '../resources/ethWallet.json'); 13 | const walletBabyjubPath = path.join(__dirname, '../resources/babyjubWallet.json'); 14 | const noncePath = path.join(__dirname, '../../../nonceJson.json'); 15 | 16 | const passphrase = 'foo'; 17 | 18 | async function createWallet() { 19 | if (!fs.existsSync(resources)) { 20 | await fs.mkdirSync(resources); 21 | } 22 | const wallet = await Wallet.createRandom(); 23 | const encWallet = await wallet.toEncryptedJson(passphrase); 24 | await fs.writeFileSync(walletPath, JSON.stringify(encWallet, null, 1), 'utf-8'); 25 | await fs.writeFileSync(walletPathDefault, JSON.stringify(encWallet, null, 1), 'utf-8'); 26 | await fs.writeFileSync(walletEthPath, JSON.stringify(encWallet.ethWallet, null, 1), 'utf-8'); 27 | await fs.writeFileSync(walletBabyjubPath, JSON.stringify(encWallet.babyjubWallet, null, 1), 'utf-8'); 28 | } 29 | 30 | async function createConfig(address, depositEthAddress) { 31 | if (!fs.existsSync(resources)) { 32 | await fs.mkdirSync(resources); 33 | } 34 | const actualConfig = { 35 | wallet: path.join(__dirname, '../resources/wallet-test.json'), 36 | urlOperator: 'http://127.0.0.1:9000', 37 | addressRollup: '', 38 | nodeEth: 'http://localhost:8545', 39 | abiRollupPath: path.join(__dirname, '../resources/rollupabi.json'), 40 | depositEthAddress: '', 41 | }; 42 | 43 | actualConfig.addressRollup = address; 44 | actualConfig.depositEthAddress = depositEthAddress; 45 | await fs.writeFileSync(configTestPath, JSON.stringify(actualConfig, null, 1), 'utf-8'); 46 | await fs.writeFileSync(configPath, JSON.stringify(actualConfig, null, 1), 'utf-8'); 47 | } 48 | 49 | async function createRollupAbi(abi) { 50 | if (!fs.existsSync(resources)) { 51 | await fs.mkdirSync(resources); 52 | } 53 | await fs.writeFileSync(abiRollupPath, JSON.stringify(abi, null, 1), 'utf-8'); 54 | } 55 | 56 | async function deleteResources() { 57 | if (fs.existsSync(resources)) { 58 | await fse.remove(resources); 59 | } 60 | if (fs.existsSync(walletPathDefault)) { 61 | await fs.unlinkSync(walletPathDefault); 62 | } 63 | if (fs.existsSync(configPath)) { 64 | await fs.unlinkSync(configPath); 65 | } 66 | if (fs.existsSync(noncePath)) { 67 | await fs.unlinkSync(noncePath); 68 | } 69 | } 70 | 71 | module.exports = { 72 | createWallet, 73 | createConfig, 74 | createRollupAbi, 75 | deleteResources, 76 | }; 77 | -------------------------------------------------------------------------------- /rollup-cli/test/integration-test/config/info.md: -------------------------------------------------------------------------------- 1 | # Test 2 | 3 | ### Ethereum Wallet test 4 | `npx mocha rollup-cli/test/utils/ethereum-wallet.test.js` 5 | 6 | ### Wallet test 7 | `npx mocha rollup-cli/test/utils/wallet.test.js` 8 | 9 | ### DB test 10 | `npx mocha rollup-cli/test/utils/db.test.js` 11 | 12 | ## TX Test 13 | Next test should have running dummy `operator` server in `http://127.0.0.1:9000` 14 | 15 | This server can be found in `rollup-cli/test/helpers/api-client.js` 16 | 17 | - Go to `rollup-cli` folder: 18 | - Run server by typing `npm run operator-dummy` 19 | 20 | ### Offchain TX test 21 | Run test by typing `npx mocha rollup-cli/test/actions/send.test.js` 22 | 23 | ### Onchain TX test 24 | Run ganache testnet: `ganache-cli` 25 | 26 | Run onchain TX test by typing: `npx truffle test rollup-cli/test/actions/onchain.test.js` 27 | 28 | ### CLI Test 29 | Run ganache testnet: `ganache-cli` 30 | 31 | This command will create all the needed configuration to trigger test 32 | - Run `npx truffle test rollup-cli/test/integration-test/config/setup-cli.test.js` 33 | 34 | - Run test: 35 | Go to `rollup-cli` folder: 36 | - Run `npx mocha test/integration-test/cli.test.js` 37 | -------------------------------------------------------------------------------- /rollup-cli/test/utils/db.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const Db = require('../../src/utils/db'); 3 | 4 | const { expect } = chai; 5 | 6 | describe('[memory database]', () => { 7 | let dataBase; 8 | 9 | it('Check databse key - values', () => { 10 | dataBase = new Db(); 11 | for (let i = 0; i < 10; i++) { 12 | const key = `key-${i}`; 13 | const value = `value-${i}`; 14 | dataBase.insert(key, value); 15 | } 16 | 17 | for (let i = 0; i < 10; i++) { 18 | const key = `key-${i}`; 19 | const value = dataBase.get(key); 20 | expect(value).to.be.equal(`value-${i}`); 21 | } 22 | }); 23 | 24 | it('export and import database', () => { 25 | const dbObj = dataBase.exportObj(); 26 | 27 | const newdB = Db.newFromObj(dbObj); 28 | for (let i = 0; i < 10; i++) { 29 | const key = `key-${i}`; 30 | const valueOld = dataBase.get(key); 31 | const valueNew = newdB.get(key); 32 | expect(valueOld).to.be.equal(valueNew); 33 | } 34 | }); 35 | 36 | it('Clear single key', () => { 37 | const singleKey = 'key-3'; 38 | dataBase.delete(singleKey); 39 | const value = dataBase.get(singleKey); 40 | expect(value).to.be.equal(null); 41 | }); 42 | 43 | it('Clear full database', () => { 44 | dataBase.deleteAll(); 45 | for (let i = 0; i < 10; i++) { 46 | const key = `key-${i}`; 47 | const value = dataBase.get(key); 48 | expect(value).to.be.equal(null); 49 | } 50 | }); 51 | }); 52 | -------------------------------------------------------------------------------- /rollup-cli/test/utils/ethereum-wallet.test.js: -------------------------------------------------------------------------------- 1 | const chai = require('chai'); 2 | const { EthereumWallet, verifyEthereum } = require('../../src/utils/ethereum-wallet'); 3 | 4 | const { expect } = chai; 5 | 6 | describe('Ethereum wallet', function () { 7 | this.timeout(10000); 8 | const mnemonic = 'maximum direct solution mushroom before meat public bean board frown announce lawn'; 9 | const privTest = '0x1f07e59e1c8f9406a89d461fcc6d1044485c5960ba7fe123c67a8b0bbf115524'; 10 | const pass = 'passphrase'; 11 | 12 | it('from mnemonic', () => { 13 | const wallet = EthereumWallet.fromMnemonic(mnemonic); 14 | expect(wallet.privateKey).to.be.equal(privTest); 15 | }); 16 | 17 | it('from random', () => { 18 | const wallet = EthereumWallet.createRandom(); 19 | expect(wallet.mnemonic).to.not.be.equal(undefined); 20 | expect(wallet.privateKey).to.not.be.equal(undefined); 21 | expect(wallet.publicKey).to.not.be.equal(undefined); 22 | expect(wallet.address).to.not.be.equal(undefined); 23 | expect(wallet.publicKeyCompressed).to.not.be.equal(undefined); 24 | }); 25 | 26 | it('from-to json', async () => { 27 | const wallet0 = EthereumWallet.fromMnemonic(mnemonic); 28 | const priv0 = wallet0.privateKey; 29 | const json = await wallet0.toEncryptedJson(pass); 30 | // import walllet from json generated 31 | const wallet1 = await EthereumWallet.fromEncryptedJson(json, pass); 32 | const priv1 = wallet1.privateKey; 33 | expect(priv0).to.be.equal(priv1); 34 | // import walllet from json generated with invalid passphrase 35 | const passInvalid = 'passInvalid'; 36 | try { 37 | await EthereumWallet.fromEncryptedJson(json, passInvalid); 38 | } catch (error) { 39 | expect((error.message).includes('invalid password')).to.be.equal(true); 40 | } 41 | }); 42 | 43 | it('sign-verify message', async () => { 44 | const wallet = EthereumWallet.fromMnemonic(mnemonic); 45 | const msg = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, ' 46 | + 'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'; 47 | const signatureHex = await wallet.signMessage(msg); 48 | let verify = await verifyEthereum(wallet.publicKey, msg, signatureHex); 49 | expect(verify).to.be.equal(true); 50 | verify = await verifyEthereum(wallet.publicKey, `${msg}Invalid`, signatureHex); 51 | expect(verify).to.be.equal(false); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /rollup-cli/tools/gunners/checkRollupOnChain.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | const ethers = require('ethers'); 3 | const fs = require('fs'); 4 | const path = require('path'); 5 | const { Scalar } = require('ffjavascript'); 6 | 7 | const urlNodeEth = 'https://goerli.infura.io/v3/135e56bb9eaa42c59e73481fcb0f9b4a'; 8 | const provider = new ethers.providers.JsonRpcProvider(urlNodeEth); 9 | 10 | const configParamsPath = path.join(__dirname, 'config/params.json'); 11 | 12 | 13 | const actualConfig = JSON.parse(fs.readFileSync(configParamsPath, 'utf8')); 14 | 15 | const contractRollup = new ethers.Contract(actualConfig.rollupAddress, actualConfig.abiRollup, provider); 16 | 17 | 18 | async function checkRollup() { 19 | const getStateDepth = await contractRollup.getStateDepth(); 20 | const stateDepthScalar = Scalar.e(getStateDepth._hex); 21 | console.log({ stateDepthScalar }); 22 | 23 | const fillingInfo = await contractRollup.fillingMap(stateDepthScalar.toString()); 24 | const currenOnchanTx = Scalar.e(fillingInfo.currentOnChainTx._hex); 25 | console.log({ currenOnchanTx }); 26 | 27 | 28 | const fillingInfoPlusOne = await contractRollup.fillingMap((Scalar.add(stateDepthScalar, 1)).toString()); 29 | const currenOnchanTxPlusOne = Scalar.e(fillingInfoPlusOne.currentOnChainTx._hex); 30 | console.log({ currenOnchanTxPlusOne }); 31 | 32 | const fillingInfoPlusTwo = await contractRollup.fillingMap((Scalar.add(stateDepthScalar, 2)).toString()); 33 | const currenOnchanTxPlusTwo = Scalar.e(fillingInfoPlusTwo.currentOnChainTx._hex); 34 | console.log({ currenOnchanTxPlusTwo }); 35 | } 36 | checkRollup(); 37 | -------------------------------------------------------------------------------- /rollup-cli/tools/gunners/depositGunner.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-await-in-loop */ 2 | /* eslint-disable no-console */ 3 | // Constant variables 4 | const fs = require('fs'); 5 | const ethers = require('ethers'); 6 | const path = require('path'); 7 | const { Scalar } = require('ffjavascript'); 8 | const { Wallet } = require('../../src/utils/wallet'); 9 | const { createWallets, walletsDeposit } = require('./utils'); 10 | 11 | const configParamsPath = path.join(__dirname, 'config/params.json'); 12 | const walletPath = path.join(__dirname, 'config/wallet.json'); 13 | 14 | // Argument variables 15 | const pass = 'password'; 16 | const urlNodeEth = 'https://goerli.infura.io/v3/135e56bb9eaa42c59e73481fcb0f9b4a'; 17 | const addressTokens = '0xaff4481d10270f50f203e0763e2597776068cbc5'; 18 | const actualConfig = JSON.parse(fs.readFileSync(configParamsPath, 'utf8')); 19 | const { abiRollup } = actualConfig; 20 | const pathWallets = path.join(__dirname, './config/wallets'); 21 | const { mnenonic } = actualConfig; 22 | const tokenId = 0; 23 | 24 | const numTx = 1; 25 | const numWallets = 1; 26 | const etherFund = 0.1; 27 | const tokensFund = Scalar.e(1e+19); 28 | 29 | async function gunOnChainTx() { 30 | const wallet = JSON.parse(fs.readFileSync(walletPath, 'utf-8')); 31 | const walletRollup = await Wallet.fromEncryptedJson(wallet, pass); 32 | 33 | let walletEthFunder = walletRollup.ethWallet.wallet; 34 | 35 | const provider = new ethers.providers.JsonRpcProvider(urlNodeEth); 36 | walletEthFunder = walletEthFunder.connect(provider); 37 | 38 | await createWallets(numWallets, Scalar.mul(tokensFund, numTx), pass, actualConfig.rollupAddress, walletEthFunder, etherFund, 39 | addressTokens, actualConfig.abiTokens, urlNodeEth, pathWallets, mnenonic, 0); 40 | await walletsDeposit(tokensFund, pass, actualConfig.rollupAddress, abiRollup, urlNodeEth, tokenId, pathWallets, numTx); 41 | } 42 | gunOnChainTx(); 43 | -------------------------------------------------------------------------------- /rollup-cli/tools/gunners/info.md: -------------------------------------------------------------------------------- 1 | # Gunners 2 | 3 | Automatic creation of deposits and off-chain transactions in order to stress the operator and get performance measures. 4 | 5 | # Config files: 6 | 7 | In order to use it are needed 2 config files 8 | 9 | - `params.json` with the following content: 10 | * `rollupAddress` : addres of the rollup smart contract 11 | * `abiRollup`: abi of the rollup 12 | * `abiTokens`: abi of ERC20 token 13 | * `mnenonic` : mnemonic wich the wallets will be created 14 | - `wallet.json` A rollup wallet, an easy way to create one is using the CLI. 15 | 16 | Change the `Argument variables` at the start of both files before and just execute them with node. 17 | 18 | 19 | -------------------------------------------------------------------------------- /rollup-cli/tools/gunners/sendGunner.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | /* eslint-disable no-await-in-loop */ 3 | 4 | // const variables 5 | const fs = require('fs'); 6 | const { stringifyBigInts } = require('ffjavascript').utils; 7 | const path = require('path'); 8 | 9 | const feeTable = require('../../../js/constants').fee; 10 | const { Wallet } = require('../../src/utils/wallet'); 11 | const CliExternalOperator = require('../../../rollup-operator/src/cli-external-operator'); 12 | 13 | 14 | // Argument variables 15 | const pass = 'password'; 16 | const jsonWalletFrom = fs.readFileSync(path.join(__dirname, 'config/wallet.json'), 'utf-8'); 17 | const jsonWalletTo = fs.readFileSync(path.join(__dirname, './config/wallets/2wallet.json')); 18 | const apiOperator = new CliExternalOperator('http://localhost:9000'); 19 | const numTx = 500; 20 | const fee = feeTable['50%']; 21 | const amount = 2; 22 | const coin = 0; 23 | 24 | 25 | function timeout(ms) { 26 | return new Promise((resolve) => setTimeout(resolve, ms)); 27 | } 28 | 29 | 30 | async function send() { 31 | const walletFrom = await Wallet.fromEncryptedJson(JSON.parse(jsonWalletFrom), pass); 32 | const walletBaby = walletFrom.babyjubWallet; 33 | const fromLeaf = await apiOperator.getStateAccount(coin, walletBaby.publicKey[0].toString(16), walletBaby.publicKey[1].toString(16)); 34 | const nonceToSend = fromLeaf.data.nonce; 35 | console.log(nonceToSend); 36 | 37 | const walletTo = await Wallet.fromEncryptedJson(JSON.parse(jsonWalletTo), pass); 38 | const toAx = walletTo.babyjubWallet.publicKey[0].toString(16); 39 | const toAy = walletTo.babyjubWallet.publicKey[1].toString(16); 40 | const toLeaf = await apiOperator.getStateAccount(coin, toAx, toAy); 41 | const toEthAddr = toLeaf.data.ethAddress; 42 | 43 | console.log('from Wallet: '); 44 | console.log(`http://localhost:9000/accounts/${walletFrom.babyjubWallet.publicKey[0].toString(16)}/${walletFrom.babyjubWallet.publicKey[1].toString(16)}/${coin}`); 45 | console.log('to Wallet: '); 46 | console.log(`http://localhost:9000/accounts/${walletTo.babyjubWallet.publicKey[0].toString(16)}/${walletTo.babyjubWallet.publicKey[1].toString(16)}/${coin}`); 47 | 48 | for (let i = 0; i < numTx; i++) { 49 | const tx = { 50 | toAx, 51 | toAy, 52 | toEthAddr, 53 | coin, 54 | amount, 55 | nonce: nonceToSend + i, 56 | fee, 57 | rqOffset: 0, 58 | onChain: 0, 59 | newAccount: 0, 60 | }; 61 | await walletFrom.signRollupTx(tx); // sign included in transaction 62 | const parseTx = stringifyBigInts(tx);// convert bigint to Strings 63 | 64 | apiOperator.sendTx(parseTx).then((resTx) => { 65 | if (resTx.status.toString() === '200') { 66 | console.log('correct!'); 67 | } else { 68 | console.log(resTx.response.data); 69 | } 70 | }).catch((error) => { 71 | console.log(error.message); 72 | }); 73 | console.log('send!'); 74 | await timeout(0); 75 | } 76 | } 77 | 78 | send(); 79 | -------------------------------------------------------------------------------- /rollup-cli/tools/slasher.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-await-in-loop */ 2 | /* eslint-disable no-console */ 3 | const ethers = require('ethers'); 4 | const { Wallet } = require('../src/wallet'); 5 | 6 | const TIMEOUT_ERROR = 2000; 7 | const TIMEOUT_NEXT_LOOP = 10000; 8 | 9 | async function slashSC(urlNode, addressSC, walletJson, password, abi, oldCurrentSlot) { 10 | // eslint-disable-next-line no-constant-condition 11 | while (true) { 12 | try { 13 | const walletRollup = await Wallet.fromEncryptedJson(walletJson, password); 14 | let walletEth = walletRollup.ethWallet.wallet; 15 | const provider = new ethers.providers.JsonRpcProvider(urlNode); 16 | walletEth = walletEth.connect(provider); 17 | const contract = new ethers.Contract(addressSC, abi, walletEth); 18 | const currentSlot = await contract.currentSlot(); 19 | const slots = []; 20 | for (let i = oldCurrentSlot; i < currentSlot; i++) { 21 | slots.push(i); 22 | } 23 | slots.forEach((slot) => { 24 | console.log(`SLASH SLOT: ${slot}`); 25 | contract.slash(slot).then((response) => { 26 | console.log(response); 27 | }).catch((error) => { 28 | console.log(`ERROR: ${error}`); 29 | setTimeout(slashSC, TIMEOUT_ERROR, urlNode, addressSC, walletJson, password, abi, oldCurrentSlot); 30 | }); 31 | }); 32 | setTimeout(slashSC, TIMEOUT_NEXT_LOOP, urlNode, addressSC, walletJson, password, abi, currentSlot); 33 | } catch (error) { 34 | console.log('ERROR: ', error); 35 | setTimeout(slashSC, TIMEOUT_ERROR, urlNode, addressSC, walletJson, password, abi, oldCurrentSlot); 36 | } 37 | } 38 | } 39 | 40 | module.exports = { slashSC }; 41 | -------------------------------------------------------------------------------- /rollup-cli/tools/slasher.test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-underscore-dangle */ 2 | /* eslint-disable no-await-in-loop */ 3 | /* eslint-disable consistent-return */ 4 | /* global artifacts */ 5 | /* global contract */ 6 | /* global web3 */ 7 | 8 | // const chai = require("chai"); 9 | // const { expect } = chai; 10 | // ganache-cli --mnemonic "vivid bitter wealth early teach village shoot tide beauty universe green vanish" 11 | 12 | const fs = require('fs'); 13 | const poseidonUnit = require('circomlib/src/poseidon_gencontract'); 14 | const { slashSC } = require('./slasher'); 15 | const timeTravel = require('../../test/contracts/helpers/timeTravel'); 16 | 17 | const Verifier = artifacts.require('../../../../contracts/test/VerifierHelper'); 18 | const RollupTest = artifacts.require('../../../../contracts/test/RollupTest'); 19 | const RollupPoS = artifacts.require('../../../../contracts/test/RollupPoS'); 20 | const { timeout } = require('../../rollup-operator/src/utils'); 21 | 22 | 23 | contract('RollupPoS', async (accounts) => { 24 | let insPoseidonUnit; 25 | let insRollupTest; 26 | let insVerifier; 27 | let insRollupPoS; 28 | 29 | const urlNode = 'http://localhost:8545'; 30 | let addressSC; 31 | const walletPath = './walletSlash.json'; 32 | const pass = 'foo'; 33 | let abi; 34 | let walletJson; 35 | const maxTx = 10; 36 | const maxOnChainTx = 10; 37 | const { 38 | 0: owner, 39 | } = accounts; 40 | 41 | 42 | before(async () => { 43 | // Deploy poseidon 44 | const C = new web3.eth.Contract(poseidonUnit.abi); 45 | insPoseidonUnit = await C.deploy({ data: poseidonUnit.createCode() }) 46 | .send({ gas: 2500000, from: owner }); 47 | 48 | // Deploy Verifier 49 | insVerifier = await Verifier.new(); 50 | 51 | // Deploy Rollup test 52 | insRollupTest = await RollupTest.new(insVerifier.address, insPoseidonUnit._address, 53 | maxTx, maxOnChainTx); 54 | 55 | insRollupPoS = await RollupPoS.new(insRollupTest.address, maxTx); 56 | 57 | addressSC = insRollupPoS.contract._address; 58 | abi = insRollupPoS.abi; 59 | walletJson = JSON.parse(fs.readFileSync(walletPath, 'utf8')); 60 | }); 61 | it('slash', async () => { 62 | await timeTravel.addBlocks(1011); 63 | 64 | slashSC(urlNode, addressSC, walletJson, pass, abi, 0); 65 | await timeout(30000); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /rollup-operator/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "es6": true, 4 | "node": true, 5 | "mocha": true 6 | }, 7 | "parserOptions": { 8 | "ecmaVersion": 2017 9 | }, 10 | "extends": "eslint:recommended", 11 | "rules": { 12 | "indent": [ 13 | "error", 14 | 4 15 | ], 16 | "linebreak-style": [ 17 | "error", 18 | "unix" 19 | ], 20 | "quotes": [ 21 | "error", 22 | "double" 23 | ], 24 | "semi": [ 25 | "error", 26 | "always" 27 | ] 28 | } 29 | }; 30 | -------------------------------------------------------------------------------- /rollup-operator/.gitignore: -------------------------------------------------------------------------------- 1 | getIden3.sh 2 | node_modules 3 | test/config/*-test.json 4 | test/config/test.json 5 | test/config/test-pob.json 6 | test/server/proof-of-stake/tmp-* 7 | test/server/proof-of-burn/tmp-* 8 | test/synch-pool-service/config/*-test.json 9 | -------------------------------------------------------------------------------- /rollup-operator/index.js: -------------------------------------------------------------------------------- 1 | const cliExternalOperator = require("./src/cli-external-operator"); 2 | 3 | module.exports = { 4 | cliExternalOperator, 5 | }; -------------------------------------------------------------------------------- /rollup-operator/src/cli-proof-server.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | 3 | /** 4 | * Client to interact with server-proof 5 | */ 6 | class CliProofServer { 7 | 8 | /** 9 | * Initialize client 10 | * @param {String} url - server proof url 11 | */ 12 | constructor(url) { 13 | this.url = url; 14 | } 15 | 16 | /** 17 | * Get server proof status 18 | * @returns {Object} - http response 19 | */ 20 | async getStatus() { 21 | return axios.get(`${this.url}/status`); 22 | } 23 | 24 | /** 25 | * Send zkSnark inputs 26 | * @param {Object} input - zkSnark inputs 27 | * @returns {Object} - http response 28 | */ 29 | async setInput(input) { 30 | return axios.post(`${this.url}/input`, input); 31 | } 32 | 33 | /** 34 | * Send cancel action 35 | * @returns {Object} - http response 36 | */ 37 | async cancel() { 38 | return axios.post(`${this.url}/cancel`); 39 | } 40 | } 41 | 42 | module.exports = CliProofServer; -------------------------------------------------------------------------------- /rollup-operator/src/constants.js: -------------------------------------------------------------------------------- 1 | const poseidon = require("circomlib").poseidon; 2 | const utils = require("ffjavascript").utils; 3 | 4 | function string2Int(str) { 5 | return utils.leBuff2int(Buffer.from(str)); 6 | } 7 | const hash = poseidon.createHash(1, 8, 57); 8 | 9 | /** 10 | * List of keys to use in synchronizer database 11 | */ 12 | const DB_SYNCH_BATCH_INFO = hash([string2Int("DB_SYNCH_BATCH_INFO")]); 13 | 14 | /** 15 | * Operator modes 16 | */ 17 | const mode = { 18 | light: 0, 19 | full: 1, 20 | archive: 2, 21 | }; 22 | 23 | module.exports = { 24 | mode, 25 | DB_SYNCH_BATCH_INFO, 26 | }; -------------------------------------------------------------------------------- /rollup-operator/src/synch-pool-service/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iden3/rollup/1048dfe09a9042362ee6c36eba4951bf63b3e062/rollup-operator/src/synch-pool-service/README.md -------------------------------------------------------------------------------- /rollup-operator/src/synch-pool-service/api-bitfinex.js: -------------------------------------------------------------------------------- 1 | const axios = require("axios"); 2 | 3 | const TICKER_PARAMS = { 4 | BID: 0, 5 | BID_SIZE: 1, 6 | ASK: 2, 7 | ASK_SIZE: 3, 8 | DAILY_CHANGE: 4, 9 | DAILY_CHANGE_RELATIVE: 5, 10 | LAST_PRICE: 6, 11 | VOLUME: 7, 12 | HIGH: 8, 13 | LOW: 9, 14 | }; 15 | 16 | /** 17 | * Client to interact with Bitfinex public API 18 | */ 19 | class ApiBitfinex { 20 | 21 | /** 22 | * Initialize client 23 | * @param {String} url - API url 24 | */ 25 | constructor(url="https://api-pub.bitfinex.com/v2/") { 26 | this.url = url; 27 | } 28 | 29 | /** 30 | * Get last price for a given market 'currency vs base' 31 | * @param {String} currency - currency to get data from 32 | * @param {String} base - base market 33 | * @returns {Number} last price on market 'currency vs base' 34 | */ 35 | async getTokenLastPrice(currency, base) { 36 | const pathParams = `ticker/t${currency}${base}`; 37 | const res = await axios.get(`${this.url}/${pathParams}`); 38 | return res.data[TICKER_PARAMS.LAST_PRICE]; 39 | } 40 | 41 | /** 42 | * Gets list of valid exchange trading pairs 43 | * @returns {Array} All current trading pairs 44 | */ 45 | async getTraddingPairs() { 46 | const res = await axios.get(`${this.url}/conf/pub:list:pair:exchange`); 47 | return res.data[0]; 48 | } 49 | } 50 | 51 | module.exports = ApiBitfinex; 52 | -------------------------------------------------------------------------------- /rollup-operator/src/synch-pool-service/run-synch-pool.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const chalk = require("chalk"); 3 | const winston = require("winston"); 4 | const rmRf = require("rimraf"); 5 | 6 | const MemDb = require("../../../rollup-utils/mem-db"); 7 | const LevelDb = require("../../../rollup-utils/level-db"); 8 | const synchService = require("./synch-pool-service"); 9 | 10 | const { argv } = require("yargs") 11 | .usage(` 12 | synch-pool-service 13 | 14 | options 15 | ======= 16 | synch-pool-service 17 | start synch-pool-service 18 | 19 | --clear [true | false] 20 | Erase persistent database 21 | Default: false 22 | `) 23 | .epilogue("Synchronize pool service"); 24 | 25 | // Global vars 26 | let poolSynch; 27 | let logger; 28 | 29 | // Log vars 30 | const infoInit = `${chalk.bgCyan.black("LOADING")} ==> `; 31 | 32 | (async () => { 33 | let info; 34 | // load environment data 35 | const pathEnvironmentFile = `${__dirname}/config.env`; 36 | require("dotenv").config({ path: pathEnvironmentFile }); 37 | 38 | // load synch pool service configuration file 39 | let config; 40 | if (process.env.CONFIG_PATH) { 41 | config = JSON.parse(fs.readFileSync(process.env.CONFIG_PATH, "utf8")); 42 | } else { 43 | config = JSON.parse(fs.readFileSync("./synch-pool-config.json", "utf8")); 44 | } 45 | 46 | // config winston 47 | const loggerLevel = process.env.LOG_LEVEL; 48 | 49 | var options = { 50 | console: { 51 | level: loggerLevel, 52 | format: winston.format.combine( 53 | winston.format.colorize(), 54 | winston.format.simple(), 55 | ) 56 | }, 57 | }; 58 | 59 | logger = winston.createLogger({ 60 | transports: [ 61 | new winston.transports.Console(options.console) 62 | ] 63 | }); 64 | 65 | // delete database folders if `--clear true` 66 | const clearFlag = (argv.clear === "true") ? true : false; 67 | 68 | if (clearFlag){ 69 | if (config.pathDb) 70 | rmRf.sync(config.pathDb); 71 | } 72 | 73 | /////////////////// 74 | ///// INIT DATABASE 75 | /////////////////// 76 | let db; 77 | 78 | if (config.pathDb == undefined){ 79 | info = infoInit; 80 | info += chalk.white.bold("memory database"); 81 | logger.info(info); 82 | db = new MemDb(); 83 | } else { 84 | info = infoInit; 85 | info += chalk.white.bold("levelDb database"); 86 | logger.info(info); 87 | db = new LevelDb(config.pathDb); 88 | } 89 | 90 | //////////////////// 91 | ///// SYNCH SERVICE 92 | /////////////////// 93 | poolSynch = new synchService( 94 | db, 95 | config.ethNodeUrl, 96 | config.ethAddress, 97 | config.rollupAddress, 98 | config.rollupAbi, 99 | config.logLevel, 100 | config.pathConversionTable, 101 | config.pathCustomTokens, 102 | config.timeouts, 103 | ); 104 | poolSynch.synchLoop(); 105 | })(); -------------------------------------------------------------------------------- /rollup-operator/test/config/config.env-example: -------------------------------------------------------------------------------- 1 | WALLET_PATH = ${HOME}/rollup/rollup-operator/test/config/wallet-test.json 2 | CONFIG_SYNCH = ${HOME}/rollup/rollup-operator/test/config/synch-config-test.json 3 | CONFIG_POOL = ${HOME}/rollup/rollup-operator/test/config/pool-config-test.json 4 | EXPOSE_API_SERVER = true 5 | OPERATOR_PORT_EXTERNAL = 9000 6 | URL_SERVER_PROOF = http://127.0.0.1:10001 7 | LOG_LEVEL = info 8 | OPERATOR_MODE = archive 9 | GAS_MULTIPLIER = 1 10 | GAS_LIMIT = default 11 | LAN=true -------------------------------------------------------------------------------- /rollup-operator/test/config/custom-token-example.json: -------------------------------------------------------------------------------- 1 | {"0":{"price":20,"decimals":18}} -------------------------------------------------------------------------------- /rollup-operator/test/config/pool-config-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "maxSlots": 10, 3 | "maxDeposits":18, 4 | "executableSlots": 1, 5 | "nonExecutableSlots": 1, 6 | "timeout": 1000, 7 | "pathConversionTable": "/home/data/conversion-table.json" 8 | } -------------------------------------------------------------------------------- /rollup-operator/test/config/synch-config-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "rollup" : { 3 | "synchDb": "/home/synch-db", 4 | "treeDb": "/home/tree-db", 5 | "address": "0x4865811f5C899B249659096E06f26e00654507C3", 6 | "abi": "abi_object", 7 | "creationHash": "cab6144879d51312fd532604028d838ff6573f5e1dd5ef3691a183515ff37969", 8 | "timeouts": {"ERROR":1000,"NEXT_LOOP":2500,"LOGGER":5000} 9 | }, 10 | "rollupPoS" : { 11 | "synchDb": "/home/pos-db", 12 | "address": "0x769afbf9765f6ea956a9bc976aeba97867aeb087", 13 | "abi": "abi_object", 14 | "creationHash": "9876af65976bca97atcbcta9767976a9btctbc9a76tceebca976t2364afacb9a7c6", 15 | "timeouts": {"ERROR":1000,"NEXT_LOOP":2500,"LOGGER":5000} 16 | }, 17 | "ethNodeUrl": "ropsten.infura.io/v3/39753c9df15e15ee844e2c19a928bbc1", 18 | "ethAddressCaller": "0x100a38D3c4C77Aa8C38e5089208a6E4C992c5C6B" 19 | } -------------------------------------------------------------------------------- /rollup-operator/test/helpers/add-blocks.js: -------------------------------------------------------------------------------- 1 | /* global contract */ 2 | const { addBlocks } = require("../../../test/contracts/helpers/timeTravel"); 3 | 4 | const slotsPerEra = 20; 5 | const blockPerSlots = 100; 6 | 7 | const erasToAdd = 1; 8 | 9 | const blocksToAdd = erasToAdd*slotsPerEra*blockPerSlots; 10 | 11 | contract("Add blocks", () => { 12 | it("Should add blocks", async () => { 13 | await addBlocks(blocksToAdd); 14 | console.log(`Add ${blocksToAdd} blocks`); 15 | }); 16 | }); -------------------------------------------------------------------------------- /rollup-operator/test/server/helpers/utils-test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | const Scalar = require("ffjavascript").Scalar; 3 | 4 | function timeout(ms) { 5 | return new Promise(resolve => setTimeout(resolve, ms)); 6 | } 7 | 8 | async function assertForgeBatch(cliRollup, targetBatch, timeoutLoop){ 9 | let batchForged = false; 10 | let counter = 0; 11 | while(!batchForged && counter < 10) { 12 | const res = await cliRollup.getState(); 13 | const info = res.data; 14 | if (info.rollupSynch.lastBatchSynched > targetBatch) { 15 | batchForged = true; 16 | break; 17 | } 18 | await timeout(timeoutLoop); 19 | counter += 1; 20 | } 21 | expect(batchForged).to.be.equal(true); 22 | } 23 | 24 | async function assertBalances(cliRollup, rollupAccounts, arrayBalances){ 25 | const numAccounts = rollupAccounts.length; 26 | const coin = 0; 27 | 28 | for (let i = 0; i < numAccounts; i++){ 29 | if (arrayBalances[i] !== null){ 30 | const ax = rollupAccounts[i].babyjubWallet.publicKey[0].toString(16); 31 | const ay = rollupAccounts[i].babyjubWallet.publicKey[1].toString(16); 32 | 33 | const res = await cliRollup.getStateAccount(coin, ax, ay); 34 | expect(Scalar.eq(res.data.amount, arrayBalances[i])).to.be.equal(true); 35 | } 36 | } 37 | } 38 | 39 | module.exports = { 40 | assertForgeBatch, 41 | assertBalances, 42 | }; -------------------------------------------------------------------------------- /rollup-operator/test/server/proof-of-burn/create-config-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | path_wallet="$(pwd)/test/config/wallet-pob-test.json" 4 | path_synch="$(pwd)/test/config/synch-config-pob-test.json" 5 | path_pool="$(pwd)/test/config/pool-config-pob-test.json" 6 | path_env="$(pwd)/src/server/proof-of-burn/config.env" 7 | 8 | echo "WALLET_PATH = $path_wallet 9 | CONFIG_SYNCH = $path_synch 10 | CONFIG_POOL = $path_pool 11 | EXPOSE_API_SERVER = true 12 | OPERATOR_PORT_EXTERNAL = 9000 13 | URL_SERVER_PROOF = http://127.0.0.1:10001 14 | LOG_LEVEL = info 15 | OPERATOR_MODE = archive 16 | GAS_MULTIPLIER = 1 17 | GAS_LIMIT = default 18 | LAN=true 19 | PASSWORD=passTest" > $path_env -------------------------------------------------------------------------------- /rollup-operator/test/server/proof-of-burn/test-pob.md: -------------------------------------------------------------------------------- 1 | # Run server operator test 2 | Commands are called from repository `rollup` root directory 3 | 4 | Start local ethereum blockchain 5 | - `ganache-cli -a 100 --defaultBalanceEther 10000` 6 | 7 | Deploy contracts and build configuration files 8 | - Memory database 9 | `npx truffle test ./rollup-operator/test/server/proof-of-burn/build-configs-memDb-pob.test.js` 10 | - LevelDb database 11 | `npx truffle test ./rollup-operator/test/server/proof-of-burn/build-configs-levelDb-pob.test.js` 12 | 13 | Open new terminal and run `server-proof` server 14 | - `cd ./rollup-operator` 15 | - `npm run server-proof` 16 | 17 | Open new terminal and run `synch-pool-service` server 18 | - `cd ./rollup-operator` 19 | - `npm run service-synch-pool` 20 | - service needs configuration file `config.env` in its path 21 | - example can be found in `rollup/rollup-operator/test/synch-pool-service/config/config.env-example` 22 | - *It should be noted that this file should be where the `run-synch-pool.js` is invoked and its name should be `config.env` 23 | 24 | Open new terminal and run operator service 25 | - `cd ./rollup-operator` 26 | - `npm run test:operator-pob` 27 | - password could be typed on console or by adding an environment variable: 28 | - `PASSWORD=passTest` 29 | - operator needs configuration file `config.env` in its path 30 | - example can be found in `rollup/rollup-operator/test/config/config-example.env` 31 | - *It should be noted that this file should be where the `operator.js` is invoked and its name should be `config.env` 32 | 33 | Run test 34 | - `npx truffle test ./rollup-operator/test/server/proof-of-burn/operator-server-pob.test.js` 35 | -------------------------------------------------------------------------------- /rollup-operator/test/server/proof-of-stake/create-config-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | path_wallet="$(pwd)/test/config/wallet-test.json" 4 | path_synch="$(pwd)/test/config/synch-config-test.json" 5 | path_pool="$(pwd)/test/config/pool-config-test.json" 6 | path_env="$(pwd)/src/server/proof-of-stake/config.env" 7 | 8 | echo "WALLET_PATH = $path_wallet 9 | CONFIG_SYNCH = $path_synch 10 | CONFIG_POOL = $path_pool 11 | EXPOSE_API_SERVER = true 12 | OPERATOR_PORT_EXTERNAL = 9000 13 | URL_SERVER_PROOF = http://127.0.0.1:10001 14 | LOG_LEVEL = info 15 | OPERATOR_MODE = archive 16 | GAS_MULTIPLIER = 1 17 | GAS_LIMIT = default 18 | LAN=true 19 | PASSWORD=passTest" > $path_env -------------------------------------------------------------------------------- /rollup-operator/test/server/proof-of-stake/test-pos.md: -------------------------------------------------------------------------------- 1 | # Run server operator test 2 | Commands are called from repository `rollup` root directory 3 | 4 | Start local ethereum blockchain 5 | - `ganache-cli -a 100 --defaultBalanceEther 10000` 6 | 7 | Deploy contracts and build configuration files 8 | - Memory database 9 | `npx truffle test ./rollup-operator/test/server/proof-of-stake/build-configs-memDb-pos.test.js` 10 | - LevelDb database 11 | `npx truffle test ./rollup-operator/test/server/proof-of-stake/build-configs-levelDb-pos.test.js` 12 | 13 | Open new terminal and run `server-proof` server 14 | - `cd ./rollup-operator` 15 | - `npm run server-proof` 16 | 17 | Open new terminal and run `synch-pool-service` server 18 | - `cd ./rollup-operator` 19 | - `npm run service-synch-pool` 20 | - service needs configuration file `config.env` in its path 21 | - example can be found in `rollup/rollup-operator/test/synch-pool-service/config/config.env-example` 22 | - *It should be noted that this file should be where the `run-synch-pool.js` is invoked and its name should be `config.env` 23 | 24 | Open new terminal and run operator service 25 | - `cd ./rollup-operator` 26 | - `npm run test:operator-pos` 27 | - password could be typed on console or by adding an environment variable: 28 | - `PASSWORD=passTest` 29 | - operator needs configuration file `config.env` in its path 30 | - example can be found in `rollup/rollup-operator/test/config/config-example.env` 31 | - *It should be noted that this file should be where the `operator-pos.js` is invoked and its name should be `config.env` 32 | 33 | Run test 34 | - `npx truffle test ./rollup-operator/test/server/proof-of-stake/operator-server-pos.test.js` -------------------------------------------------------------------------------- /rollup-operator/test/synch-pool-service/api-bitfinex.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | const ApiBitfinex = require("../../src/synch-pool-service/api-bitfinex"); 3 | 4 | describe("Api Bitfinex", function () { 5 | 6 | let cliBitfinex; 7 | 8 | it("Should initialize client api bitfinex", async () => { 9 | cliBitfinex = new ApiBitfinex(); 10 | }); 11 | 12 | it("Should get base price", async () => { 13 | const base = "USD"; 14 | 15 | const tagBTC = "BTC"; 16 | const tagETH = "ETH"; 17 | 18 | const resBTC = await cliBitfinex.getTokenLastPrice(tagBTC, base); 19 | const resETH = await cliBitfinex.getTokenLastPrice(tagETH, base); 20 | 21 | expect(resBTC).to.be.not.equal(undefined); 22 | expect(resETH).to.be.not.equal(undefined); 23 | }); 24 | 25 | it("Should get list of tradding pairs", async () => { 26 | const resList = await cliBitfinex.getTraddingPairs(); 27 | 28 | expect(resList.length).to.be.above(0); 29 | }); 30 | 31 | it("Should get list of tradding pairs", async () => { 32 | const token = "DAI"; 33 | 34 | const base0 = "USD"; 35 | const base1 = "ETH"; 36 | const base2 = "BTC"; 37 | 38 | const lastPriceBase0 = await cliBitfinex.getTokenLastPrice(token, base0); 39 | const lastPriceBase1 = await cliBitfinex.getTokenLastPrice(token, base1); 40 | const lastPriceBase2 = await cliBitfinex.getTokenLastPrice(token, base2); 41 | 42 | expect(lastPriceBase0).to.be.not.equal(undefined); 43 | expect(lastPriceBase1).to.be.not.equal(undefined); 44 | expect(lastPriceBase2).to.be.not.equal(undefined); 45 | }); 46 | }); -------------------------------------------------------------------------------- /rollup-operator/test/synch-pool-service/build-configs-levelDb.test.js: -------------------------------------------------------------------------------- 1 | /* global artifacts */ 2 | /* global contract */ 3 | /* global web3 */ 4 | 5 | const poseidonUnit = require("circomlib/src/poseidon_gencontract"); 6 | const fs = require("fs"); 7 | const path = require("path"); 8 | 9 | const Verifier = artifacts.require("../contracts/test/VerifierHelper"); 10 | const Rollup = artifacts.require("../contracts/test/Rollup"); 11 | const configSynchPoolPath = path.join(__dirname, "./config/config-test.json"); 12 | const pathConversionTable = path.join(__dirname,"./config/table-conversion-test.json"); 13 | const pathCustomTokens = path.join(__dirname,"./config/custom-test.json"); 14 | 15 | contract("Synnchronizer Pool", (accounts) => { 16 | const { 17 | 0: owner, 18 | 2: feeTokenAddress, 19 | 3: ethAddress, 20 | } = accounts; 21 | 22 | const maxTx = 10; 23 | const maxOnChainTx = 5; 24 | 25 | let insPoseidonUnit; 26 | let insRollup; 27 | let insVerifier; 28 | 29 | before(async () => { 30 | // Deploy poseidon 31 | const C = new web3.eth.Contract(poseidonUnit.abi); 32 | insPoseidonUnit = await C.deploy({ data: poseidonUnit.createCode()}) 33 | .send({ gas: 2500000, from: owner }); 34 | 35 | // Deploy Verifier 36 | insVerifier = await Verifier.new(); 37 | 38 | // Deploy Rollup 39 | insRollup = await Rollup.new(insVerifier.address, insPoseidonUnit._address, 40 | maxTx, maxOnChainTx, feeTokenAddress); 41 | }); 42 | 43 | it("Should build configuration for synch pool service", async () => { 44 | const pathServicePoolDb = `${__dirname}/tmp-0`; 45 | 46 | let config = { 47 | pathDb: pathServicePoolDb, 48 | ethNodeUrl: "http://localhost:8545", 49 | ethAddress: ethAddress, 50 | rollupAddress: insRollup.address, 51 | rollupAbi: Rollup.abi, 52 | logLevel: "debug", 53 | pathConversionTable: pathConversionTable, 54 | pathCustomTokens: pathCustomTokens, 55 | timeouts: { ERROR: 5000, NEXT_LOOP: 5000 }, 56 | }; 57 | 58 | fs.writeFileSync(configSynchPoolPath, JSON.stringify(config)); 59 | }); 60 | }); -------------------------------------------------------------------------------- /rollup-operator/test/synch-pool-service/build-configs-memDb.test.js: -------------------------------------------------------------------------------- 1 | /* global artifacts */ 2 | /* global contract */ 3 | /* global web3 */ 4 | 5 | const poseidonUnit = require("circomlib/src/poseidon_gencontract"); 6 | const fs = require("fs"); 7 | const path = require("path"); 8 | 9 | const Verifier = artifacts.require("../contracts/test/VerifierHelper"); 10 | const Rollup = artifacts.require("../contracts/test/Rollup"); 11 | const configSynchPoolPath = path.join(__dirname, "./config/config-test.json"); 12 | const pathConversionTable = path.join(__dirname,"./config/table-conversion-test.json"); 13 | const pathCustomTokens = path.join(__dirname,"./config/custom-test.json"); 14 | 15 | contract("Synnchronizer Pool", (accounts) => { 16 | const { 17 | 0: owner, 18 | 2: feeTokenAddress, 19 | 3: ethAddress, 20 | } = accounts; 21 | 22 | const maxTx = 10; 23 | const maxOnChainTx = 5; 24 | 25 | let insPoseidonUnit; 26 | let insRollup; 27 | let insVerifier; 28 | 29 | before(async () => { 30 | // Deploy poseidon 31 | const C = new web3.eth.Contract(poseidonUnit.abi); 32 | insPoseidonUnit = await C.deploy({ data: poseidonUnit.createCode()}) 33 | .send({ gas: 2500000, from: owner }); 34 | 35 | // Deploy Verifier 36 | insVerifier = await Verifier.new(); 37 | 38 | // Deploy Rollup 39 | insRollup = await Rollup.new(insVerifier.address, insPoseidonUnit._address, 40 | maxTx, maxOnChainTx, feeTokenAddress); 41 | }); 42 | 43 | it("Should build configuration for synch pool service", async () => { 44 | let config = { 45 | pathDb: undefined, 46 | ethNodeUrl: "http://localhost:8545", 47 | ethAddress: ethAddress, 48 | rollupAddress: insRollup.address, 49 | rollupAbi: Rollup.abi, 50 | logLevel: "debug", 51 | pathConversionTable: pathConversionTable, 52 | pathCustomTokens: pathCustomTokens, 53 | timeouts: { ERROR: 5000, NEXT_LOOP: 5000 }, 54 | }; 55 | 56 | fs.writeFileSync(configSynchPoolPath, JSON.stringify(config)); 57 | }); 58 | }); -------------------------------------------------------------------------------- /rollup-operator/test/synch-pool-service/config/config.env-example: -------------------------------------------------------------------------------- 1 | CONFIG_PATH = /home/configs/config.json 2 | LOG_LEVEL = debug -------------------------------------------------------------------------------- /rollup-operator/test/synch-pool-service/config/create-config-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | path_synch="$(pwd)/test/synch-pool-service/config/config-test.json" 4 | path_env="$(pwd)/src/synch-pool-service/config.env" 5 | 6 | echo "CONFIG_PATH = $path_synch 7 | LOG_LEVEL = debug" > $path_env 8 | -------------------------------------------------------------------------------- /rollup-operator/test/synch-pool-service/test.md: -------------------------------------------------------------------------------- 1 | # Run pool synchronizer test 2 | Commands are called from repository `rollup` root directory 3 | 4 | Start local ethereum blockchain 5 | - `ganache-cli -a 10 --defaultBalanceEther 10000` 6 | 7 | ## Unit test 8 | 9 | Test api bitfinex 10 | - `npx mocha ./rollup-operator/test/synch-pool-service/api-bitfinex.test.js` 11 | 12 | Test pool synchronizer 13 | - `npx truffle test ./rollup-operator/test/synch-pool-service/synch-pool-service.test.js` 14 | 15 | ## Service pool synchronizer 16 | 17 | Build configuration files: 18 | - Memory database 19 | - `npx truffle test ./rollup-operator/test/synch-pool-service/build-configs-memDb.test.js` 20 | - LevelDb database 21 | - `npx truffle test ./rollup-operator/test/synch-pool-service/build-configs-levelDb.test.js` 22 | 23 | Open new terminal and run service 24 | - `cd ./rollup-operator` 25 | - `npm run service-synch-pool` 26 | - service needs configuration file `config.env` in its path 27 | - example can be found in `rollup/rollup-operator/test/synch-pool-service/config/config.env-example` 28 | - *It should be noted that this file should be where the `run-synch-pool.js` is invoked and its name should be `config.env` 29 | 30 | Test service 31 | - `npx truffle test ./rollup-operator/test/synch-pool-service/run-synch-pool.test.js` -------------------------------------------------------------------------------- /rollup-operator/test/test.md: -------------------------------------------------------------------------------- 1 | # Run operator test 2 | Commands are called from repository `rollup` root directory 3 | 4 | In case some test doesn't work as expected, try to increase the `timeoutDelay` global variable 5 | 6 | Start local ethereum blockchain 7 | - `ganache-cli -a 20 --defaultBalanceEther 10000` 8 | 9 | ## Unit test 10 | 11 | Test Token synchronizer: 12 | - `npx truffle test ./rollup-operator/test/synch-tokens.test.js` 13 | 14 | Test PoS synchronizer: 15 | - `npx truffle test ./rollup-operator/test/proof-of-stake/synch-pos.test.js` 16 | 17 | Test PoB synchronizer: 18 | - `npx truffle test ./rollup-operator/test/proof-of-burn/synch-pob.test.js` 19 | 20 | Test pool synchronizer: 21 | - `npx truffle test ./rollup-operator/test/synch-pool.test.js` 22 | 23 | Test Rollup synchronizer: 24 | - `npx truffle test ./rollup-operator/test/synch.test.js` 25 | 26 | Test operator manager: 27 | - `npx truffle test ./rollup-operator/test/proof-of-stake/interface-pos.test.js` 28 | 29 | Test interface PoB: 30 | - `npx truffle test ./rollup-operator/test/proof-of-burn/interface-pob.test.js` 31 | 32 | All at once: 33 | - `npx truffle test ./rollup-operator/test/synch-tokens.test.js; npx truffle test ./rollup-operator/test/proof-of-stake/synch-pos.test.js; npx truffle test ./rollup-operator/test/proof-of-burn/synch-pob.test.js; npx truffle test ./rollup-operator/test/synch-pool.test.js; npx truffle test ./rollup-operator/test/synch.test.js; npx truffle test ./rollup-operator/test/proof-of-stake/interface-pos.test.js; npx truffle test ./rollup-operator/test/proof-of-burn/interface-pob.test.js` 34 | 35 | ## Test server proof 36 | 37 | Open new terminal and run server-proof service: 38 | - `cd ./rollup-operator` 39 | - `npm run server-proof` 40 | 41 | Test `server-proof`: 42 | - `npx mocha ./rollup-operator/test/server-proof.test.js` 43 | 44 | ## Test loop-manager 45 | 46 | Start local ethereum blockchain 47 | - `npx ganache-cli -b 1 -a 100 --defaultBalanceEther 10000` 48 | 49 | Open new terminal and run server-proof service: 50 | - `cd ./rollup-operator` 51 | - `npm run server-proof` 52 | 53 | Test loop-manager: 54 | - `npx truffle test ./rollup-operator/test/proof-of-stake/loop-manager-pos.test.js` 55 | 56 | Test loop-manager PoB: 57 | - `npx truffle test ./rollup-operator/test/proof-of-burn/loop-manager-pob.test.js` 58 | -------------------------------------------------------------------------------- /rollup-utils/babyjub-wallet-utils.js: -------------------------------------------------------------------------------- 1 | const cryptoLib = require("crypto"); 2 | 3 | /** 4 | * Get Hmac 5 | * @param {Buffer} keyBuff - key used to encrypt private key 6 | * @param {Buffer} encryptedDataBuff - represents private key encrypted 7 | * @returns {String} Hmac encoded as base64 string 8 | */ 9 | function getMac(keyBuff, encryptedDataBuff) { 10 | const concatBuff = Buffer.concat([keyBuff.slice(0, 16), encryptedDataBuff]); 11 | return cryptoLib.createHmac("sha256", concatBuff.toString("base64")).digest("base64"); 12 | } 13 | 14 | /** 15 | * Computes key to encrypt from a given password 16 | * @param {String} pass - password 17 | * @param {String} salt - 16 bytes encoded as base64 string 18 | * @param {Number} iterations - number of iterations 19 | * @param {Number} keyLen - key length 20 | * @param {String} digest - hash to use 21 | * @returns {String} - computed key encoded as base64 22 | */ 23 | function passToKey(pass, salt, iterations, keyLen, digest) { 24 | const key = cryptoLib.pbkdf2Sync(pass, salt, iterations, keyLen, digest); 25 | return key.toString("base64"); 26 | } 27 | 28 | /** 29 | * Check key and encrypted data macthes mac provided 30 | * @param {String} key - key encoded as base64 31 | * @param {String} encryptedData - encrypted data encoded as base64 32 | * @param {String} mac - mac to check encoded as base62 string 33 | */ 34 | function checkPass(key, encryptedData, mac) { 35 | const keyBuff = Buffer.from(key, "base64"); 36 | const encryptedDataBuff = Buffer.from(encryptedData, "base64"); 37 | const macCalc = getMac(keyBuff, encryptedDataBuff); 38 | if (macCalc !== mac) { 39 | throw new Error("invalid password"); 40 | } 41 | } 42 | 43 | /** 44 | * Decrypts encrypted data with a given key 45 | * @param {String} key - key to decrypt 46 | * @param {String} encryptedData - data encrypted 47 | * @param {String} algo - algorithm used 48 | * @param {Buffer} iv - initilaization vector 49 | * @returns {String} - utf8 String clear data 50 | */ 51 | function decrypt(key, encryptedData, algo, iv) { 52 | const decipher = cryptoLib.createDecipheriv(algo, key, iv); 53 | let decrypted = decipher.update(encryptedData, "base64", "utf8"); 54 | decrypted += decipher.final("utf8"); 55 | return decrypted; 56 | } 57 | 58 | /** 59 | * Encrypt data with a given key 60 | * @param {String} key - key to encrypt 61 | * @param {String} msg - clear data 62 | * @param {String} algo - algorithm used 63 | * @param {Buffer} iv - initilaization vector 64 | * @returns {String} - base64 encrypted data 65 | */ 66 | function encrypt(key, msg, algo, iv) { 67 | const cipher = cryptoLib.createCipheriv(algo, key, iv); 68 | let encrypted = cipher.update(msg, "utf8", "base64"); 69 | encrypted += cipher.final("base64"); 70 | return encrypted; 71 | } 72 | 73 | module.exports = { 74 | getMac, 75 | passToKey, 76 | checkPass, 77 | decrypt, 78 | encrypt, 79 | }; 80 | -------------------------------------------------------------------------------- /rollup-utils/index.js: -------------------------------------------------------------------------------- 1 | const babyjubKeys = require("./babyjub-hd-keys"); 2 | const babyjubWalletUtils = require("./babyjub-wallet-utils"); 3 | const babyjubWallet = require("./babyjub-wallet"); 4 | const eddsaBabyjub = require("./eddsa-babyjub"); 5 | const levelDB = require("./level-db"); 6 | const memDB = require("./mem-db"); 7 | const rollupTreeUtils = require("./rollup-tree-utils"); 8 | const rollupTree = require("./rollup-tree"); 9 | const rollupUtils = require("./rollup-utils"); 10 | const smtLevelDB = require("./smt-leveldb"); 11 | const utils = require("./utils"); 12 | 13 | module.exports = { 14 | babyjubKeys, 15 | babyjubWalletUtils, 16 | babyjubWallet, 17 | eddsaBabyjub, 18 | levelDB, 19 | memDB, 20 | rollupTreeUtils, 21 | rollupTree, 22 | rollupUtils, 23 | smtLevelDB, 24 | utils 25 | }; -------------------------------------------------------------------------------- /rollup-utils/mem-db.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Database interface for memory allocation 3 | */ 4 | class MemorydB { 5 | constructor() { 6 | this.db = new Map(); 7 | } 8 | 9 | /** 10 | * Method to store [key - value] on database 11 | * @param {String} key 12 | * @param {String} value 13 | */ 14 | insert(key, value) { 15 | this.db.set(key, value); 16 | } 17 | 18 | /** 19 | * Method to retrieve a value given a key 20 | * @param {String} key 21 | * @returns {String} 22 | */ 23 | get(key) { 24 | const value = this.db.get(key); 25 | if (value === undefined) { return null; } 26 | return value; 27 | } 28 | 29 | /** 30 | * Method to retrieve a value given a key if it exist 31 | * otherwise return default value 32 | * @param {String} key 33 | * @param {Any} defaultElem 34 | * @returns {String | Any} 35 | */ 36 | getOrDefault(key, defaultElem) { 37 | const res = this.get(key); 38 | if (res == null) return defaultElem; 39 | return res; 40 | } 41 | 42 | /** 43 | * Method to retrieve a value given a key 44 | * @param {String} key 45 | * @returns {String} 46 | */ 47 | listKeys(prefix) { 48 | const keyList = []; 49 | this.db.forEach((value, key) => { 50 | if (key.indexOf(prefix) !== -1) { 51 | keyList.push(key); 52 | } 53 | }); 54 | return keyList; 55 | } 56 | 57 | /** 58 | * Method to delete a value given a key 59 | * @param {String} key 60 | */ 61 | delete(key) { 62 | this.db.delete(key); 63 | } 64 | 65 | /** 66 | * Method to delete all the [key - value] items 67 | */ 68 | deleteAll() { 69 | this.db.clear(); 70 | } 71 | 72 | /** 73 | * export MemoryDb into an object 74 | * @return {Object} - Database as object representation 75 | */ 76 | export() { 77 | const obj = {}; 78 | this.db.forEach((value, key) => { 79 | obj[key] = value; 80 | }); 81 | return obj; 82 | } 83 | 84 | /** 85 | * new database from object 86 | * @param {Object} dbObj - database as object representation 87 | * @return {Object} - MemorydB class object 88 | */ 89 | import(dbObj) { 90 | Object.keys(dbObj).forEach((key) => { 91 | this.insert(key, dbObj[key]); 92 | }); 93 | } 94 | } 95 | 96 | module.exports = MemorydB; 97 | -------------------------------------------------------------------------------- /rollup-utils/rollup-wallet-utils.js: -------------------------------------------------------------------------------- 1 | const ethers = require("ethers"); 2 | const eddsaBabyJub = require("./eddsa-babyjub"); 3 | const { hashBuffer } = require("./utils"); 4 | 5 | const ROLLUP_MESSAGE = "Login to iden3 zkRollup"; 6 | 7 | /** 8 | * To verify ethereum signature 9 | * @param {String} address 10 | * @param {String} messStr 11 | * @param {String} signatureHex 12 | * @returns {Boolean} 13 | */ 14 | function verifyEthereum(address, messStr, signatureHex) { 15 | const extractAddress = ethers.utils.verifyMessage(messStr, signatureHex); 16 | return address === extractAddress; 17 | } 18 | 19 | /** 20 | * Verifies signature for a given message using babyjubjub 21 | * @param {String} publicKeyHex - Babyjubjub public key encoded as hex string 22 | * @param {String} messStr - clear message data 23 | * @param {String} signatureHex - Ecdsa signature compresed and encoded as hex string 24 | * @returns {boolean} True if validation is succesfull; otherwise false 25 | */ 26 | function verifyRollup(publicKeyHex, messStr, signatureHex) { 27 | if (publicKeyHex.substr(0, 2) === "0x") 28 | publicKeyHex = publicKeyHex.substr(2); 29 | 30 | const pkBuff = Buffer.from(publicKeyHex, "hex"); 31 | const pk = eddsaBabyJub.PublicKey.newFromCompressed(pkBuff); 32 | const msgBuff = Buffer.from(messStr); 33 | const hash = hashBuffer(msgBuff); 34 | const sigBuff = Buffer.from(signatureHex, "hex"); 35 | const sig = eddsaBabyJub.Signature.newFromCompressed(sigBuff); 36 | return pk.verifyPoseidon(hash, sig); 37 | } 38 | 39 | module.exports = { 40 | ROLLUP_MESSAGE, 41 | verifyEthereum, 42 | verifyRollup 43 | }; -------------------------------------------------------------------------------- /simple-webapp/.eslintrc.js: -------------------------------------------------------------------------------- 1 | // Eslint configuration file. 2 | // Configuration documentation: http://eslint.org/docs/user-guide/configuring.html 3 | // Rules documentation: http://eslint.org/docs/rules/ 4 | // 5 | // Codes: 6 | // 0 - turn the rule off 7 | // 1 - turn the rule on as a warning (doesn't affect exit code) 8 | // 2 - turn the rule on as an error (exit code is 1 when triggered) 9 | 10 | const path = require('path'); 11 | 12 | module.exports = { 13 | "extends": "airbnb", 14 | "env": { 15 | "es6": true, 16 | "browser": true, 17 | "node": true, 18 | "jest": true 19 | }, 20 | "parserOptions": { 21 | "ecmaVersion": 2018, 22 | "sourceType": "module", 23 | }, 24 | "parser": "babel-eslint", // to use global variables as document along the app 25 | "rules": { 26 | "arrow-body-style": [0], 27 | "class-methods-use-this": [0], 28 | "no-unused-vars": [2, { "varsIgnorePattern": "export^" }], 29 | "no-unused-expressions": [0], 30 | "func-names": [0], 31 | "global-require": [0], 32 | "max-len": ["error", { "code": 120, "comments": 800 }], 33 | "no-underscore-dangle": [0], 34 | "import/no-extraneous-dependencies": [ 35 | "error", 36 | {"devDependencies": true, "optionalDependencies": false, "peerDependencies": false} 37 | ], 38 | "no-param-reassign": [2, { "props": false }], 39 | "no-prototype-builtins": [0], 40 | "no-plusplus": [0], 41 | "no-restricted-syntax": [0], 42 | "no-return-assign": [0], 43 | "no-console": [2, { "allow": ["warn", "error"] }], 44 | "no-bitwise": [0], 45 | "no-mixed-operators": [0], 46 | "no-use-before-define": ["error", { "functions": false, "classes": false }], 47 | "consistent-return": [0], 48 | "no-async-promise-executor": [0], 49 | "new-cap": [0], 50 | "import/no-extraneous-dependencies": [0], 51 | "import/prefer-default-export": [0], 52 | "react/no-did-update-set-state": [0], 53 | "react/prefer-stateless-function": [0], 54 | "react/jsx-filename-extension": [1, { "extensions": [".js", ".jsx"] }], 55 | "react/require-default-props": [0], 56 | "react/destructuring-assignment": [0], 57 | "react/jsx-closing-bracket-location": [1, 'after-props'], 58 | "react/forbid-prop-types": [0], 59 | "react/no-array-index-key": [0], 60 | "react/no-access-state-in-setstate": [0], 61 | "react/static-property-placement": [0], 62 | } 63 | }; -------------------------------------------------------------------------------- /simple-webapp/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | test/ 3 | 4 | /src/utils/bundle-cli.js 5 | /src/utils/bundle-op.js 6 | /src/utils/config.json -------------------------------------------------------------------------------- /simple-webapp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rollup-wallet", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "axios": "^0.19.0", 7 | "bip39": "^3.0.2", 8 | "circomlib": "0.0.19", 9 | "ethereumjs-util": "^6.1.0", 10 | "file-saver": "^2.0.2", 11 | "fs": "0.0.1-security", 12 | "hdkey": "^1.1.1", 13 | "immutable": "^4.0.0-rc.12", 14 | "prop-types": "^15.7.2", 15 | "react": "^16.10.2", 16 | "react-dom": "^16.10.2", 17 | "react-redux": "^7.1.1", 18 | "react-router-dom": "^5.1.2", 19 | "react-scripts": "3.2.0", 20 | "redux": "^4.0.4", 21 | "redux-thunk": "^2.3.0", 22 | "semantic-ui-css": "^2.4.1", 23 | "semantic-ui-react": "^0.88.1", 24 | "truffle-hdwallet-provider": "^1.0.17", 25 | "web3": "^1.2.4" 26 | }, 27 | "scripts": { 28 | "start": "react-scripts start", 29 | "build": "react-scripts build", 30 | "test": "react-scripts test", 31 | "eject": "react-scripts eject", 32 | "lint": "eslint --ext .jsx,.js src" 33 | }, 34 | "eslintConfig": { 35 | "extends": "react-app" 36 | }, 37 | "browserslist": { 38 | "production": [ 39 | ">0.2%", 40 | "not dead", 41 | "not op_mini all" 42 | ], 43 | "development": [ 44 | "last 1 chrome version", 45 | "last 1 firefox version", 46 | "last 1 safari version" 47 | ] 48 | }, 49 | "devDependencies": { 50 | "babel-eslint": "^10.0.3", 51 | "eslint": "^6.7.1", 52 | "eslint-config-airbnb": "^18.0.1", 53 | "eslint-import-resolver-webpack": "^0.11.1", 54 | "eslint-plugin-import": "^2.18.2", 55 | "eslint-plugin-jsx-a11y": "^6.2.3", 56 | "eslint-plugin-react": "^7.16.0" 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /simple-webapp/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Rollup 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /simple-webapp/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /simple-webapp/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | -------------------------------------------------------------------------------- /simple-webapp/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | } 8 | 9 | .App-header { 10 | background-color: #282c34; 11 | min-height: 10vh; 12 | display: flex; 13 | flex-direction: column; 14 | align-items: center; 15 | justify-content: center; 16 | font-size: calc(10px + 2vmin); 17 | color: white; 18 | } 19 | 20 | .App-link { 21 | color: #09d3ac; 22 | } 23 | -------------------------------------------------------------------------------- /simple-webapp/src/App.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Route } from 'react-router-dom'; 3 | import './App.css'; 4 | 5 | import InitView from './views/init-view'; 6 | import ActionView from './views/action-view'; 7 | 8 | class App extends Component { 9 | render() { 10 | return ( 11 | <> 12 | } /> 16 | } /> 20 | 21 | ); 22 | } 23 | } 24 | 25 | export default (App); 26 | -------------------------------------------------------------------------------- /simple-webapp/src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /simple-webapp/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /simple-webapp/src/index.js: -------------------------------------------------------------------------------- 1 | import { Provider } from 'react-redux'; 2 | import { BrowserRouter as Router, Route } from 'react-router-dom'; 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import store from './store'; 6 | import 'semantic-ui-css/semantic.min.css'; 7 | import App from './App'; 8 | import * as serviceWorker from './serviceWorker'; 9 | 10 | ReactDOM.render( 11 | 12 | 13 | 14 | 15 | , 16 | document.getElementById('root'), 17 | ); 18 | 19 | // If you want your app to work offline and load faster, you can change 20 | // unregister() to register() below. Note this comes with some pitfalls. 21 | // Learn more about service workers: http://bit.ly/CRA-PWA 22 | serviceWorker.unregister(); 23 | -------------------------------------------------------------------------------- /simple-webapp/src/state/general/constants.js: -------------------------------------------------------------------------------- 1 | export const LOAD_WALLET = 'LOAD_WALLET'; 2 | export const LOAD_WALLET_SUCCESS = 'LOAD_WALLET_SUCCESS'; 3 | export const LOAD_WALLET_ERROR = 'LOAD_WALLET_ERROR'; 4 | export const CREATE_WALLET = 'CREATE_WALLET'; 5 | export const CREATE_WALLET_SUCCESS = 'CREATE_WALLET_SUCCESS'; 6 | export const CREATE_WALLET_ERROR = 'CREATE_WALLET_ERROR'; 7 | export const LOAD_FILES = 'LOAD_FILES'; 8 | export const LOAD_FILES_SUCCESS = 'LOAD_FILES_SUCCESS'; 9 | export const LOAD_FILES_ERROR = 'LOAD_FILES_ERROR'; 10 | export const LOAD_OPERATOR = 'LOAD_OPERATOR'; 11 | export const LOAD_OPERATOR_SUCCESS = 'LOAD_OPERATOR_SUCCESS'; 12 | export const LOAD_OPERATOR_ERROR = 'LOAD_OPERATOR_ERROR'; 13 | export const INFO_ACCOUNT = 'INFO_ACCOUNT'; 14 | export const INFO_ACCOUNT_SUCCESS = 'INFO_ACCOUNT_SUCCESS'; 15 | export const INFO_ACCOUNT_ERROR = 'INFO_ACCOUNT_ERROR'; 16 | export const CHECK_ETHER_ERROR = 'CHECK_ETHER_ERROR'; 17 | export const CHECK_APPROVED_TOKENS_ERROR = 'CHECK_APPROVED_TOKENS_ERROR'; 18 | export const INIT_ETHER_ERROR = 'INIT_ETHER_ERROR'; 19 | export const INIT_APPROVED_TOKENS_ERROR = 'INIT_APPROVED_TOKENS_ERROR'; 20 | export const SET_GAS_MULTIPLIER = 'SET_GAS_MULTIPLIER'; 21 | -------------------------------------------------------------------------------- /simple-webapp/src/state/general/index.js: -------------------------------------------------------------------------------- 1 | import reducer from './reducer'; 2 | 3 | export default { 4 | reducer, 5 | }; 6 | -------------------------------------------------------------------------------- /simple-webapp/src/state/reducers.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import general from './general'; 3 | import transactions from './tx'; 4 | 5 | export default combineReducers({ 6 | general: general.reducer, 7 | transactions: transactions.reducer, 8 | }); 9 | -------------------------------------------------------------------------------- /simple-webapp/src/state/tx/constants.js: -------------------------------------------------------------------------------- 1 | export const SEND_DEPOSIT = 'SEND_DEPOSIT'; 2 | export const SEND_DEPOSIT_SUCCESS = 'SEND_DEPOSIT_SUCCESS'; 3 | export const SEND_DEPOSIT_ERROR = 'SEND_DEPOSIT_ERROR'; 4 | export const SEND_WITHDRAW = 'SEND_WITHDRAW'; 5 | export const SEND_WITHDRAW_SUCCESS = 'SEND_WITHDRAW_SUCCESS'; 6 | export const SEND_WITHDRAW_ERROR = 'SEND_WITHDRAW_ERROR'; 7 | export const SEND_SEND = 'SEND_SEND'; 8 | export const SEND_SEND_SUCCESS = 'SEND_SEND_SUCCESS'; 9 | export const SEND_SEND_ERROR = 'SEND_SEND_ERROR'; 10 | export const SEND_SEND0 = 'SEND_SEND0'; 11 | export const SEND_SEND0_SUCCESS = 'SEND_SEND0_SUCCESS'; 12 | export const SEND_SEND0_ERROR = 'SEND_SEND0_ERROR'; 13 | export const APPROVE = 'APPROVE'; 14 | export const APPROVE_SUCCESS = 'APPROVE_SUCCESS'; 15 | export const APPROVE_ERROR = 'APPROVE_ERROR'; 16 | export const GET_TOKENS = 'GET_TOKENS'; 17 | export const GET_TOKENS_SUCCESS = 'GET_TOKENS_SUCCESS'; 18 | export const GET_TOKENS_ERROR = 'GET_TOKENS_ERROR'; 19 | export const GET_EXIT_ROOT = 'GET_EXIT_ROOT'; 20 | export const GET_EXIT_ROOT_SUCCESS = 'GET_EXIT_ROOT_SUCCESS'; 21 | export const GET_EXIT_ROOT_ERROR = 'GET_EXIT_ROOT_ERROR'; 22 | export const GET_INIT = 'GET_INIT'; 23 | -------------------------------------------------------------------------------- /simple-webapp/src/state/tx/index.js: -------------------------------------------------------------------------------- 1 | import * as actions from './actions'; 2 | import * as constants from './constants'; 3 | import reducer from './reducer'; 4 | 5 | export default { 6 | actions, 7 | constants, 8 | reducer, 9 | }; 10 | -------------------------------------------------------------------------------- /simple-webapp/src/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware, compose } from 'redux'; 2 | import thunk from 'redux-thunk'; 3 | import rootReducer from './state/reducers'; 4 | 5 | const initialState = {}; 6 | 7 | const middleware = [thunk]; 8 | 9 | const store = createStore(rootReducer, initialState, compose(applyMiddleware(...middleware))); 10 | 11 | export default store; 12 | -------------------------------------------------------------------------------- /simple-webapp/src/utils/config-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "operator": "http://127.0.0.1:9000", 3 | "tokensAddress": "0xaFF4481D10270F50f203E0763e2597776068CBc5", 4 | "address": "0x9A1D1FaD8F2Db1f608D03Da3BF482059e59c0890", 5 | "nodeEth": "ropsten.infura.io/v3/39753c9df15e15ee844e2c19a928bbc1", 6 | "abiRollup": "abi_object", 7 | "abiTokens": "abi_object" 8 | } -------------------------------------------------------------------------------- /simple-webapp/src/utils/disable-eslint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sed -i '1i /* eslint-disable */' ./simple-webapp/src/utils/bundle-cli.js 3 | sed -i '1i /* eslint-disable */' ./simple-webapp/src/utils/bundle-op.js -------------------------------------------------------------------------------- /simple-webapp/src/utils/utils.js: -------------------------------------------------------------------------------- 1 | export const readFile = (file) => { 2 | return new Promise((resolve) => { 3 | const reader = new FileReader(); 4 | reader.readAsText(file); 5 | reader.onload = function (event) { 6 | resolve(JSON.parse(event.target.result)); 7 | }; 8 | }); 9 | }; 10 | -------------------------------------------------------------------------------- /simple-webapp/src/views/action-view/components/constants.js: -------------------------------------------------------------------------------- 1 | export const GAS_MULTIPLIER = { 2 | SLOW: 1, 3 | AVG: 2, 4 | FAST: 3, 5 | }; -------------------------------------------------------------------------------- /simple-webapp/src/views/action-view/components/gmButtons.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { Button } from 'semantic-ui-react'; 4 | import { selectGasMultiplier } from '../../../state/general/actions'; 5 | import { GAS_MULTIPLIER } from './constants'; 6 | 7 | class ButtonGM extends Component { 8 | 9 | state = { 10 | slow: false, 11 | avg: true, 12 | fast: false, 13 | } 14 | 15 | selectActive = (num) => { 16 | if (num === GAS_MULTIPLIER.SLOW) { 17 | this.setState({slow: true, avg: false, fast: false}); 18 | } else if (num === GAS_MULTIPLIER.AVG) { 19 | this.setState({slow: false, avg: true, fast: false}); 20 | } else if (num === GAS_MULTIPLIER.FAST) { 21 | this.setState({slow: false, avg: false, fast: true}); 22 | } else { 23 | this.setState({slow: false, avg: false, fast: false}); 24 | } 25 | } 26 | 27 | changeGasMultiplier = (num, event) => { 28 | event.preventDefault(); 29 | this.props.selectGasMultiplier(num); 30 | this.selectActive(num); 31 | } 32 | 33 | render() { 34 | return ( 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | ); 43 | } 44 | } 45 | 46 | export default connect(null, { selectGasMultiplier })(ButtonGM); 47 | -------------------------------------------------------------------------------- /simple-webapp/src/views/action-view/components/menu-actions.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { Table, Button, Icon } from 'semantic-ui-react'; 4 | 5 | class MenuActions extends Component { 6 | static propTypes = { 7 | handleItemClick: PropTypes.func.isRequired, 8 | } 9 | 10 | render() { 11 | return ( 12 | 13 | 14 | 15 | 16 | ONCHAIN 17 | 18 | 19 | OFFCHAIN 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 32 | 36 | 37 | 38 | 39 | 40 | 44 | 48 | 49 | 50 | 51 | 52 |
53 | ); 54 | } 55 | } 56 | export default MenuActions; 57 | -------------------------------------------------------------------------------- /simple-webapp/src/views/action-view/components/menu.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import { Menu, Icon } from 'semantic-ui-react'; 3 | import { Link } from 'react-router-dom'; 4 | 5 | class MenuBack extends Component { 6 | render() { 7 | return ( 8 | 9 | 10 | 11 | 13 | 14 | Import another wallet 15 | 16 | 17 | 18 | 20 | 21 | Back 22 | 23 | 24 | 25 | 26 | ); 27 | } 28 | } 29 | 30 | export default MenuBack; 31 | -------------------------------------------------------------------------------- /simple-webapp/src/views/action-view/components/modal-error.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | // import PropTypes from 'prop-types'; 3 | import { 4 | Modal, Button, 5 | } from 'semantic-ui-react'; 6 | 7 | const message = { 8 | "0": "You do not have enough approved tokens.", 9 | "1": "You do not have enough ether.", 10 | } 11 | 12 | class ModalError extends Component { 13 | 14 | render() { 15 | return ( 16 | 17 | Error 18 | 19 | {message[this.props.error]} 20 | 21 | 22 | 25 | 26 | 27 | ); 28 | } 29 | } 30 | 31 | export default ModalError; -------------------------------------------------------------------------------- /simple-webapp/src/views/action-view/components/modal-info-id.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | Table, Icon, Modal, Button, 5 | } from 'semantic-ui-react'; 6 | 7 | const web3 = require('web3'); 8 | 9 | class ModalInfoId extends Component { 10 | static propTypes = { 11 | txs: PropTypes.array, 12 | }; 13 | 14 | static defaultProps = { 15 | txs: [{ idx: 0, amount: 0 }], 16 | }; 17 | 18 | getIdTokens = () => { 19 | try { 20 | const { txs } = this.props; 21 | return txs.map((key, index) => { 22 | return ( 23 | 24 | {key.idx} 25 | {web3.utils.fromWei(key.amount, 'ether')} 26 | 27 | ); 28 | }); 29 | } catch (err) { 30 | return ( 31 | 32 | 0 33 | 0 34 | 35 | ); 36 | } 37 | } 38 | 39 | render() { 40 | return ( 41 | } closeIcon> 42 | 43 | 44 | 45 | 46 | 47 | ID 48 | TOKENS 49 | 50 | 51 | 52 | {this.getIdTokens()} 53 | 54 |
55 |
56 |
57 | ); 58 | } 59 | } 60 | 61 | export default ModalInfoId; 62 | -------------------------------------------------------------------------------- /simple-webapp/src/views/action-view/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './containers/action-view'; 2 | -------------------------------------------------------------------------------- /simple-webapp/src/views/init-view/components/modal-create.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | Button, Modal, Form, Icon, Message, 5 | } from 'semantic-ui-react'; 6 | 7 | class ModalCreate extends Component { 8 | static propTypes = { 9 | fileNameRef: PropTypes.object.isRequired, 10 | passwordRef: PropTypes.object.isRequired, 11 | modalCreate: PropTypes.bool.isRequired, 12 | handleClickCreate: PropTypes.func.isRequired, 13 | toggleModalCreate: PropTypes.func.isRequired, 14 | errorCreateWallet: PropTypes.string, 15 | isCreatingWallet: PropTypes.bool.isRequired, 16 | isLoadingWallet: PropTypes.bool.isRequired, 17 | } 18 | 19 | isLoading = () => { 20 | if (this.props.isCreatingWallet === true || this.props.isLoadingWallet === true) { 21 | return ( 22 | 23 | 24 | We are creating and importing your wallet.. 25 | 26 | ); 27 | } if (this.props.errorCreateWallet !== '') { 28 | return ( 29 | 30 | 31 | Error 32 | 33 | ); 34 | } 35 | } 36 | 37 | render() { 38 | return ( 39 | 40 | Rollup Wallet 41 | 42 |
43 | 44 | 48 | 49 | 50 | 54 | 55 |
56 | {this.isLoading()} 57 |
58 | 59 | 63 | 67 | 68 |
69 | ); 70 | } 71 | } 72 | 73 | export default ModalCreate; 74 | -------------------------------------------------------------------------------- /simple-webapp/src/views/init-view/components/modal-import.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import { 4 | Button, Modal, Form, Icon, Message, 5 | } from 'semantic-ui-react'; 6 | 7 | class ModalImport extends Component { 8 | static propTypes = { 9 | passwordRef: PropTypes.object.isRequired, 10 | errorWallet: PropTypes.string, 11 | modalImport: PropTypes.bool.isRequired, 12 | isLoadingWallet: PropTypes.bool.isRequired, 13 | handleChangeWallet: PropTypes.func.isRequired, 14 | handleClickImport: PropTypes.func.isRequired, 15 | toggleModalImport: PropTypes.func.isRequired, 16 | } 17 | 18 | isLoading = () => { 19 | if (this.props.isLoadingWallet === true) { 20 | return ( 21 | 22 | 23 | We are checking your wallet... 24 | 25 | ); 26 | } if (this.props.errorWallet !== '') { 27 | return ( 28 | 29 | 30 | Invalid Wallet or Password 31 | 32 | ); 33 | } 34 | } 35 | 36 | render() { 37 | return ( 38 | 39 | Import Wallet 40 | 41 |
42 | 43 | 47 | 48 | 49 | 53 | 54 |
55 | {this.isLoading()} 56 |
57 | 58 | 62 | 66 | 67 |
68 | ); 69 | } 70 | } 71 | 72 | export default ModalImport; 73 | -------------------------------------------------------------------------------- /simple-webapp/src/views/init-view/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './containers/init-view'; 2 | -------------------------------------------------------------------------------- /test/circuits/checkfees.test.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const tester = require("circom").tester; 3 | const Scalar = require("ffjavascript").Scalar; 4 | 5 | const { fix2float } = require("../../js/utils"); 6 | 7 | describe("Check fess circuit test", function () { 8 | this.timeout(5000); 9 | let circuit; 10 | let accFees; 11 | let feeTotal; 12 | let feeTotalInput; 13 | 14 | before( async() => { 15 | circuit = await tester(path.join(__dirname, "circuits-test", "checkfees_test.circom")); 16 | await circuit.loadConstraints(); 17 | console.log("Constraints `checkfees.circom` circuit: " + circuit.constraints.length + "\n"); 18 | }); 19 | 20 | it("Should test check fee values ok ", async () => { 21 | accFees = new Array(16).fill(0); 22 | feeTotal = new Array(16).fill(0); 23 | 24 | for (let i = 0; i < feeTotal.length - 1; i++){ 25 | accFees[i] = Scalar.e(i); 26 | feeTotal[i] = Scalar.e(fix2float(i)); 27 | } 28 | 29 | // feeTotalInpiut 30 | feeTotalInput = Scalar.e(0); 31 | for (let i = 0; i < accFees.length; i++) { 32 | feeTotalInput = Scalar.add(feeTotalInput, Scalar.shl(Scalar.e(feeTotal[i]), 16*i)); 33 | } 34 | 35 | const input = { 36 | accFee: accFees, 37 | feeTotals: feeTotalInput, 38 | }; 39 | 40 | const w = await circuit.calculateWitness(input); 41 | 42 | await circuit.assertOut(w, {feeTotalOk: 1}); 43 | }); 44 | 45 | it("Should test check fee values ko", async () => { 46 | // Modify input to try to get more fees than accumulated 47 | feeTotal[7] = Scalar.add(feeTotal[7], 1); 48 | 49 | // Re-calculate feeTotals 50 | // feeTotalInpiut 51 | feeTotalInput = Scalar.e(0); 52 | for (let i = 0; i < accFees.length; i++) { 53 | feeTotalInput = Scalar.add(feeTotalInput, Scalar.shl(Scalar.e(feeTotal[i]), 16*i)); 54 | } 55 | 56 | const input = { 57 | accFee: accFees, 58 | feeTotals: feeTotalInput, 59 | }; 60 | 61 | const w = await circuit.calculateWitness(input); 62 | 63 | await circuit.assertOut(w, {feeTotalOk: 0}); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /test/circuits/circuits-test/balancesupdater_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/balancesupdater.circom"; 2 | 3 | component main = BalancesUpdater(); 4 | -------------------------------------------------------------------------------- /test/circuits/circuits-test/checkfees_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/checkfees.circom"; 2 | 3 | component main = CheckFees(); -------------------------------------------------------------------------------- /test/circuits/circuits-test/decodefloat_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/decodefloat.circom"; 2 | 3 | component main = DecodeFloat(); 4 | -------------------------------------------------------------------------------- /test/circuits/circuits-test/decodetx_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/decodetx.circom"; 2 | 3 | component main = DecodeTx(24); -------------------------------------------------------------------------------- /test/circuits/circuits-test/feeplandecoder_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/feeplandecoder.circom"; 2 | 3 | component main = FeePlanDecoder(); 4 | -------------------------------------------------------------------------------- /test/circuits/circuits-test/feetableselector_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/feetableselector.circom"; 2 | 3 | component main = FeeTableSelector(); -------------------------------------------------------------------------------- /test/circuits/circuits-test/feeupdater_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/feeupdater.circom"; 2 | 3 | component main = FeeUpdater(); -------------------------------------------------------------------------------- /test/circuits/circuits-test/requiredtxverifier_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/requiredtxverifier.circom"; 2 | 3 | component main = RequiredTxVerifier(); -------------------------------------------------------------------------------- /test/circuits/circuits-test/rollup_pool_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/rollup.circom"; 2 | 3 | component main = Rollup(4, 8); 4 | -------------------------------------------------------------------------------- /test/circuits/circuits-test/rollup_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/rollup.circom"; 2 | 3 | component main = Rollup(5, 8); 4 | -------------------------------------------------------------------------------- /test/circuits/circuits-test/rolluptx_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/rolluptx.circom"; 2 | 3 | component main = RollupTx(24); -------------------------------------------------------------------------------- /test/circuits/circuits-test/rolluptxstates_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/rolluptxstates.circom"; 2 | 3 | component main = RollupTXStates(); -------------------------------------------------------------------------------- /test/circuits/circuits-test/statepacker_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/statepacker.circom"; 2 | 3 | component main = StatePacker(); -------------------------------------------------------------------------------- /test/circuits/circuits-test/unpackax_test.circom: -------------------------------------------------------------------------------- 1 | include "../../../circuits/unpackax.circom"; 2 | 3 | component main = UnpackAx(); -------------------------------------------------------------------------------- /test/circuits/decodefloat.test.js: -------------------------------------------------------------------------------- 1 | const { assert } = require("chai"); 2 | const path = require("path"); 3 | const tester = require("circom").tester; 4 | const Scalar = require("ffjavascript").Scalar; 5 | 6 | const utils = require("../../js/utils"); 7 | 8 | describe("Decode float test", function () { 9 | let circuit; 10 | 11 | const testVector = [ 12 | [0x307B, "123000000"], 13 | [0x1DC6, "454500"], 14 | [0xFFFF, "10235000000000000000000000000000000"], 15 | [0x0000, "0"], 16 | [0x0400, "0"], 17 | [0x0001, "1"], 18 | [0x0401, "1"], 19 | [0x0800, "0"], 20 | [0x0c00, "5"], 21 | [0x0801, "10"], 22 | [0x0c01, "15"], 23 | ]; 24 | 25 | before( async() => { 26 | circuit = await tester(path.join(__dirname, "circuits-test", "decodefloat_test.circom")); 27 | await circuit.loadConstraints(); 28 | console.log("Constraints `decodefloat.circom` circuit: " + circuit.constraints.length + "\n"); 29 | }); 30 | 31 | it("Should test utils", async () => { 32 | for (let i=0; i { 44 | for (let i=0; i { 9 | circuit = await tester(path.join(__dirname, "circuits-test", "feeplandecoder_test.circom")); 10 | await circuit.loadConstraints(); 11 | console.log("Constraints `feeplandecoder.circom` circuit: " + circuit.constraints.length + "\n"); 12 | }); 13 | 14 | it("Should test fee plan decoder circuit", async () => { 15 | const feePlanCoins = new Array(16).fill(0); 16 | 17 | for (let i = 0; i < feePlanCoins.length - 1; i++){ 18 | feePlanCoins[i] = Scalar.e(i); 19 | } 20 | 21 | let coinsInput = Scalar.e(0); 22 | for (let i = 0; i < feePlanCoins.length; i++) { 23 | coinsInput = Scalar.add(coinsInput, Scalar.shl(Scalar.e(feePlanCoins[i]), 16*i)); 24 | } 25 | 26 | const input = { 27 | feePlanCoins: coinsInput, 28 | }; 29 | 30 | const w = await circuit.calculateWitness(input); 31 | 32 | const output = { 33 | feePlanCoin: feePlanCoins, 34 | }; 35 | 36 | await circuit.assertOut(w, output); 37 | }); 38 | }); -------------------------------------------------------------------------------- /test/circuits/feetableselector.test.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const tester = require("circom").tester; 3 | const Scalar = require("ffjavascript").Scalar; 4 | 5 | const Constants = require("../../js/constants"); 6 | 7 | describe("Fee table selector circuit", function () { 8 | let circuit; 9 | 10 | before( async() => { 11 | circuit = await tester(path.join(__dirname, "circuits-test", "feetableselector_test.circom")); 12 | await circuit.loadConstraints(); 13 | console.log("Constraints `feetableselector.circom` circuit: " + circuit.constraints.length + "\n"); 14 | }); 15 | 16 | it("Should test fee table", async () => { 17 | const { tableAdjustedFee } = Constants; 18 | const input = {}; 19 | const output = {}; 20 | 21 | for (let i = 0; i < tableAdjustedFee.length; i ++){ 22 | input.feeSel = i; 23 | 24 | output.feeOut = tableAdjustedFee[i]; 25 | 26 | const w = await circuit.calculateWitness(input); 27 | await circuit.assertOut(w, output); 28 | } 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /test/circuits/helpers/checkbatch.js: -------------------------------------------------------------------------------- 1 | async function checkBatch(circuit, w, bb) { 2 | 3 | const out = { 4 | newStRoot: bb.getNewStateRoot(), 5 | newExitRoot: bb.getNewExitRoot(), 6 | onChainHash: bb.getOnChainHash(), 7 | offChainHash: bb.getOffChainHash(), 8 | finalIdx: bb.getFinalIdx(), 9 | }; 10 | 11 | await circuit.assertOut(w, out); 12 | } 13 | 14 | module.exports = checkBatch; -------------------------------------------------------------------------------- /test/circuits/helpers/utils-circuit.js: -------------------------------------------------------------------------------- 1 | function random(ceil){ 2 | return Math.floor((Math.random() * ceil)); 3 | } 4 | 5 | function printTx(w, c, nTx){ 6 | console.log(); 7 | console.log(`<-------Tx: ${nTx}------->`); 8 | console.log("<--Off-Chain-->"); 9 | console.log("txData: ", w[c.getSignalIdx(`main.txData[${nTx}]`)].toString()); 10 | console.log("fromIdx: ", w[c.getSignalIdx(`main.fromIdx[${nTx}]`)].toString()); 11 | console.log("toIdx: ", w[c.getSignalIdx(`main.toIdx[${nTx}]`)].toString()); 12 | console.log("toAx: ", w[c.getSignalIdx(`main.toAx[${nTx}]`)].toString()); 13 | console.log("toAy: ", w[c.getSignalIdx(`main.toAy[${nTx}]`)].toString()); 14 | console.log("toEthAddr: ", w[c.getSignalIdx(`main.toEthAddr[${nTx}]`)].toString()); 15 | console.log("rqTxData: ", w[c.getSignalIdx(`main.rqTxData[${nTx}]`)].toString()); 16 | console.log("step: ", w[c.getSignalIdx(`main.step[${nTx}]`)].toString()); 17 | console.log("s: ", w[c.getSignalIdx(`main.s[${nTx}]`)].toString()); 18 | console.log("r8x: ", w[c.getSignalIdx(`main.r8x[${nTx}]`)].toString()); 19 | console.log("r8y: ", w[c.getSignalIdx(`main.r8y[${nTx}]`)].toString()); 20 | 21 | console.log("<--On-Chain-->"); 22 | console.log("loadAmount: ", w[c.getSignalIdx(`main.loadAmount[${nTx}]`)].toString()); 23 | console.log("fromEthAddr: ", w[c.getSignalIdx(`main.fromEthAddr[${nTx}]`)].toString()); 24 | console.log("fromAx: ", w[c.getSignalIdx(`main.fromAx[${nTx}]`)].toString()); 25 | console.log("fromAy: ", w[c.getSignalIdx(`main.fromAy[${nTx}]`)].toString()); 26 | console.log(); 27 | } 28 | 29 | module.exports = { 30 | random, 31 | printTx, 32 | }; -------------------------------------------------------------------------------- /test/circuits/unpack-ax.test.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const tester = require("circom").tester; 3 | const babyJub = require("circomlib").babyJub; 4 | const eddsa = require("circomlib").eddsa; 5 | const crypto = require("crypto"); 6 | 7 | describe("Unpack Babyjubjub Ax point", function () { 8 | let circuit; 9 | 10 | this.timeout(10000); 11 | 12 | before( async() => { 13 | circuit = await tester(path.join(__dirname, "circuits-test", "unpackax_test.circom"), {reduceConstraints:false}); 14 | await circuit.loadConstraints(); 15 | console.log("Constraints `unpackax.circom` circuit: " + circuit.constraints.length + "\n"); 16 | }); 17 | 18 | it("Should unpack babyjubjub base point", async () => { 19 | 20 | const babyAx = babyJub.Base8[0].toString(); 21 | 22 | const w = await circuit.calculateWitness({ Ax: babyAx }, {logTrigger:false, logOutput: false, logSet: false}); 23 | 24 | const checkOut = { 25 | Ay: babyJub.Base8[1], 26 | }; 27 | 28 | await circuit.assertOut(w, checkOut); 29 | }); 30 | 31 | it("Should unpack babyjubjub random points", async () => { 32 | 33 | const rounds = 25; 34 | 35 | for (let i = 0; i < rounds; i++){ 36 | const privKey = crypto.randomBytes(32); 37 | const pubKey = eddsa.prv2pub(privKey); 38 | 39 | const Ax = pubKey[0]; 40 | const Ay = pubKey[1]; 41 | 42 | const w = await circuit.calculateWitness({ Ax: Ax }, {logTrigger:false, logOutput: false, logSet: false}); 43 | 44 | const checkOut = { 45 | Ay: Ay, 46 | }; 47 | 48 | await circuit.assertOut(w, checkOut); 49 | } 50 | }); 51 | }); -------------------------------------------------------------------------------- /test/contracts/helpers/timeTravel.js: -------------------------------------------------------------------------------- 1 | /* global web3 */ 2 | async function advanceTime(time) { 3 | // eslint-disable-next-line no-new 4 | new Promise((resolve, reject) => { 5 | web3.currentProvider.send({ 6 | jsonrpc: "2.0", 7 | method: "evm_increaseTime", 8 | params: [time], 9 | id: new Date().getTime(), 10 | }, (err, result) => { 11 | if (err) { return reject(err); } 12 | return resolve(result); 13 | }); 14 | }); 15 | } 16 | 17 | async function advanceBlock() { 18 | // eslint-disable-next-line no-new 19 | new Promise((resolve, reject) => { 20 | web3.currentProvider.send({ 21 | jsonrpc: "2.0", 22 | method: "evm_mine", 23 | id: new Date().getTime(), 24 | }, (err, result) => { 25 | if (err) { return reject(err); } 26 | return resolve(result); 27 | }); 28 | }); 29 | } 30 | 31 | async function addBlocks(numBlocks) { 32 | for (let i = 0; i < numBlocks; i++) { 33 | // eslint-disable-next-line no-await-in-loop 34 | await advanceBlock(); 35 | } 36 | } 37 | 38 | module.exports = { 39 | advanceTime, 40 | advanceBlock, 41 | addBlocks, 42 | }; 43 | -------------------------------------------------------------------------------- /test/js/helpers/utils-test.js: -------------------------------------------------------------------------------- 1 | const Constants = require("../../../js/constants"); 2 | 3 | async function depositTx(bb, account, coin, loadamount) { 4 | bb.addTx({ 5 | loadAmount: loadamount, 6 | coin: coin, 7 | fromAx: account.ax, 8 | fromAy: account.ay, 9 | fromEthAddr: account.ethAddress, 10 | toAx: Constants.exitAx, 11 | toAy: Constants.exitAy, 12 | toEthAddr: Constants.exitEthAddr, 13 | onChain: true 14 | }); 15 | } 16 | 17 | module.exports = { 18 | depositTx, 19 | }; -------------------------------------------------------------------------------- /test/js/rollupaccount.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | 3 | const Account = require("../../js/rollupaccount"); 4 | const utils = require("../../js/utils"); 5 | const Constants = require("../../js/constants"); 6 | 7 | describe("Rollup account", () => { 8 | let account; 9 | 10 | it("Create rollup account", async () => { 11 | account = new Account(1); 12 | 13 | expect(account.ax).to.be.not.equal(undefined); 14 | expect(account.ay).to.be.not.equal(undefined); 15 | expect(account.ethAddress).to.be.not.equal(undefined); 16 | }); 17 | 18 | it("Sign transaction", async () => { 19 | const account2 = new Account(2); 20 | 21 | const tx = { 22 | toAx: account2.ax, 23 | toAy: account2.ay, 24 | toEthAddr: account2.ethAddress, 25 | coin: 0, 26 | amount: 500, 27 | nonce: 0, 28 | fee: Constants.fee["1%"] 29 | }; 30 | 31 | account.signTx(tx); 32 | 33 | expect(tx.fromAx).to.be.equal(account.ax); 34 | expect(tx.fromAy).to.be.equal(account.ay); 35 | expect(tx.fromEthAddr).to.be.equal(account.ethAddress); 36 | 37 | expect(tx.r8x).to.be.not.equal(undefined); 38 | expect(tx.r8y).to.be.not.equal(undefined); 39 | expect(tx.s).to.be.not.equal(undefined); 40 | 41 | // Verify transaction 42 | const res = utils.verifyTxSig(tx); 43 | expect(res).to.be.equal(true); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /test/rollup-utils/babyjub-hd-keys.test.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | const babyKeys = require("../../rollup-utils/babyjub-hd-keys"); 3 | const eddsa = require("../../rollup-utils/eddsa-babyjub"); 4 | 5 | const { expect } = chai; 6 | 7 | describe("BabyJubjub key generation", () => { 8 | const mnemonic = "urban add pulse prefer exist recycle verb angle sell year more mosquito"; 9 | const privTest0 = "c72c427a1b6de6890c61254610ce2a5089b83fddab770177f1dc5fd574be39d3"; 10 | const privTest1 = "e26a07ed01c9784622200d4f40008dfd8b1163f11d250886c5f6a37a10df8a9f"; 11 | 12 | it("from mnemonic", () => { 13 | const hdKeys = babyKeys.fromMnemonic(mnemonic); 14 | const priv0 = hdKeys.getPrivate(0); 15 | const priv1 = hdKeys.getPrivate(1); 16 | expect(priv0.toString("hex")).to.be.equal(privTest0); 17 | expect(priv1.toString("hex")).to.be.equal(privTest1); 18 | }); 19 | 20 | it("from random", () => { 21 | const hdKeys = babyKeys.fromRandom(); 22 | const pubPoint = hdKeys.getPublic(0); 23 | const pubCompressed = hdKeys.getPublic(0, true); 24 | const priv = new eddsa.PrivateKey(hdKeys.getPrivate(0)); 25 | expect(priv.public().p[0].toString()).to.be.equal(pubPoint[0].toString()); 26 | expect(priv.public().p[1].toString()).to.be.equal(pubPoint[1].toString()); 27 | expect(priv.public().compress().toString()).to.be.equal(pubCompressed.toString()); 28 | }); 29 | 30 | it("from / to ethereum extended private key", () => { 31 | const hdKeys = babyKeys.fromMnemonic(mnemonic); 32 | const ethPriv = hdKeys.getEthExtendedPrivate(); 33 | const ethAddress = hdKeys.getEthAddress(); 34 | 35 | const hdKeys1 = babyKeys.fromEthExtendedPriv(ethPriv); 36 | const ethAddress1 = hdKeys1.getEthAddress(); 37 | expect(ethAddress).to.be.equal(ethAddress1); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /test/rollup-utils/rollup-account.test.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | 3 | const assert = chai.assert; 4 | 5 | const RollupAccount = require("../../js/rollupaccount"); 6 | 7 | describe("Rollup Account", function () { 8 | 9 | it("Check a normal TX works ok", async () => { 10 | const account = new RollupAccount("c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3"); 11 | 12 | assert.equal(account.ethAddress, "0x627306090abab3a6e1400e9345bc60c78a8bef57"); 13 | }); 14 | 15 | it("Should create a new account", async () => { 16 | const account = new RollupAccount(); 17 | 18 | assert.equal(account.ethAddress.length, 42); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /test/rollup-utils/rollup-tree-utils.test.js: -------------------------------------------------------------------------------- 1 | const chai = require("chai"); 2 | const rollupTreeUtils = require("../../rollup-utils/rollup-tree-utils"); 3 | const Scalar = require("ffjavascript").Scalar; 4 | 5 | const { expect } = chai; 6 | 7 | describe("Rollup tree utils", () => { 8 | const amountDeposit = Scalar.e(2); 9 | const tokenId = Scalar.e(3); 10 | const Ax = Scalar.e(30890499764467592830739030727222305800976141688008169211302); 11 | const Ay = Scalar.e(19826930437678088398923647454327426275321075228766562806246); 12 | const ethAddress = Scalar.fromString("0xe0fbce58cfaa72812103f003adce3f284fe5fc7c", 16); 13 | const nonce = Scalar.e(4); 14 | 15 | it("hash leaf rollup tree", async () => { 16 | // Result retrieved from 'RollupHelpersV2.sol' 17 | const hash = "14422829883928914365932440251151072538283151652611338902014520044711612747360"; 18 | const res = rollupTreeUtils.hashStateTree(amountDeposit, tokenId, Ax, Ay, ethAddress, nonce); 19 | 20 | expect(res.hash.toString()).to.be.equal(hash); 21 | }); 22 | }); -------------------------------------------------------------------------------- /test/rollup-utils/smt-leveldb.test.js: -------------------------------------------------------------------------------- 1 | const { expect } = require("chai"); 2 | const util = require("util"); 3 | const exec = util.promisify( require("child_process").exec); 4 | const SMT = require("circomlib").SMT; 5 | const Scalar = require("ffjavascript").Scalar; 6 | 7 | const { SMTLevelDb } = require("../../rollup-utils/smt-leveldb"); 8 | 9 | describe("Smt level Db", () => { 10 | const pathDb = `${__dirname}/tmp-smt`; 11 | let smt; 12 | 13 | after(async () => { 14 | await exec(`rm -rf ${pathDb}`); 15 | }); 16 | 17 | it("Create smt with levelDb database", async () => { 18 | const db = new SMTLevelDb(pathDb); 19 | const rt = await db.getRoot(); 20 | smt = new SMT(db, rt); 21 | 22 | expect(Scalar.isZero(smt.root)).to.be.equal(true); 23 | }); 24 | 25 | it("test all smt functions", async () => { 26 | const key1 = Scalar.e(111); 27 | const value1 = Scalar.e(222); 28 | const key2 = Scalar.e(333); 29 | const value2 = Scalar.e(444); 30 | const value3 = Scalar.e(555); 31 | 32 | await smt.insert(key1, value1); 33 | await smt.insert(key2, value2); 34 | let resValue1 = await smt.find(key1); 35 | expect(resValue1.foundValue.toString()).to.be.equal(value1.toString()); 36 | const resValue2 = await smt.find(key2); 37 | expect(resValue2.foundValue.toString()).to.be.equal(value2.toString()); 38 | await smt.delete(key2); 39 | try { 40 | await smt.find(key2); 41 | } catch (error) { 42 | expect((error.message).includes("Key not found in database")).to.be.equal(true); 43 | } 44 | await smt.update(key1, value3); 45 | resValue1 = await smt.find(key1); 46 | expect(resValue1.foundValue.toString()).to.be.equal(value3.toString()); 47 | }); 48 | }); 49 | -------------------------------------------------------------------------------- /tools/calculate_iden3rollupmagic.js: -------------------------------------------------------------------------------- 1 | 2 | const Scalar = require("ffjavascript").Scalar; 3 | var blake = require("blakejs"); 4 | 5 | const r = Scalar.fromString("21888242871839275222246405745257275088548364400416034343698204186575808495617"); 6 | 7 | const IDEN3_ROLLUP_TX = "IDEN3_ROLLUP_TX"; 8 | 9 | const h = blake.blake2bHex(IDEN3_ROLLUP_TX); 10 | 11 | const n = Scalar.mod(Scalar.fromString(h, 16), r); 12 | 13 | console.log(n.toString()); 14 | 15 | // Result: 1625792389453394788515067275302403776356063435417596283072371667635754651289 16 | -------------------------------------------------------------------------------- /tools/compute-fee-table.js: -------------------------------------------------------------------------------- 1 | // Set maximum fee to charge 2 | const topValueFee = 0.5; 3 | 4 | // Set division steps 5 | const divStep = new Array(14).fill(0); 6 | divStep[13] = 2.5; 7 | divStep[12] = 2; 8 | divStep[11] = 2; 9 | divStep[10] = 2.5; 10 | divStep[9] = 2; 11 | divStep[8] = 2; 12 | divStep[7] = 2.5; 13 | divStep[6] = 2; 14 | divStep[5] = 2; 15 | divStep[4] = 2.5; 16 | divStep[3] = 2; 17 | divStep[2] = 2; 18 | divStep[1] = 2.5; 19 | divStep[0] = 2; 20 | 21 | 22 | const table = new Array(16).fill(0); 23 | 24 | // Compute fee inverse table 25 | table[15] = 1 / topValueFee; 26 | for (let i = table.length - 2; i > 0; i--){ 27 | table[i] = table[i+1] * divStep[i-1]; 28 | } 29 | 30 | // Compute % fee table 31 | const tablePercentage = new Array(16).fill(0); 32 | tablePercentage[0] = "0%"; 33 | for (let i = 1; i < table.length; i++){ 34 | tablePercentage[i] = `${((1 / table[i]) * 100).toString()}%`; 35 | } 36 | 37 | // Compute decimal fee table 38 | const tableDecimal = new Array(16).fill(0); 39 | tableDecimal[0] = 0; 40 | for (let i = 1; i < table.length; i++){ 41 | tableDecimal[i] = 1 / table[i]; 42 | } 43 | 44 | // Compute decimal fee table 45 | const adjustedTable = new Array(16).fill(0); 46 | for (let i = 0; i < tableDecimal.length; i++){ 47 | adjustedTable[i] = tableDecimal[i] * 2**32; 48 | } 49 | 50 | // Compute decimal fee table 51 | const realFee = new Array(16).fill(0); 52 | for (let i = 0; i < tableDecimal.length; i++){ 53 | realFee[i] = (adjustedTable[i] / 2**32) * 100; 54 | } 55 | 56 | // console.log(table); 57 | console.log("tablePercentage: ", tablePercentage); 58 | console.log("tableDecimal: ", tableDecimal); 59 | 60 | console.log("AdjustedTable: ", adjustedTable); 61 | console.log("RealFee: ", realFee); --------------------------------------------------------------------------------