Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Variable height merkle trees for transactions #906

Merged
merged 3 commits into from
Sep 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
21 changes: 11 additions & 10 deletions nightfall-client/src/event-handlers/block-proposed.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -148,36 +148,37 @@ 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
// 2. Save transactions hash of the transactions in this L2 block that contains withdraw transactions for this client
// 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(
// eslint-disable-next-line consistent-return
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,
);
}
}),
Expand Down
2 changes: 1 addition & 1 deletion nightfall-deployer/contracts/Config.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions nightfall-deployer/contracts/Shield.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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');
Expand Down
15 changes: 12 additions & 3 deletions nightfall-deployer/contracts/State.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
} {
Expand Down Expand Up @@ -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');
Expand Down
14 changes: 11 additions & 3 deletions nightfall-deployer/contracts/Utils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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)
} {
Expand All @@ -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 {
Expand Down
11 changes: 8 additions & 3 deletions nightfall-optimist/src/classes/block.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -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;
}
Expand Down
21 changes: 11 additions & 10 deletions wallet/src/nightfall-browser/event-handlers/block-proposed.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down Expand Up @@ -154,37 +154,38 @@ 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
// 2. Save transactions hash of the transactions in this L2 block that contains withdraw transactions for this client
// 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(
// eslint-disable-next-line consistent-return
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,
);
}
}),
Expand Down