diff --git a/config/default.js b/config/default.js index 9284b9656..c0a4997b6 100644 --- a/config/default.js +++ b/config/default.js @@ -32,7 +32,6 @@ module.exports = { PROOF_QUEUE: 'generate-proof', MAX_QUEUE: 5, TIMBER_HEIGHT: 32, - TXHASH_TREE_HEIGHT: 5, CONFIRMATION_POLL_TIME: 1000, CONFIRMATIONS: 12, DEFAULT_ACCOUNT_NUM: 10, @@ -88,7 +87,7 @@ module.exports = { CURVE: process.env.CURVE || 'bn128', TRANSACTIONS_PER_BLOCK: Number(process.env.TRANSACTIONS_PER_BLOCK) || 2, - RETRIES: Number(process.env.AUTOSTART_RETRIES) || 50, + RETRIES: Number(process.env.AUTOSTART_RETRIES) || 150, USE_STUBS: process.env.USE_STUBS === 'true', VK_IDS: { deposit: 0, transfer: 1, withdraw: 2 }, // used as an enum to mirror the Shield contracts enum for vk types. The keys of this object must correspond to a 'folderpath' (the .zok file without the '.zok' bit) BN128_GROUP_ORDER: 21888242871839275222246405745257275088548364400416034343698204186575808495617n, diff --git a/nightfall-client/src/event-handlers/block-proposed.mjs b/nightfall-client/src/event-handlers/block-proposed.mjs index 30d95bb86..6ab4146e2 100644 --- a/nightfall-client/src/event-handlers/block-proposed.mjs +++ b/nightfall-client/src/event-handlers/block-proposed.mjs @@ -24,7 +24,7 @@ import { } from '../services/database.mjs'; import { decryptCommitment } from '../services/commitment-sync.mjs'; -const { TIMBER_HEIGHT, TXHASH_TREE_HEIGHT, HASH_TYPE, TXHASH_TREE_HASH_TYPE } = config; +const { TIMBER_HEIGHT, HASH_TYPE, TXHASH_TREE_HASH_TYPE } = config; const { ZERO } = constants; /** @@ -148,6 +148,11 @@ async function blockProposedEventHandler(data, syncing) { }), ); + let height = 1; + while (2 ** height < block.transactionHashes.length) { + ++height; + } + // If this L2 block contains withdraw transactions known to this client, // the following needs to be saved for later to be used during finalise/instant withdraw // 1. Save sibling path for the withdraw transaction hash that is present in transaction hashes timber tree @@ -155,16 +160,12 @@ async function blockProposedEventHandler(data, syncing) { // transactions hash is a linear hash of the transactions in an L2 block which is calculated during proposeBlock in // the contract if ((await countWithdrawTransactionHashes(block.transactionHashes)) > 0) { - const transactionHashesTimber = new Timber( - ...[, , , ,], - TXHASH_TREE_HASH_TYPE, - TXHASH_TREE_HEIGHT, - ); - const updatedTransctionHashesTimber = Timber.statelessUpdate( + const transactionHashesTimber = new Timber(...[, , , ,], TXHASH_TREE_HASH_TYPE, height); + const updatedTransactionHashesTimber = Timber.statelessUpdate( transactionHashesTimber, block.transactionHashes, TXHASH_TREE_HASH_TYPE, - TXHASH_TREE_HEIGHT, + height, ); await Promise.all( @@ -172,12 +173,12 @@ async function blockProposedEventHandler(data, syncing) { block.transactionHashes.map(async (transactionHash, i) => { if (await isTransactionHashWithdraw(transactionHash)) { const siblingPathTransactionHash = - updatedTransctionHashesTimber.getSiblingPath(transactionHash); + updatedTransactionHashesTimber.getSiblingPath(transactionHash); return setTransactionHashSiblingInfo( transactionHash, siblingPathTransactionHash, transactionHashesTimber.leafCount + i, - updatedTransctionHashesTimber.root, + updatedTransactionHashesTimber.root, ); } }), diff --git a/nightfall-deployer/contracts/Config.sol b/nightfall-deployer/contracts/Config.sol index 277dd8d02..95c6b62e3 100644 --- a/nightfall-deployer/contracts/Config.sol +++ b/nightfall-deployer/contracts/Config.sol @@ -11,7 +11,7 @@ contract Config is Ownable, Structures { uint256 constant ROTATE_PROPOSER_BLOCKS = 4; uint256 constant COOLING_OFF_PERIOD = 1 weeks; bytes32 constant ZERO = bytes32(0); - uint256 constant TXHASH_TREE_HEIGHT = 5; + uint256 constant TRANSACTIONS_PER_BLOCK = 32; address bootProposer; address bootChallenger; diff --git a/nightfall-deployer/contracts/Shield.sol b/nightfall-deployer/contracts/Shield.sol index 68d406ba3..698c67cb5 100644 --- a/nightfall-deployer/contracts/Shield.sol +++ b/nightfall-deployer/contracts/Shield.sol @@ -116,7 +116,7 @@ contract Shield is Stateful, Config, Key_Registry, ReentrancyGuardUpgradeable, P Block calldata b, Transaction calldata t, uint256 index, - bytes32[6] calldata siblingPath + bytes32[] calldata siblingPath ) external view returns (bool) { // check this block is a real one, in the queue, not something made up. state.areBlockAndTransactionReal(b, t, index, siblingPath); @@ -149,7 +149,7 @@ contract Shield is Stateful, Config, Key_Registry, ReentrancyGuardUpgradeable, P Block calldata b, Transaction calldata t, uint256 index, - bytes32[6] calldata siblingPath + bytes32[] calldata siblingPath ) external { // check this block is a real one, in the queue, not something made up and that the transaction exists in the block state.areBlockAndTransactionReal(b, t, index, siblingPath); @@ -224,7 +224,7 @@ contract Shield is Stateful, Config, Key_Registry, ReentrancyGuardUpgradeable, P Block calldata b, Transaction calldata t, uint256 index, - bytes32[6] calldata siblingPath + bytes32[] calldata siblingPath ) external payable nonReentrant { // The transaction is a withdrawal transaction require(t.transactionType == TransactionTypes.WITHDRAW, 'Can only advance withdrawals'); diff --git a/nightfall-deployer/contracts/State.sol b/nightfall-deployer/contracts/State.sol index f75c6d7e1..d3df3b4ac 100644 --- a/nightfall-deployer/contracts/State.sol +++ b/nightfall-deployer/contracts/State.sol @@ -93,7 +93,7 @@ contract State is Initializable, ReentrancyGuardUpgradeable, Pausable, Config { require(BLOCK_STAKE <= msg.value, 'The stake payment is incorrect'); require(b.proposer == msg.sender, 'The proposer address is not the sender'); // set the maximum tx/block to prevent unchallengably large blocks - require(t.length < 33, 'The block has too many transactions'); + require(t.length <= TRANSACTIONS_PER_BLOCK, 'The block has too many transactions'); uint256 feePaymentsEth = 0; uint256 feePaymentsMatic = 0; @@ -126,8 +126,17 @@ contract State is Initializable, ReentrancyGuardUpgradeable, Pausable, Config { } let transactionHashesRoot // calculate and store transaction hashes root + let height := 1 for { - let i := 5 + + } lt(exp(2, height), t.length) { + + } { + height := add(height, 1) + } + + for { + let i := height } gt(i, 0) { i := sub(i, 1) } { @@ -328,7 +337,7 @@ contract State is Initializable, ReentrancyGuardUpgradeable, Pausable, Config { Block calldata b, Transaction calldata t, uint256 index, - bytes32[6] calldata siblingPath + bytes32[] calldata siblingPath ) public view { bytes32 blockHash = Utils.hashBlock(b); require(blockHashes[b.blockNumberL2].blockHash == blockHash, 'This block does not exist'); diff --git a/nightfall-deployer/contracts/Utils.sol b/nightfall-deployer/contracts/Utils.sol index 24ed8e227..4db4ba534 100644 --- a/nightfall-deployer/contracts/Utils.sol +++ b/nightfall-deployer/contracts/Utils.sol @@ -153,8 +153,16 @@ library Utils { } { mstore(add(transactionHashesPos, mul(0x20, i)), mload(add(leavesPos, mul(0x20, i)))) } + let height := 1 for { - let i := 5 + + } lt(exp(2, height), length) { + + } { + height := add(height, 1) + } + for { + let i := height } gt(i, 0) { i := sub(i, 1) } { @@ -178,11 +186,11 @@ library Utils { } function checkPath( - bytes32[6] calldata siblingPath, + bytes32[] calldata siblingPath, uint256 leafIndex, bytes32 node ) public pure returns (bool) { - for (uint256 i = 5; i > 0; i--) { + for (uint256 i = siblingPath.length - 1; i > 0; i--) { if (leafIndex % 2 == 0) { node = keccak256(abi.encodePacked(node, siblingPath[i])); } else { diff --git a/nightfall-optimist/src/classes/block.mjs b/nightfall-optimist/src/classes/block.mjs index 303389f9a..1f93fd439 100644 --- a/nightfall-optimist/src/classes/block.mjs +++ b/nightfall-optimist/src/classes/block.mjs @@ -7,7 +7,7 @@ import constants from 'common-files/constants/index.mjs'; import { getLatestBlockInfo, getTreeByBlockNumberL2 } from '../services/database.mjs'; import { buildBlockSolidityStruct, calcBlockHash } from '../services/block-utils.mjs'; -const { TIMBER_HEIGHT, TXHASH_TREE_HEIGHT, HASH_TYPE, TXHASH_TREE_HASH_TYPE } = config; +const { TIMBER_HEIGHT, HASH_TYPE, TXHASH_TREE_HASH_TYPE } = config; const { ZERO } = constants; /** @@ -162,12 +162,17 @@ class Block { static calcTransactionHashesRoot(transactions) { const transactionHashes = transactions.map(t => t.transactionHash); - const timber = new Timber(...[, , , ,], TXHASH_TREE_HASH_TYPE, TXHASH_TREE_HEIGHT); + let height = 1; + while (2 ** height < transactionHashes.length) { + ++height; + } + + const timber = new Timber(...[, , , ,], TXHASH_TREE_HASH_TYPE, height); const updatedTimber = Timber.statelessUpdate( timber, transactionHashes, TXHASH_TREE_HASH_TYPE, - TXHASH_TREE_HEIGHT, + height, ); return updatedTimber.root; } diff --git a/wallet/src/nightfall-browser/event-handlers/block-proposed.js b/wallet/src/nightfall-browser/event-handlers/block-proposed.js index cd45f151f..06144b64a 100644 --- a/wallet/src/nightfall-browser/event-handlers/block-proposed.js +++ b/wallet/src/nightfall-browser/event-handlers/block-proposed.js @@ -26,7 +26,7 @@ import { } from '../services/database'; import { edwardsDecompress } from '../../common-files/utils/curve-maths/curves'; -const { TIMBER_HEIGHT, TXHASH_TREE_HEIGHT, HASH_TYPE, TXHASH_TREE_HASH_TYPE } = global.config; +const { TIMBER_HEIGHT, HASH_TYPE, TXHASH_TREE_HASH_TYPE } = global.config; const { ZERO } = global.nightfallConstants; /** @@ -154,6 +154,11 @@ async function blockProposedEventHandler(data, zkpPrivateKeys, nullifierKeys) { }), ); + let height = 1; + while (2 ** height < block.transactionHashes.length) { + ++height; + } + // If this L2 block contains withdraw transactions known to this client, // the following needs to be saved for later to be used during finalise/instant withdraw // 1. Save sibling path for the withdraw transaction hash that is present in transaction hashes timber tree @@ -161,17 +166,13 @@ async function blockProposedEventHandler(data, zkpPrivateKeys, nullifierKeys) { // transactions hash is a linear hash of the transactions in an L2 block which is calculated during proposeBlock in // the contract if ((await countWithdrawTransactionHashes(block.transactionHashes)) > 0) { - const transactionHashesTimber = new Timber( - ...[, , , ,], - TXHASH_TREE_HASH_TYPE, - TXHASH_TREE_HEIGHT, - ); + const transactionHashesTimber = new Timber(...[, , , ,], TXHASH_TREE_HASH_TYPE, height); - const updatedTransctionHashesTimber = Timber.statelessUpdate( + const updatedTransactionHashesTimber = Timber.statelessUpdate( transactionHashesTimber, block.transactionHashes, TXHASH_TREE_HASH_TYPE, - TXHASH_TREE_HEIGHT, + height, ); await Promise.all( @@ -179,12 +180,12 @@ async function blockProposedEventHandler(data, zkpPrivateKeys, nullifierKeys) { block.transactionHashes.map(async (transactionHash, i) => { if (await isTransactionHashWithdraw(transactionHash)) { const siblingPathTransactionHash = - updatedTransctionHashesTimber.getSiblingPath(transactionHash); + updatedTransactionHashesTimber.getSiblingPath(transactionHash); return setTransactionHashSiblingInfo( transactionHash, siblingPathTransactionHash, transactionHashesTimber.leafCount + i, - updatedTransctionHashesTimber.root, + updatedTransactionHashesTimber.root, ); } }),