Skip to content

Commit

Permalink
fix: add transaction submit eventHandler for nightfall-client
Browse files Browse the repository at this point in the history
  • Loading branch information
LijuJoseJJ committed Feb 2, 2023
1 parent 7f12f8f commit d1ac399
Show file tree
Hide file tree
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
Expand Up @@ -14,15 +14,18 @@ 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 {
getLatestTree,
saveTree,
saveTransaction,
saveBlock,
setTransactionHashSiblingInfo,
findDuplicateTransactions,
deleteTransactionsByTransactionHashes,
} from '../services/database.mjs';
import { decryptCommitment } from '../services/commitment-sync.mjs';

Expand Down Expand Up @@ -55,6 +58,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);
Expand Down Expand Up @@ -102,6 +106,12 @@ async function blockProposedEventHandler(data, syncing) {
...transaction,
isDecrypted,
});

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

return Promise.all([
Expand All @@ -113,6 +123,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),
]),
]);
});

Expand Down
3 changes: 3 additions & 0 deletions nightfall-client/src/event-handlers/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
};
Expand Down
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
Expand Up @@ -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
Expand Up @@ -271,3 +271,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
Expand Up @@ -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);
Expand Down Expand Up @@ -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 d1ac399

Please sign in to comment.