Skip to content

Commit

Permalink
fix: add transaction submit eventHandler for nightfall-client
Browse files Browse the repository at this point in the history
LijuJoseJJ committed Mar 28, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent a5c326d commit e2d769e
Showing 6 changed files with 146 additions and 3 deletions.
19 changes: 18 additions & 1 deletion nightfall-client/src/event-handlers/block-proposed.mjs
Original file line number Diff line number Diff line change
@@ -14,8 +14,9 @@ import {
setSiblingInfo,
countCircuitTransactions,
isTransactionHashBelongCircuit,
deleteNonNullifiedCommitments,
} from '../services/commitment-storage.mjs';
import getProposeBlockCalldata from '../services/process-calldata.mjs';
import { getProposeBlockCalldata } from '../services/process-calldata.mjs';
import { zkpPrivateKeys, nullifierKeys } from '../services/keys.mjs';
import {
getTreeByBlockNumberL2,
@@ -24,6 +25,8 @@ import {
saveBlock,
setTransactionHashSiblingInfo,
getNumberOfL2Blocks,
findDuplicateTransactions,
deleteTransactionsByTransactionHashes,
} from '../services/database.mjs';
import { decryptCommitment } from '../services/commitment-sync.mjs';
import { syncState } from '../services/state-sync.mjs';
@@ -79,6 +82,7 @@ async function blockProposedEventHandler(data, syncing) {

const dbUpdates = transactions.map(async transaction => {
let saveTxToDb = false;
let duplicateTransactions = []; // duplicate tx holding same commitments or nullifiers

// filter out non zero commitments and nullifiers
const nonZeroCommitments = transaction.commitments.filter(c => c !== ZERO);
@@ -126,6 +130,12 @@ async function blockProposedEventHandler(data, syncing) {
...transaction,
isDecrypted,
});

duplicateTransactions = await findDuplicateTransactions(
nonZeroCommitments,
nonZeroNullifiers,
[transaction.transactionHash],
);
}

return Promise.all([
@@ -137,6 +147,13 @@ async function blockProposedEventHandler(data, syncing) {
data.blockNumber,
data.transactionHash,
),
deleteTransactionsByTransactionHashes([...duplicateTransactions.map(t => t.transactionHash)]),
deleteNonNullifiedCommitments([
...duplicateTransactions
.map(t => t.commitments)
.flat()
.filter(c => c !== ZERO),
]),
]);
});

3 changes: 3 additions & 0 deletions nightfall-client/src/event-handlers/index.mjs
Original file line number Diff line number Diff line change
@@ -2,15 +2,18 @@ import { startEventQueue } from './subscribe.mjs';
import blockProposedEventHandler from './block-proposed.mjs';
import rollbackEventHandler from './rollback.mjs';
import removeBlockProposedEventHandler from './chain-reorg.mjs';
import transactionSubmittedEventHandler from './transaction-submitted.mjs';

const eventHandlers = {
BlockProposed: blockProposedEventHandler,
TransactionSubmitted: transactionSubmittedEventHandler,
Rollback: rollbackEventHandler,
removers: {
BlockProposed: removeBlockProposedEventHandler,
},
priority: {
BlockProposed: 0,
TransactionSubmitted: 1,
Rollback: 0,
},
};
57 changes: 57 additions & 0 deletions nightfall-client/src/event-handlers/transaction-submitted.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import logger from '@polygon-nightfall/common-files/utils/logger.mjs';
import constants from '@polygon-nightfall/common-files/constants/index.mjs';
import { getTransactionSubmittedCalldata } from '../services/process-calldata.mjs';
import { countCommitments, countNullifiers } from '../services/commitment-storage.mjs';
import { saveTransaction } from '../services/database.mjs';

const { ZERO } = constants;

async function doesAnyOfCommitmentsExistInDB(commitments) {
const count = await countCommitments(commitments);
return Boolean(count);
}

async function doesAnyOfNullifiersExistInDB(nullifiers) {
const count = await countNullifiers(nullifiers);
return Boolean(count);
}

/**
* This handler runs whenever a new transaction is submitted to the blockchain
*/
async function transactionSubmittedEventHandler(eventParams) {
const { offchain = false, ...data } = eventParams;
let saveTxInDb = false;

const transaction = await getTransactionSubmittedCalldata(data);
transaction.blockNumber = data.blockNumber;
transaction.transactionHashL1 = data.transactionHash;

// logic: if any of non zero commitment in transaction alraedy exist in db
// i.e transaction belong to user using this nightfall-client.
// for example: for deposit we store commitment while transaction submit,
// similarly for transfer we store change commitment while transaction submit

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

if (await doesAnyOfCommitmentsExistInDB(nonZeroCommitments)) {
saveTxInDb = true;
} else if (doesAnyOfNullifiersExistInDB(nonZeroNullifiers)) {
saveTxInDb = true;
}

if (saveTxInDb) {
await saveTransaction({ ...transaction });
}

logger.info({
msg: 'Client Transaction Handler - New transaction received.',
transaction,
offchain,
saveTxInDb,
});
}

export default transactionSubmittedEventHandler;
8 changes: 8 additions & 0 deletions nightfall-client/src/services/commitment-storage.mjs
Original file line number Diff line number Diff line change
@@ -1072,3 +1072,11 @@ export async function getCommitmentsDepositedRollbacked(compressedZkpPublicKey)

return db.collection(COMMITMENTS_COLLECTION).find(query).toArray();
}

// function to delete non nullified commitments
export async function deleteNonNullifiedCommitments(commitments) {
const connection = await mongo.connection(MONGO_URL);
const query = { _id: { $in: commitments }, isNullifiedOnChain: -1 };
const db = connection.db(COMMITMENTS_DB);
return db.collection(COMMITMENTS_COLLECTION).deleteMany(query);
}
15 changes: 15 additions & 0 deletions nightfall-client/src/services/database.mjs
Original file line number Diff line number Diff line change
@@ -292,3 +292,18 @@ export async function getTransactionsByTransactionHashesByL2Block(transactionHas
);
return transactions;
}

/**
* Function to find duplicate transactions for an array of commitments or nullifiers
* this function is used in blockProposedEventHandler
*/
export async function findDuplicateTransactions(commitments, nullifiers, transactionHashes = []) {
const connection = await mongo.connection(MONGO_URL);
const db = connection.db(COMMITMENTS_DB);
const query = {
$or: [{ commitments: { $in: commitments } }, { nullifiers: { $in: nullifiers } }],
transactionHash: { $nin: transactionHashes },
blockNumberL2: { $exists: false },
};
return db.collection(TRANSACTIONS_COLLECTION).find(query).toArray();
}
47 changes: 45 additions & 2 deletions nightfall-client/src/services/process-calldata.mjs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ import { unpackBlockInfo } from '@polygon-nightfall/common-files/utils/block-uti

const { SIGNATURES } = config;

async function getProposeBlockCalldata(eventData) {
export async function getProposeBlockCalldata(eventData) {
const web3 = Web3.connection();
const { transactionHash } = eventData;
const tx = await web3.eth.getTransaction(transactionHash);
@@ -76,4 +76,47 @@ async function getProposeBlockCalldata(eventData) {
return { transactions, block };
}

export default getProposeBlockCalldata;
export async function getTransactionSubmittedCalldata(eventData) {
const web3 = Web3.connection();
const { transactionHash } = eventData;
const tx = await web3.eth.getTransaction(transactionHash);
// Remove the '0x' and function signature to recove rhte abi bytecode
const abiBytecode = `0x${tx.input.slice(10)}`;
const transactionData = web3.eth.abi.decodeParameter(SIGNATURES.SUBMIT_TRANSACTION, abiBytecode);
const [
packedTransactionInfo,
historicRootBlockNumberL2Packed,
tokenId,
ercAddress,
recipientAddress,
commitments,
nullifiers,
compressedSecrets,
proof,
] = transactionData;

const { value, fee, circuitHash, tokenType } =
Transaction.unpackTransactionInfo(packedTransactionInfo);

const historicRootBlockNumberL2 = Transaction.unpackHistoricRoot(
nullifiers.length,
historicRootBlockNumberL2Packed,
);

const transaction = {
value,
fee,
circuitHash,
tokenType,
historicRootBlockNumberL2,
tokenId,
ercAddress,
recipientAddress,
commitments,
nullifiers,
compressedSecrets,
proof,
};
transaction.transactionHash = Transaction.calcHash(transaction);
return transaction;
}

0 comments on commit e2d769e

Please sign in to comment.