Skip to content

Commit

Permalink
Merge pull request #118 from 0xPolygonHermez/feature/l1-info-tree-final
Browse files Browse the repository at this point in the history
add l1InfoTree
  • Loading branch information
krlosMata authored Nov 21, 2023
2 parents 687b197 + 60f62e6 commit 3e71479
Show file tree
Hide file tree
Showing 38 changed files with 2,057 additions and 3,368 deletions.
1 change: 0 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ module.exports = {
'no-console': [2, { allow: ['warn', 'error'] }],
'import/prefer-default-export': [0],
'lines-between-class-members': ['error', 'always', { exceptAfterSingleLine: true }],
'multiline-comment-style': 'error',
'no-await-in-loop': 'off',
'newline-before-return': 'error',
},
Expand Down
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -114,4 +114,7 @@ test/cache
package-lock.json

# ignore genesis
tools/fill-genesis/*.ignore.json
tools/fill-genesis/*.ignore.json

# input examples
tools/inputs-examples/
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ module.exports.getPoseidon = require('./src/poseidon_opt');
module.exports.MTBridge = require('./src/mt-bridge');
module.exports.mtBridgeUtils = require('./src/mt-bridge-utils');
module.exports.Database = require('./src/database');
module.exports.l1InfoTreeUtils = require('./src/l1-info-tree-utils');
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
"main": "index.js",
"scripts": {
"setup": "npm i",
"test": "cd test && npx hardhat compile && cd .. && npx mocha ./test/*.test.js && npm run test:e2e && npm run test:blockinfo && npm run test:selfdestruct",
"test": "cd test && npx hardhat compile && cd .. && npx mocha ./test/*.test.js && npm run test:e2e && npm run test:blockinfo && npm run test:selfdestruct && npm run test:etrog",
"test:etrog": "npx mocha ./test/processor.test.js --etrog",
"test:e2e": "npx mocha ./test/processor.test.js --e2e",
"test:blockinfo": "npx mocha ./test/processor.test.js --blockinfo",
"test:selfdestruct": "npx mocha ./test/processor.test.js --selfdestruct",
"eslint": "npx eslint src/** test/*.test.js && npx eslint tools",
"eslint:fix": "npx eslint src/** test/*.test.js --fix && npx eslint tools --fix",
"test:update": "./tools/update-tests/update-tests.sh",
"test:database": "npx mocha ./test/database.test.js"
"test:database": "npx mocha ./test/database.test.js",
"build:inputs": "npx mocha ./test/processor.test.js --update --geninputs && npx mocha ./test/processor.test.js --etrog --update --geninputs"
},
"repository": {
"type": "git",
Expand All @@ -35,7 +37,7 @@
},
"homepage": "https://github.com/0xPolygonHermez/zkevm-commonjs#readme",
"devDependencies": {
"@0xpolygonhermez/zkevm-contracts": "github:0xPolygonHermez/zkevm-contracts#v2.0.0-fork.5",
"@0xpolygonhermez/zkevm-contracts": "github:0xPolygonHermez/zkevm-contracts#feature/l1-info-tree",
"@ethersproject/abi": "^5.6.4",
"@nomiclabs/hardhat-ethers": "^2.1.0",
"@nomiclabs/hardhat-waffle": "^2.0.2",
Expand Down
6 changes: 5 additions & 1 deletion src/block-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const constants = require('./constants');
const {
keyBlockHeaderParams, keyTxLogs, keyTxStatus, keyTxHash, keyTxCumulativeGasUsed,
} = require('./block-keys-utils');

/**
* Set a state of an ethereum address
* @param {Object} smt merkle tree structure
Expand All @@ -14,22 +15,25 @@ const {
* @param {Scalar|Number} gasLimit block gas limit
* @param {Scalar|Number} timestamp block timestamp
* @param {String} GER block's global exit root
* @param {String} blockHashL1 block hash L1
* @returns {Array[Field]} new state root
*/
async function initBlockHeader(smt, root, oldBlockHash, coinbase, blockNumber, gasLimit, timestamp, GER) {
async function initBlockHeader(smt, root, oldBlockHash, coinbase, blockNumber, gasLimit, timestamp, GER, blockHashL1) {
const keyBlockHash = await keyBlockHeaderParams(constants.INDEX_BLOCK_HEADER_PARAM_BLOCK_HASH);
const keyCoinbase = await keyBlockHeaderParams(constants.INDEX_BLOCK_HEADER_PARAM_COINBASE);
const keyBlockNumber = await keyBlockHeaderParams(constants.INDEX_BLOCK_HEADER_PARAM_NUMBER);
const keyGasLimit = await keyBlockHeaderParams(constants.INDEX_BLOCK_HEADER_PARAM_GAS_LIMIT);
const keyTimestamp = await keyBlockHeaderParams(constants.INDEX_BLOCK_HEADER_PARAM_TIMESTAMP);
const keyGER = await keyBlockHeaderParams(constants.INDEX_BLOCK_HEADER_PARAM_GER);
const keyBlockHashL1 = await keyBlockHeaderParams(constants.INDEX_BLOCK_HEADER_PARAM_BLOCK_HASH_L1);

let result = await smt.set(root, keyBlockHash, Scalar.e(oldBlockHash));
result = await smt.set(result.newRoot, keyCoinbase, Scalar.e(coinbase));
result = await smt.set(result.newRoot, keyBlockNumber, Scalar.e(blockNumber));
result = await smt.set(result.newRoot, keyGasLimit, Scalar.e(gasLimit));
result = await smt.set(result.newRoot, keyTimestamp, Scalar.e(timestamp));
result = await smt.set(result.newRoot, keyGER, Scalar.e(GER));
result = await smt.set(result.newRoot, keyBlockHashL1, Scalar.e(blockHashL1));

return result.newRoot;
}
Expand Down
14 changes: 12 additions & 2 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ module.exports.INDEX_BLOCK_HEADER_PARAM_NUMBER = 2;
module.exports.INDEX_BLOCK_HEADER_PARAM_GAS_LIMIT = 3;
module.exports.INDEX_BLOCK_HEADER_PARAM_TIMESTAMP = 4;
module.exports.INDEX_BLOCK_HEADER_PARAM_GER = 5;
module.exports.INDEX_BLOCK_HEADER_PARAM_GAS_USED = 6;
module.exports.INDEX_BLOCK_HEADER_PARAM_BLOCK_HASH_L1 = 6;
module.exports.INDEX_BLOCK_HEADER_PARAM_GAS_USED = 7;

// SMT constant
module.exports.BYTECODE_ELEMENTS_HASH = 8;
Expand All @@ -58,9 +59,10 @@ module.exports.GLOBAL_EXIT_ROOT_STORAGE_POS = 0;
module.exports.LOCAL_EXIT_ROOT_STORAGE_POS = 1;
module.exports.BLOCK_GAS_LIMIT = 2 ** 32 - 1;
module.exports.TX_GAS_LIMIT = 30000000;

module.exports.BATCH_DIFFICULTY = 0;
module.exports.ADDRESS_SYSTEM = '0x000000000000000000000000000000005ca1ab1e';

// Adress system storage slots
module.exports.LAST_BLOCK_STORAGE_POS = 0;
module.exports.STATE_ROOT_STORAGE_POS = 1;
module.exports.TIMESTAMP_STORAGE_POS = 2;
Expand All @@ -72,3 +74,11 @@ module.exports.BRIDGE_LEAF_TYPE_MESSAGE = 1;

// Tx Types
module.exports.TX_CHANGE_L2_BLOCK = 11;

// Tx Type 11: Change L2 Block constants
module.exports.DELTA_TIMESTAMP_BYTES = 4;
module.exports.INDEX_L1INFOTREE_BYTES = 4;
module.exports.TYPE_BYTES = 1;

// Common
module.exports.ZERO_BYTES32 = '0x0000000000000000000000000000000000000000000000000000000000000000';
16 changes: 8 additions & 8 deletions src/contract-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,34 @@ const { Scalar } = require('ffjavascript');
const { sha256Snark, padZeros } = require('./utils');

/**
* Compute accumulateInputHash = Keccak256(oldAccInputHash, batchHashData, historicGERRoot, timestampLimit, seqAddress)
* Compute accumulateInputHash = Keccak256(oldAccInputHash, batchHashData, l1InfoRoot, timestampLimit, seqAddress)
* @param {String} oldAccInputHash - old accumulateInputHash
* @param {String} batchHashData - Batch hash data
* @param {String} historicGERRoot - Global Exit Root
* @param {String} l1InfoRoot - Global Exit Root
* @param {Number} timestampLimit - Block timestampLimit
* @param {String} sequencerAddress - Sequencer address
* @param {Number} isForced - Flag for forced transaction
* @param {String} forcedBlockHashL1 - Flag for forced transaction
* @returns {String} - accumulateInputHash in hex encoding
*/
function calculateAccInputHash(
oldAccInputHash,
batchHashData,
historicGERRoot,
l1InfoRoot,
timestampLimit,
sequencerAddress,
isForced = 0,
forcedBlockHashL1,
) {
const oldAccInputHashHex = `0x${Scalar.e(oldAccInputHash).toString(16).padStart(64, '0')}`;

const hashKeccak = ethers.utils.solidityKeccak256(
['bytes32', 'bytes32', 'bytes32', 'uint64', 'address', 'bool'],
['bytes32', 'bytes32', 'bytes32', 'uint64', 'address', 'bytes32'],
[
oldAccInputHashHex,
batchHashData,
historicGERRoot,
l1InfoRoot,
timestampLimit,
sequencerAddress,
isForced,
forcedBlockHashL1,
],
);

Expand Down
23 changes: 23 additions & 0 deletions src/l1-info-tree-utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const ethers = require('ethers');

/**
* Calculate L1InfoTree leaf value
* @param {String} globalExitRoot - global exit root
* @param {String} blockHash - block hash
* @param {BigInt} timestamp - Timestamp
* @returns {Sting} - Leaf value
*/
function getL1InfoTreeValue(globalExitRoot, blockHash, timestamp) {
return ethers.utils.solidityKeccak256(
['bytes32', 'bytes32', 'uint64'],
[
globalExitRoot,
blockHash,
timestamp,
],
);
}

module.exports = {
getL1InfoTreeValue,
};
2 changes: 1 addition & 1 deletion src/mt-bridge-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function verifyMerkleProof(leaf, smtProof, index, root) {
* @param {String} destinationAddress - Destination address
* @param {BigNumber} amount - Amount of tokens
* @param {BigNumber} metadataHash - Hash of the metadata
* @returns {Boolean} - Leaf value
* @returns {String} - Leaf value
*/
function getLeafValue(leafType, originNetwork, originAddress, destinationNetwork, destinationAddress, amount, metadataHash) {
return ethers.utils.solidityKeccak256(['uint8', 'uint32', 'address', 'uint32', 'address', 'uint256', 'bytes32'], [leafType, originNetwork, originAddress, destinationNetwork, destinationAddress, amount, metadataHash]);
Expand Down
88 changes: 82 additions & 6 deletions src/processor-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ const { ethers } = require('ethers');
const { Scalar } = require('ffjavascript');
const Constants = require('./constants');
const smtUtils = require('./smt-utils');
const { valueToHexStr } = require('./utils');

/**
* Extract an integer from a byte array
* @param {Uint8Array} data - Byte array
Expand Down Expand Up @@ -102,7 +104,7 @@ function addressToHexStringRlp(address) {

/**
* Convert a standar rawTx of ethereum [rlp(nonce,gasprice,gaslimit,to,value,data,r,s,v)]
* to our custom raw tx [rlp(nonce,gasprice,gaslimit,to,value,data,0,0)|r|s|v]
* to our custom raw tx [rlp(nonce,gasprice,gaslimit,to,value,data,0,0)|r|s|v|effectivePercentage]
* @param {String} rawTx - Standar raw transaction
* @returns {String} - Custom raw transaction
*/
Expand Down Expand Up @@ -141,7 +143,12 @@ function encodedStringToArray(encodedTransactions) {
let offset = 0;

while (offset < encodedTxBytes.length) {
if (encodedTxBytes[offset] >= 0xf8) {
if (encodedTxBytes[offset] === Constants.TX_CHANGE_L2_BLOCK) {
const bytesToRead = 1 + Constants.DELTA_TIMESTAMP_BYTES + Constants.INDEX_L1INFOTREE_BYTES;
const tx = ethers.utils.hexlify(encodedTxBytes.slice(offset, offset + bytesToRead));
decodedRawTx.push(tx);
offset += bytesToRead;
} else if (encodedTxBytes[offset] >= 0xf8) {
const lengthLength = encodedTxBytes[offset] - 0xf7;
if (offset + 1 + lengthLength > encodedTxBytes.length) {
throw new Error('encodedTxBytes short segment too short');
Expand All @@ -152,16 +159,18 @@ function encodedStringToArray(encodedTransactions) {
throw new Error('encodedTxBytes long segment too short');
}

decodedRawTx.push(ethers.utils.hexlify(encodedTxBytes.slice(offset, offset + 1 + lengthLength + length + Constants.SIGNATURE_BYTES + Constants.EFFECTIVE_PERCENTAGE_BYTES)));
offset = offset + 1 + lengthLength + length + Constants.SIGNATURE_BYTES + Constants.EFFECTIVE_PERCENTAGE_BYTES;
const bytesToRead = 1 + lengthLength + length + Constants.SIGNATURE_BYTES + Constants.EFFECTIVE_PERCENTAGE_BYTES;
decodedRawTx.push(ethers.utils.hexlify(encodedTxBytes.slice(offset, offset + bytesToRead)));
offset += bytesToRead;
} else if (encodedTxBytes[offset] >= 0xc0) {
const length = encodedTxBytes[offset] - 0xc0;
if (offset + 1 + length > encodedTxBytes.length) {
throw new Error('encodedTxBytes array too short');
}

decodedRawTx.push(ethers.utils.hexlify(encodedTxBytes.slice(offset, offset + 1 + length + Constants.SIGNATURE_BYTES + Constants.EFFECTIVE_PERCENTAGE_BYTES)));
offset = offset + 1 + length + Constants.SIGNATURE_BYTES + Constants.EFFECTIVE_PERCENTAGE_BYTES;
const bytesToRead = 1 + length + Constants.SIGNATURE_BYTES + Constants.EFFECTIVE_PERCENTAGE_BYTES;
decodedRawTx.push(ethers.utils.hexlify(encodedTxBytes.slice(offset, offset + bytesToRead)));
offset += bytesToRead;
} else {
throw new Error('Error encodedStringToArray');
}
Expand Down Expand Up @@ -297,10 +306,25 @@ function decodeCustomRawTxProverMethod(encodedTransactions) {

txDecoded.r = ethers.utils.hexlify(encodedTxBytes.slice(offset, offset + lenR));
offset += lenR;
// r: assert to read 32 bytes
if (txDecoded.r.length !== (2 + 2 * lenR)) {
throw new Error('Invalid signature length: R');
}

txDecoded.s = ethers.utils.hexlify(encodedTxBytes.slice(offset, offset + lenS));
offset += lenS;
// s: assert to read 32 bytes
if (txDecoded.s.length !== (2 + 2 * lenS)) {
throw new Error('Invalid signature length: S');
}

txDecoded.v = ethers.utils.hexlify(encodedTxBytes.slice(offset, offset + lenV));
offset += lenV;
// v: assert to read 32 bytes
if (txDecoded.v.length !== (2 + 2 * lenV)) {
throw new Error('Invalid signature length: V');
}

txDecoded.effectivePercentage = ethers.utils.hexlify(encodedTxBytes.slice(offset, offset + lenEffectivePercentage));
offset += lenEffectivePercentage;
if (txDecoded.effectivePercentage === '0x') {
Expand Down Expand Up @@ -336,6 +360,56 @@ async function computeL2TxHash(tx) {
return txHash;
}

/**
* Decode string into a changeL2Transaction transaction type
* @param {String} _rawTx
* @returns {Object} transaction object
*/
async function decodeChangeL2BlockTx(_rawTx) {
const tx = {};

let offsetChars = 0;
const serializedTx = _rawTx.startsWith('0x') ? _rawTx.slice(2) : _rawTx;

let charsToRead = Constants.TYPE_BYTES * 2;

tx.type = parseInt(serializedTx.slice(offsetChars, offsetChars + charsToRead), 16);
offsetChars += charsToRead;

charsToRead = Constants.DELTA_TIMESTAMP_BYTES * 2;
tx.deltaTimestamp = Scalar.fromString(serializedTx.slice(offsetChars, offsetChars + charsToRead), 16);
offsetChars += charsToRead;

charsToRead = Constants.INDEX_L1INFOTREE_BYTES * 2;
tx.indexL1InfoTree = parseInt(serializedTx.slice(offsetChars, offsetChars + charsToRead), 16);

return tx;
}

/**
* Serialize transaction for the batch
* fields: [type | deltaTimestamp | indexL1InfoTree ]
* bytes: [ 1 | 4 | 4 ]
* @param {Object} tx - transaction object
* @returns {String} - Serialized tx in hexadecimal string
*/
function serializeChangeL2Block(tx) {
let data = Scalar.e(0);

let offsetBits = 0;

data = Scalar.add(data, Scalar.shl(tx.indexL1InfoTree, offsetBits));
offsetBits += Constants.INDEX_L1INFOTREE_BYTES * 8;

data = Scalar.add(data, Scalar.shl(tx.deltaTimestamp, offsetBits));
offsetBits += Constants.DELTA_TIMESTAMP_BYTES * 8;

data = Scalar.add(data, Scalar.shl(tx.type, offsetBits));
offsetBits += Constants.TYPE_BYTES * 8;

return valueToHexStr(data).padStart(offsetBits / 4, '0');
}

module.exports = {
decodeCustomRawTxProverMethod,
rawTxToCustomRawTx,
Expand All @@ -346,4 +420,6 @@ module.exports = {
addressToHexStringRlp,
computeEffectiveGasPrice,
computeL2TxHash,
decodeChangeL2BlockTx,
serializeChangeL2Block,
};
Loading

0 comments on commit 3e71479

Please sign in to comment.