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

Wait for twelve L1 confirmations before updating L2 data. #426

Merged
merged 86 commits into from
Feb 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
86 commits
Select commit Hold shift + click to select a range
68d6f19
feat: basic dir structure
Westlad Nov 22, 2021
417832a
fix: node modules update
Westlad Nov 22, 2021
4be8d97
fix: node modules update
Westlad Nov 22, 2021
4cfc627
fix: docker-compose script
Westlad Nov 22, 2021
f6d8427
feat: intermediate commit
Westlad Nov 23, 2021
f59ccfd
feat: intermediate commit
Westlad Nov 23, 2021
4797bb5
feat: first working
Westlad Nov 24, 2021
02fc61f
feat: first working
Westlad Nov 24, 2021
13f3f96
feat: proposer container
Westlad Nov 25, 2021
1bbe19f
feat: containers working locally
Westlad Nov 25, 2021
ad2bc5e
fix: config
Westlad Nov 25, 2021
99d6f5b
feat: build app images
Westlad Nov 26, 2021
c9fe99b
feat: build app images
Westlad Nov 26, 2021
bb13aa0
feat: build app images
Westlad Nov 26, 2021
80e489e
docs: readme added
Westlad Nov 26, 2021
9dc599a
fix: remove container build in branch
Westlad Nov 26, 2021
8967957
fix: externalise test keys
Westlad Nov 26, 2021
e64a626
fix: externalise test keys
Westlad Nov 26, 2021
24ad9e8
fix: file refactor
Westlad Nov 26, 2021
e506a95
fix: file refactor
Westlad Nov 26, 2021
f3d0130
fix: file refactor
Westlad Nov 26, 2021
11452eb
fix: npm run
Westlad Nov 26, 2021
b5fcf93
fix: npm run
Westlad Nov 26, 2021
373a46b
fix: npm run
Westlad Nov 26, 2021
062115a
fix: use erc mocks
Westlad Nov 27, 2021
23ccad5
feat: added util to compute eth keys
Westlad Nov 29, 2021
3aec6f7
fix: change address space so no overlap with nf
Westlad Nov 29, 2021
53370d5
fix: add extra hosts to support linux machine
Westlad Nov 29, 2021
09563a7
fix: minor ropsten updates
Westlad Nov 30, 2021
a8089d0
feat: modifications to get ropsten working
Westlad Nov 30, 2021
25e6dec
chore: github actions bug
Westlad Nov 30, 2021
331e993
chore: github actions bug
Westlad Nov 30, 2021
78c4f22
fix: ropsten
Westlad Nov 30, 2021
fa50e19
feat: working on ropsten
Westlad Dec 3, 2021
2be8686
fix: close rabbitmq external ports
Westlad Dec 3, 2021
5f0b8ab
fix: remove rabbitmq ports
Westlad Dec 6, 2021
eeef360
fix: rebase to master and fix wallet incompatibilities
Westlad Dec 6, 2021
46c0ee5
fix: rebase to master and fix wallet incompatibilities
Westlad Dec 6, 2021
e6800ee
fix: gas test
Westlad Dec 6, 2021
8fc4b30
fix: addresses need hex notation
Westlad Dec 7, 2021
462a50c
fix: new nf3 signature
Westlad Dec 7, 2021
103230f
fix: longer test wait for slower ropsten network
Westlad Dec 7, 2021
c9cda1b
fix: add addresses to local test
Westlad Dec 7, 2021
6e622e3
fix: increase gasprice calculated for nf3
Westlad Dec 7, 2021
e070b12
fix: node modules update
Westlad Nov 22, 2021
9934061
fix: use erc mocks
Westlad Nov 27, 2021
d9467b3
feat(user1user2 apps): wip
ChaitanyaKonda Nov 29, 2021
fc97ebd
feat(user1user2 apps): wip
ChaitanyaKonda Nov 29, 2021
19c255c
fix: wip
ChaitanyaKonda Dec 3, 2021
09d2155
feat(ping pong test): working test
ChaitanyaKonda Dec 7, 2021
580f518
fix: wip
ChaitanyaKonda Dec 7, 2021
0471e92
fix: test gas
Westlad Dec 7, 2021
7b17158
fix: reduce ropsten gas a block gas limit is lower
Westlad Dec 7, 2021
d60fc9f
fix: readme
ChaitanyaKonda Dec 8, 2021
00bc7eb
Adding Chait's test scripts
Westlad Dec 8, 2021
8cca7ce
feat: working on ropsten
Westlad Dec 10, 2021
2ee5669
feat: working on ropsten
Westlad Dec 10, 2021
9818a0a
fix: client chain reorg does not reset block data
Westlad Dec 17, 2021
78bb73f
fix: rename containers, images and services
Westlad Dec 17, 2021
71f2dbf
fix: increase transaction block timeout to 200
Westlad Dec 18, 2021
bdff33b
fix: increase logging
Westlad Dec 20, 2021
b9d0033
fix: logging
Westlad Dec 20, 2021
6e6b6de
feat: trap no suitable commitments error
Westlad Dec 21, 2021
15b24f4
fix: increase balance wait time
Westlad Dec 21, 2021
ca29e71
fix: wait for 12 confirmations
Westlad Jan 4, 2022
30db801
fix: return receipt after waiting
Westlad Jan 4, 2022
cfbedbc
fix: chain reorg now copes with null blocks
Westlad Jan 5, 2022
781e89c
fix: events now wait for 12 confirmations
Westlad Jan 11, 2022
4b4ad95
fix: public input hash incorrect
Westlad Jan 12, 2022
d8c1010
fix: add second user back
Westlad Jan 12, 2022
0bb1275
fix: rejection of removed events improved
Westlad Jan 13, 2022
e0b03b7
chore: increase timeout blocks for ropsten
Westlad Jan 13, 2022
574d6aa
fix: event removal is the only way to reject an event
Westlad Jan 13, 2022
74ef24e
fix: typo
Westlad Jan 13, 2022
22088c2
feat: improved logging
Westlad Jan 13, 2022
41192cc
fix: remember multiple event removals
Westlad Jan 14, 2022
5e7f385
chore: improved logging
Westlad Jan 14, 2022
ed2e406
fix: ping-ping merge conflicts
Westlad Jan 19, 2022
e178d7d
fix: basic tests passing
Westlad Jan 21, 2022
1c248f4
fix: basic tests passing
Westlad Jan 21, 2022
6f12652
fix: merge conflicts
Westlad Jan 21, 2022
e278868
fix: merge
Westlad Jan 21, 2022
9a68643
fix: merge conflicts
Westlad Jan 25, 2022
c3d2d3b
fix: updated RETRIES to delpoy contracts in client, and increase wall…
druiz0992 Jan 26, 2022
8f39528
Merge pull request #429 from EYBlockchain/david/ping-pong-test-timings
Westlad Jan 27, 2022
c933b08
fix: termination of gnaache test
Westlad Jan 27, 2022
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
2 changes: 1 addition & 1 deletion .github/workflows/publish-docker-app-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ on:
push:
branches:
- master
- westlad/ping-pong
- westlad/ping-pong-test

jobs:
release:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish-docker-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ on:
push:
branches:
- master
- westlad/ping-pong
- westlad/ping-pong-test

jobs:
release:
Expand Down
36 changes: 31 additions & 5 deletions cli/lib/nf3.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class Nf3 {

currentEnvironment;

notConfirmed = 0;

constructor(web3WsUrl, ethereumSigningKey, environment = ENVIRONMENTS.localhost, zkpKeys) {
this.clientBaseUrl = environment.clientApiUrl;
this.optimistBaseUrl = environment.optimistApiUrl;
Expand Down Expand Up @@ -183,8 +185,27 @@ class Nf3 {

if (this.ethereumSigningKey) {
const signed = await this.web3.eth.accounts.signTransaction(tx, this.ethereumSigningKey);
return this.web3.eth.sendSignedTransaction(signed.rawTransaction);
// rather than waiting until we have a receipt, wait until we have enough confirmation blocks
// then return the receipt.
// TODO does this still work if there is a chain reorg or do we have to handle that?
return new Promise(resolve => {
console.log(`Confirming transaction ${signed.transactionHash}`);
this.notConfirmed++;
this.web3.eth
.sendSignedTransaction(signed.rawTransaction)
.on('confirmation', (number, receipt) => {
if (number === 12) {
this.notConfirmed--;
console.log(
`Transaction ${receipt.transactionHash} has been confirmed ${number} times.`,
`Number of unconfirmed transactions is ${this.notConfirmed}`,
);
resolve(receipt);
}
});
});
}
// TODO add wait for confirmations to the wallet functionality
return this.web3.eth.sendTransaction(tx);
}

Expand Down Expand Up @@ -317,6 +338,9 @@ class Nf3 {
ask: this.zkpKeys.ask,
fee,
});
if (res.data.error && res.data.error === 'No suitable commitments') {
throw new Error('No suitable commitments');
}
if (!offchain) {
return this.submitTransaction(res.data.txDataToSign, this.shieldContractAddress, fee);
}
Expand Down Expand Up @@ -708,7 +732,7 @@ class Nf3 {
@method
@async
@param {Array} ercList - list of erc contract addresses to filter.
@param {Boolean} filterByCompressedPkd - flag to indicate if request is filtered
@param {Boolean} filterByCompressedPkd - flag to indicate if request is filtered
ones compressed pkd
@returns {Promise} This promise resolves into an object whose properties are the
addresses of the ERC contracts of the tokens held by this account in Layer 2. The
Expand Down Expand Up @@ -748,7 +772,7 @@ class Nf3 {
@method
@async
@param {Array} ercList - list of erc contract addresses to filter.
@param {Boolean} filterByCompressedPkd - flag to indicate if request is filtered
@param {Boolean} filterByCompressedPkd - flag to indicate if request is filtered
ones compressed pkd
@returns {Promise} This promise resolves into an object whose properties are the
addresses of the ERC contracts of the tokens held by this account in Layer 2. The
Expand All @@ -769,7 +793,7 @@ class Nf3 {
@method
@async
@param {Array} ercList - list of erc contract addresses to filter.
@param {Boolean} filterByCompressedPkd - flag to indicate if request is filtered
@param {Boolean} filterByCompressedPkd - flag to indicate if request is filtered
ones compressed pkd
@returns {Promise} This promise resolves into an object whose properties are the
addresses of the ERC contracts of the tokens held by this account in Layer 2. The
Expand Down Expand Up @@ -816,7 +840,9 @@ class Nf3 {
Set a Web3 Provider URL
*/
async setWeb3Provider() {
this.web3 = new Web3(this.web3WsUrl, { transactionBlockTimeout: 200 }); // set a longer timeout
this.web3 = new Web3(this.web3WsUrl);
this.web3.eth.transactionBlockTimeout = 200;
this.web3.eth.transactionConfirmationBlocks = 12;
if (typeof window !== 'undefined') {
if (window.ethereum && this.ethereumSigningKey === '') {
this.web3 = new Web3(window.ethereum);
Expand Down
1 change: 1 addition & 0 deletions common-files/classes/public-inputs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ class PublicInputs {
hash;

constructor(publicInputs) {
// some inputs may be general numbers and some strings. We convert all to string, process and generalise.
this.publicInputs = generalise(publicInputs.flat(Infinity));
[, this.hash] = generalise(sha256(this.publicInputs).limbs(248, 2));
}
Expand Down
5 changes: 0 additions & 5 deletions common-files/package-lock.json

This file was deleted.

2 changes: 1 addition & 1 deletion common-files/utils/contract.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import config from 'config';
import Web3 from './web3.mjs';
import logger from './logger.mjs';

const web3 = Web3.connection();
export const web3 = Web3.connection();

const options = config.WEB3_OPTIONS;

Expand Down
105 changes: 65 additions & 40 deletions common-files/utils/event-queue.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,30 +22,15 @@ and catch these removals, processing them appropriately.
import Queue from 'queue';
import config from 'config';
import logger from 'common-files/utils/logger.mjs';
import { web3 } from 'common-files/utils/contract.mjs';

const { MAX_QUEUE } = config;
const { MAX_QUEUE, CONFIRMATION_POLL_TIME, CONFIRMATIONS } = config;
const fastQueue = new Queue({ autostart: true, concurrency: 1 });
const slowQueue = new Queue({ autostart: true, concurrency: 1 });
const removed = {}; // singleton holding transaction hashes of any removed events
const stopQueue = new Queue({ autostart: false, concurrency: 1 });
export const queues = [fastQueue, slowQueue, stopQueue];

/**
This function will return a promise that resolves to true when the next highest
priority queue is empty (priority goes in reverse order, prioity 0 is highest
priority)
*/
function nextHigherPriorityQueueHasEmptied(priority) {
return new Promise(resolve => {
const listener = () => resolve();
if (priority === 0) resolve(); // resolve if we're the highest priority queue
queues[priority - 1].once('end', listener); // or when the higher priority queue empties
if (queues[priority - 1].length === 0) {
queues[priority - 1].removeListener('end', listener);
resolve(); // or if it's already empty
}
});
}

/**
This function will wait until all the functions currently in a queue have been
processed. It's useful if you want to ensure that Nightfall has had an opportunity
Expand All @@ -63,17 +48,64 @@ function flushQueue(priority) {

async function enqueueEvent(callback, priority, args) {
queues[priority].push(async () => {
// await nextHigherPriorityQueueHasEmptied(priority);
// prevent conditionalmakeblock from running until fastQueue is emptied
return callback(args);
});
}

/**
This function will return when the event has been on chain for <confirmations> blocks
It's useful to call this if you want to be sure that your event has been confirmed
multiple times before you go ahead and process it.
*/

function waitForConfirmation(eventObject) {
logger.debug(`Confirming event ${eventObject.event}`);
const { transactionHash, blockNumber } = eventObject;
return new Promise((resolve, reject) => {
let confirmedBlocks = 0;
const id = setInterval(async () => {
// get the transaction that caused the event
// if it's been in a chain reorg then it will have been removed.
if (removed[transactionHash] > 0) {
clearInterval(id);
removed[eventObject.transactionHash]--;
reject(
new Error(
`Event removed; probable chain reorg. Event was ${eventObject.event}, transaction hash was ${transactionHash}`,
),
);
}
const currentBlock = await web3.eth.getBlock('latest');
if (currentBlock.number - blockNumber > confirmedBlocks) {
confirmedBlocks = currentBlock.number - blockNumber;
}
if (confirmedBlocks >= CONFIRMATIONS) {
clearInterval(id);
logger.debug(
`Event ${eventObject.event} has been confirmed ${
currentBlock.number - blockNumber
} times`,
);
resolve(eventObject);
}
}, CONFIRMATION_POLL_TIME);
});
}

async function dequeueEvent(priority) {
queues[priority].shift();
}

async function queueManager(eventObject, eventArgs) {
if (eventObject.removed) {
// in this model we don't queue removals but we can use them to reject the event
// Note the event object and its removal have the same transactionHash.
// Also note that we can get more than one removal because the event could be re-mined
// and removed again - so we need to keep count of the removals.
if (!removed[eventObject.transactionHash]) removed[eventObject.transactionHash] = 0;
removed[eventObject.transactionHash]++; // store the removal; waitForConfirmation will read this and reject.
return;
}
// First element of eventArgs must be the eventHandlers object
const [eventHandlers, ...args] = eventArgs;
// handlers contains the functions needed to handle particular types of event,
Expand All @@ -84,30 +116,23 @@ async function queueManager(eventObject, eventArgs) {
}
// pull up the priority for the event being handled (removers have identical priority)
const priority = eventHandlers.priority[eventObject.event];
// if the event was removed then we have a chain reorg and need to reset our
// layer 2 state accordingly.
if (eventObject.removed) {
if (!eventHandlers.removers[eventObject.event]) {
logger.debug(`Unknown event removal ${eventObject.event} ignored`);
return;
}
logger.info(`Queueing event removal ${eventObject.event}`);
queues[priority].push(async () => {
await nextHigherPriorityQueueHasEmptied(priority); // prevent eventHandlers running until the higher priority queue has emptied
return eventHandlers.removers[eventObject.event](eventObject, args);
});
// otherwise queue the event for processing.
} else {
logger.info(`Queueing event ${eventObject.event}`);
queues[priority].push(async () => {
// await nextHigherPriorityQueueHasEmptied(priority); // prevent eventHandlers running until the higher priority queue has emptied
logger.info(
`Queueing event ${eventObject.event}, with transaction hash ${eventObject.transactionHash} and priority ${priority}`,
);
queues[priority].push(async () => {
// we won't even think about processing an event until it's been confirmed many times
try {
await waitForConfirmation(eventObject);
return eventHandlers[eventObject.event](eventObject, args);
});
}
} catch (err) {
return logger.warn(err.message);
}
});
// }
// the queue shouldn't get too long if we're keeping up with the blockchain.
if (queues[priority].length > MAX_QUEUE)
logger.warn(`The event queue has more than ${MAX_QUEUE} events`);
}

/* ignore unused exports */
export { flushQueue, enqueueEvent, dequeueEvent, queueManager };
export { flushQueue, enqueueEvent, queueManager, dequeueEvent, waitForConfirmation };
6 changes: 6 additions & 0 deletions compress-keys.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// utility to compress a pkd
import { generalise } from 'general-number';
import { compressPublicKey } from './nightfall-client/src/services/keys.mjs';

const inputs = generalise(process.argv.slice(2, 4));
console.log(`Compressed value = ${compressPublicKey(inputs).hex()}`);
3 changes: 2 additions & 1 deletion config/default.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ module.exports = {
LOG_LEVEL: process.env.LOG_LEVEL || 'debug',
MONGO_URL: process.env.MONGO_URL || 'mongodb://localhost:27017/',
ZKP_KEY_LENGTH: 32, // use a 32 byte key length for SHA compatibility
CONFIRMATION_POLL_TIME: 1000, // time to wait before querying the blockchain (ms). Must be << block interval
CONFIRMATIONS: 12, // number of confirmations to wait before accepting a transaction
PROTOCOL: 'http://', // connect to zokrates microservice like this
WEBSOCKET_PORT: process.env.WEBSOCKET_PORT || 8080,
ZOKRATES_WORKER_HOST: process.env.ZOKRATES_WORKER_HOST || 'worker',
Expand All @@ -31,7 +33,6 @@ module.exports = {
USE_INFURA: process.env.USE_INFURA === 'true',
ETH_PRIVATE_KEY: process.env.ETH_PRIVATE_KEY, // owner's/deployer's private key
ETH_ADDRESS: process.env.ETH_ADDRESS,
USE_ROPSTEN_NODE: process.env.USE_ROPSTEN_NODE === 'true',
OPTIMIST_HOST: process.env.OPTIMIST_HOST || 'optimist',
OPTIMIST_PORT: process.env.OPTIMIST_PORT || 80,
clientBaseUrl: `http://${process.env.CLIENT_HOST}:${process.env.CLIENT_PORT}`,
Expand Down
2 changes: 1 addition & 1 deletion docker-compose.ganache.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ services:
ports:
- 8546:8546
command:
ganache-cli --accounts=10 --defaultBalanceEther=1000 --gasLimit=0x3B9ACA00 --deterministic -i 4378921 -p 8546
ganache-cli --accounts=10 --defaultBalanceEther=1000 --gasLimit=0x3B9ACA00 --deterministic -i 4378921 -p 8546 -b 1
--account="0x4775af73d6dc84a0ae76f8726bda4b9ecf187c377229cb39e1afa7a18236a69e,10000000000000000000000"
--account="0x4775af73d6dc84a0ae76f8726bda4b9ecf187c377229cb39e1afa7a18236a69d,10000000000000000000000"
--account="0xd42905d0582c476c4b74757be6576ec323d715a0c7dcff231b6348b7ab0190eb,10000000000000000000000"
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ services:
OPTIMIST_HOST: optimist1
OPTIMIST_PORT: 80
USE_STUBS: 'false' # make sure this flag is the same as in deployer service
RETRIES: 80
command: ['npm', 'run', 'dev']

client2:
Expand Down
31 changes: 18 additions & 13 deletions nightfall-client/src/event-handlers/block-proposed.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
storeCommitment,
countCommitments,
setSiblingInfo,
countTransactionHashes,
} from '../services/commitment-storage.mjs';
import getProposeBlockCalldata from '../services/process-calldata.mjs';
import Secrets from '../classes/secrets.mjs';
Expand All @@ -20,17 +19,20 @@ const { ZERO } = config;
This handler runs whenever a BlockProposed event is emitted by the blockchain
*/
async function blockProposedEventHandler(data) {
logger.info(`Received Block Proposed event`);
// ivk will be used to decrypt secrets whilst nsk will be used to calculate nullifiers for commitments and store them
const { blockNumber: currentBlockCount, transactionHash: transactionHashL1 } = data;
const { transactions, block } = await getProposeBlockCalldata(data);
logger.info(
`Received Block Proposed event with layer 2 block number ${block.blockNumberL2} and tx hash ${transactionHashL1}`,
);
const latestTree = await getLatestTree();
const blockCommitments = transactions.map(t => t.commitments.filter(c => c !== ZERO)).flat();

if ((await countTransactionHashes(block.transactionHashes)) > 0) {
await saveBlock({ blockNumber: currentBlockCount, transactionHashL1, ...block });
await Promise.all(transactions.map(t => saveTransaction({ transactionHashL1, ...t })));
}
// if ((await countCommitments(blockCommitments)) > 0) {
await saveBlock({ blockNumber: currentBlockCount, transactionHashL1, ...block });
logger.debug(`Saved L2 block ${block.blockNumberL2}, with tx hash ${transactionHashL1}`);
await Promise.all(transactions.map(t => saveTransaction({ transactionHashL1, ...t })));
// }

const dbUpdates = transactions.map(async transaction => {
// filter out non zero commitments and nullifiers
Expand All @@ -41,6 +43,7 @@ async function blockProposedEventHandler(data) {
(transaction.transactionType === '1' || transaction.transactionType === '2') &&
(await countCommitments(nonZeroCommitments)) === 0
) {
let keysTried = 1;
ivks.forEach((key, i) => {
// decompress the secrets first and then we will decryp t the secrets from this
const decompressedSecrets = Secrets.decompressSecrets(transaction.compressedSecrets);
Expand All @@ -51,20 +54,22 @@ async function blockProposedEventHandler(data) {
nonZeroCommitments[0],
);
if (Object.keys(commitment).length === 0)
logger.info("This encrypted message isn't for this recipient");
logger.info(
`This encrypted message isn't for this recipient, keys tried = ${keysTried++}`,
);
else {
// console.log('PUSHED', commitment, 'nsks', nsks[i]);
storeCommitments.push(storeCommitment(commitment, nsks[i]));
}
} catch (err) {
logger.info(err);
logger.info("This encrypted message isn't for this recipient");
logger.info(
`*This encrypted message isn't for this recipient, keys tried = ${keysTried++}`,
);
}
});
}
await Promise.all(storeCommitments).catch(function (err) {
logger.info(err);
}); // control errors when storing commitments in order to ensure next Promise being executed
await Promise.all(storeCommitments);
return Promise.all([
markOnChain(nonZeroCommitments, block.blockNumberL2, data.blockNumber, data.transactionHash),
markNullifiedOnChain(
Expand All @@ -79,8 +84,8 @@ async function blockProposedEventHandler(data) {
// await Promise.all(toStore);
await Promise.all(dbUpdates);
const updatedTimber = Timber.statelessUpdate(latestTree, blockCommitments);
await saveTree(data.blockNumber, block.blockNumberL2, updatedTimber);

await saveTree(transactionHashL1, block.blockNumberL2, updatedTimber);
logger.debug(`Saved tree for L2 block ${block.blockNumberL2}`);
await Promise.all(
// eslint-disable-next-line consistent-return
blockCommitments.map(async (c, i) => {
Expand Down
Loading