diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index b94c1715..b84ca88e 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,6 +12,7 @@ on: branches: - main - develop + - feature/fork-etrog jobs: build: diff --git a/src/constants.js b/src/constants.js index 7e9126e2..81e54a6b 100644 --- a/src/constants.js +++ b/src/constants.js @@ -80,5 +80,8 @@ module.exports.DELTA_TIMESTAMP_BYTES = 4; module.exports.INDEX_L1INFOTREE_BYTES = 4; module.exports.TYPE_BYTES = 1; +// Bridge +module.exports.GLOBAL_INDEX_MAINNET_FLAG = Scalar.pow(2, 64); + // Common module.exports.ZERO_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000'; diff --git a/src/mt-bridge-utils.js b/src/mt-bridge-utils.js index ac78f433..6f8b1a24 100644 --- a/src/mt-bridge-utils.js +++ b/src/mt-bridge-utils.js @@ -1,4 +1,6 @@ const ethers = require('ethers'); +const { Scalar } = require('ffjavascript'); +const Constants = require('./constants'); /** * Calculate an array zero hashes of @@ -51,8 +53,26 @@ function getLeafValue(leafType, originNetwork, originAddress, destinationNetwork return ethers.utils.solidityKeccak256(['uint8', 'uint32', 'address', 'uint32', 'address', 'uint256', 'bytes32'], [leafType, originNetwork, originAddress, destinationNetwork, destinationAddress, amount, metadataHash]); } +/** + * Compute globalIndex + * | 191 bits | 1 bit | 32 bits | 32 bits | + * | 0 | mainnetFlag | indexRollup | indexLocal | + * @param {Number | BigInt} indexLocal - leaf index on the mainnet exit tree + * @param {Number | BigInt} indexRollup - leaf index on the rollup tree + * @param {Bool} isMainnet flag that indicates if it is mainnet + * @returns {BigInt} global index + */ +function computeGlobalIndex(indexLocal, indexRollup, isMainnet) { + if (isMainnet === true) { + return Scalar.add(indexLocal, Constants.GLOBAL_INDEX_MAINNET_FLAG); + } + + return Scalar.add(indexLocal, Scalar.mul(indexRollup, Scalar.pow(2, 32))); +} + module.exports = { generateZeroHashes, verifyMerkleProof, getLeafValue, + computeGlobalIndex, }; diff --git a/test/helpers/test-vectors/merkle-tree-bridge/global-index.json b/test/helpers/test-vectors/merkle-tree-bridge/global-index.json new file mode 100644 index 00000000..dd9b8f6b --- /dev/null +++ b/test/helpers/test-vectors/merkle-tree-bridge/global-index.json @@ -0,0 +1,50 @@ +[ + { + "indexLocal": "0", + "indexRollup": "0", + "isMainnet": false, + "expectedGlobalIndex": "0" + }, + { + "indexLocal": "0", + "indexRollup": "0", + "isMainnet": true, + "expectedGlobalIndex": "18446744073709551616" + }, + { + "indexLocal": "1", + "indexRollup": "0", + "isMainnet": false, + "expectedGlobalIndex": "1" + }, + { + "indexLocal": "1", + "indexRollup": "0", + "isMainnet": true, + "expectedGlobalIndex": "18446744073709551617" + }, + { + "indexLocal": "0", + "indexRollup": "1", + "isMainnet": false, + "expectedGlobalIndex": "4294967296" + }, + { + "indexLocal": "0", + "indexRollup": "1", + "isMainnet": true, + "expectedGlobalIndex": "18446744073709551616" + }, + { + "indexLocal": "18446744073709551615", + "indexRollup": "18446744073709551615", + "isMainnet": false, + "expectedGlobalIndex": "79228162532711081662958534655" + }, + { + "indexLocal": "18446744073709551615", + "indexRollup": "18446744073709551615", + "isMainnet": true, + "expectedGlobalIndex": "36893488147419103231" + } +] \ No newline at end of file diff --git a/test/mt-bridge-utils.test.js b/test/mt-bridge-utils.test.js new file mode 100644 index 00000000..1d519a0c --- /dev/null +++ b/test/mt-bridge-utils.test.js @@ -0,0 +1,45 @@ +const fs = require('fs'); +const path = require('path'); +const { Scalar } = require('ffjavascript'); +const { expect } = require('chai'); +const { argv } = require('yargs'); +const { + computeGlobalIndex, +} = require('../index').mtBridgeUtils; +const { pathTestVectors } = require('./helpers/test-utils'); + +describe('Merkle Bridge Utils', () => { + const pathTests = path.join(pathTestVectors, 'merkle-tree-bridge/global-index.json'); + + let update; + let testVectors; + + before(async () => { + testVectors = JSON.parse(fs.readFileSync(pathTests)); + update = argv.update === true; + }); + + it('computeGlobalIndex', async () => { + for (let i = 0; i < testVectors.length; i++) { + const { + indexLocal, indexRollup, isMainnet, expectedGlobalIndex, + } = testVectors[i]; + + const computedGlobalIndex = computeGlobalIndex( + Scalar.e(indexLocal), + Scalar.e(indexRollup), + isMainnet, + ); + + if (update) { + testVectors[i].expectedGlobalIndex = computedGlobalIndex.toString(); + } else { + expect(computedGlobalIndex.toString()).to.be.equal(expectedGlobalIndex); + } + } + + if (update) { + fs.writeFileSync(pathTests, JSON.stringify(testVectors, null, 2)); + } + }); +});