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

avoid storing redundant tx from blockpropose event #716

Merged
merged 6 commits into from
Jul 26, 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
75 changes: 53 additions & 22 deletions nightfall-client/src/event-handlers/block-proposed.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
markNullifiedOnChain,
markOnChain,
countCommitments,
countNullifiers,
setSiblingInfo,
countWithdrawTransactionHashes,
isTransactionHashWithdraw,
Expand Down Expand Up @@ -35,34 +36,58 @@ async function blockProposedEventHandler(data, syncing) {
const latestTree = await getLatestTree();
const blockCommitments = transactions.map(t => t.commitments.filter(c => c !== ZERO)).flat();

// 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({
const dbUpdates = transactions.map(async transaction => {
let saveTxToDb = false;

// filter out non zero commitments and nullifiers
const nonZeroCommitments = transaction.commitments.flat().filter(n => n !== ZERO);
const nonZeroNullifiers = transaction.nullifiers.flat().filter(n => n !== ZERO);

const countOfNonZeroCommitments = await countCommitments(nonZeroCommitments);
const countOfNonZeroNullifiers = await countNullifiers(nonZeroNullifiers);

if (transaction.transactionType === '1' || transaction.transactionType === '2') {
if (countOfNonZeroCommitments === 0) {
await decryptCommitment(transaction, zkpPrivateKeys, nullifierKeys)
.then(isDecrypted => {
// case when one of user is recipient of transfer transaction
if (isDecrypted) {
saveTxToDb = true;
}
})
.catch(err => {
// case when transfer transaction created by user
if (countOfNonZeroNullifiers >= 1) {
saveTxToDb = true;
} else {
logger.error(err);
}
});
} else {
// case when user has transferred to himself
saveTxToDb = true;
}
} else if (transaction.transactionType === '0' && countOfNonZeroCommitments >= 1) {
// case when deposit transaction created by user
saveTxToDb = true;
} else if (transaction.transactionType === '3' && countOfNonZeroNullifiers >= 1) {
// case when withdraw transaction created by user
saveTxToDb = true;
}

if (saveTxToDb)
await saveTransaction({
transactionHashL1,
blockNumber: data.blockNumber,
blockNumberL2: block.blockNumberL2,
...t,
...transaction,
}).catch(function (err) {
if (!syncing || !err.message.includes('replay existing transaction')) throw err;
logger.warn('Attempted to replay existing transaction. This is expected while syncing');
}),
),
);
// }
});

const dbUpdates = transactions.map(async transaction => {
// filter out non zero commitments and nullifiers
const nonZeroCommitments = transaction.commitments.flat().filter(n => n !== ZERO);
const nonZeroNullifiers = transaction.nullifiers.flat().filter(n => n !== ZERO);
if (
(Number(transaction.transactionType) === 1 || Number(transaction.transactionType) === 2) &&
(await countCommitments(nonZeroCommitments)) === 0
)
await decryptCommitment(transaction, zkpPrivateKeys, nullifierKeys);
return Promise.all([
saveTxToDb,
markOnChain(nonZeroCommitments, block.blockNumberL2, data.blockNumber, data.transactionHash),
markNullifiedOnChain(
nonZeroNullifiers,
Expand All @@ -73,8 +98,14 @@ async function blockProposedEventHandler(data, syncing) {
]);
});

// await Promise.all(toStore);
await Promise.all(dbUpdates);
await Promise.all(dbUpdates).then(async updateReturn => {
// only save block if any transaction in it is saved/stored to db
const saveBlockToDb = updateReturn.map(d => d[0]);
if (saveBlockToDb.includes(true)) {
await saveBlock({ blockNumber: currentBlockCount, transactionHashL1, ...block });
}
});

const updatedTimber = Timber.statelessUpdate(
latestTree,
blockCommitments,
Expand Down
8 changes: 8 additions & 0 deletions nightfall-client/src/services/commitment-storage.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ export async function countCommitments(commitments) {
return db.collection(COMMITMENTS_COLLECTION).countDocuments(query);
}

// function to get count of nullifier. Can also be used to check if it exists
export async function countNullifiers(nullifiers) {
const connection = await mongo.connection(MONGO_URL);
const query = { nullifier: { $in: nullifiers } };
const db = connection.db(COMMITMENTS_DB);
return db.collection(COMMITMENTS_COLLECTION).countDocuments(query);
}

// // function to get count of transaction hashes. Used to decide if we should store
// // incoming blocks or transactions.
// export async function countTransactionHashes(transactionHashes) {
Expand Down
8 changes: 5 additions & 3 deletions nightfall-client/src/services/commitment-sync.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@ export async function decryptCommitment(transaction, zkpPrivateKey, nullifierKey
logger.info("This encrypted message isn't for this recipient");
}
});
await Promise.all(storeCommitments).catch(function (err) {
logger.info(err);
});

if (storeCommitments.length === 0) {
throw Error("This encrypted message isn't for any of recipients");
}
return Promise.all(storeCommitments);
}

/**
Expand Down
27 changes: 13 additions & 14 deletions wallet/src/nightfall-browser/event-handlers/block-proposed.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,10 @@ async function blockProposedEventHandler(data, ivks, nsks) {
console.log(`Received Block Proposed event: ${JSON.stringify(data)}`);
// 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);
const { transactions, block, blockTimestamp } = data;
const latestTree = await getTreeByBlockNumberL2(block.blockNumberL2 - 1);
const blockCommitments = transactions.map(t => t.commitments.filter(c => c !== ZERO)).flat();

let tempBlockSaved = false;
if ((await countTransactionHashes(block.transactionHashes)) > 0) {
await saveBlock({
blockNumber: currentBlockCount,
transactionHashL1,
...block,
});
await Promise.all(transactions.map(t => saveTransaction({ transactionHashL1, ...t })));
tempBlockSaved = true;
}
let isTxDecrypt = false;

const dbUpdates = transactions.map(async transaction => {
// filter out non zero commitments and nullifiers
Expand All @@ -71,6 +60,7 @@ async function blockProposedEventHandler(data, ivks, nsks) {
if (Object.keys(commitment).length === 0)
logger.info("This encrypted message isn't for this recipient");
else {
isTxDecrypt = true;
storeCommitments.push(storeCommitment(commitment, nsks[i]));
tempTransactionStore.push(
saveTransaction({
Expand All @@ -88,14 +78,14 @@ async function blockProposedEventHandler(data, ivks, nsks) {
await Promise.all(storeCommitments).catch(function (err) {
logger.info(err);
}); // control errors when storing commitments in order to ensure next Promise being executed
if (!tempBlockSaved) await Promise.all(tempTransactionStore);

await Promise.all(tempTransactionStore);
// Update timestamps
await updateTransactionTime(
transactions.map(t => t.transactionHash),
blockTimestamp,
);
return [
Promise.all(storeCommitments),
markOnChain(nonZeroCommitments, block.blockNumberL2, data.blockNumber, data.transactionHash),
markNullifiedOnChain(
nonZeroNullifiers,
Expand All @@ -108,6 +98,15 @@ async function blockProposedEventHandler(data, ivks, nsks) {

// await Promise.all(toStore);
await Promise.all(dbUpdates);

if (isTxDecrypt || (await countTransactionHashes(block.transactionHashes)) > 0) {
await saveBlock({
blockNumber: currentBlockCount,
transactionHashL1,
...block,
});
}

const updatedTimber = Timber.statelessUpdate(
latestTree,
blockCommitments,
Expand Down