Skip to content

Commit e2d769e

Browse files
committed
fix: add transaction submit eventHandler for nightfall-client
1 parent a5c326d commit e2d769e

File tree

6 files changed

+146
-3
lines changed

6 files changed

+146
-3
lines changed

nightfall-client/src/event-handlers/block-proposed.mjs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@ import {
1414
setSiblingInfo,
1515
countCircuitTransactions,
1616
isTransactionHashBelongCircuit,
17+
deleteNonNullifiedCommitments,
1718
} from '../services/commitment-storage.mjs';
18-
import getProposeBlockCalldata from '../services/process-calldata.mjs';
19+
import { getProposeBlockCalldata } from '../services/process-calldata.mjs';
1920
import { zkpPrivateKeys, nullifierKeys } from '../services/keys.mjs';
2021
import {
2122
getTreeByBlockNumberL2,
@@ -24,6 +25,8 @@ import {
2425
saveBlock,
2526
setTransactionHashSiblingInfo,
2627
getNumberOfL2Blocks,
28+
findDuplicateTransactions,
29+
deleteTransactionsByTransactionHashes,
2730
} from '../services/database.mjs';
2831
import { decryptCommitment } from '../services/commitment-sync.mjs';
2932
import { syncState } from '../services/state-sync.mjs';
@@ -79,6 +82,7 @@ async function blockProposedEventHandler(data, syncing) {
7982

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

8387
// filter out non zero commitments and nullifiers
8488
const nonZeroCommitments = transaction.commitments.filter(c => c !== ZERO);
@@ -126,6 +130,12 @@ async function blockProposedEventHandler(data, syncing) {
126130
...transaction,
127131
isDecrypted,
128132
});
133+
134+
duplicateTransactions = await findDuplicateTransactions(
135+
nonZeroCommitments,
136+
nonZeroNullifiers,
137+
[transaction.transactionHash],
138+
);
129139
}
130140

131141
return Promise.all([
@@ -137,6 +147,13 @@ async function blockProposedEventHandler(data, syncing) {
137147
data.blockNumber,
138148
data.transactionHash,
139149
),
150+
deleteTransactionsByTransactionHashes([...duplicateTransactions.map(t => t.transactionHash)]),
151+
deleteNonNullifiedCommitments([
152+
...duplicateTransactions
153+
.map(t => t.commitments)
154+
.flat()
155+
.filter(c => c !== ZERO),
156+
]),
140157
]);
141158
});
142159

nightfall-client/src/event-handlers/index.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ import { startEventQueue } from './subscribe.mjs';
22
import blockProposedEventHandler from './block-proposed.mjs';
33
import rollbackEventHandler from './rollback.mjs';
44
import removeBlockProposedEventHandler from './chain-reorg.mjs';
5+
import transactionSubmittedEventHandler from './transaction-submitted.mjs';
56

67
const eventHandlers = {
78
BlockProposed: blockProposedEventHandler,
9+
TransactionSubmitted: transactionSubmittedEventHandler,
810
Rollback: rollbackEventHandler,
911
removers: {
1012
BlockProposed: removeBlockProposedEventHandler,
1113
},
1214
priority: {
1315
BlockProposed: 0,
16+
TransactionSubmitted: 1,
1417
Rollback: 0,
1518
},
1619
};
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import logger from '@polygon-nightfall/common-files/utils/logger.mjs';
2+
import constants from '@polygon-nightfall/common-files/constants/index.mjs';
3+
import { getTransactionSubmittedCalldata } from '../services/process-calldata.mjs';
4+
import { countCommitments, countNullifiers } from '../services/commitment-storage.mjs';
5+
import { saveTransaction } from '../services/database.mjs';
6+
7+
const { ZERO } = constants;
8+
9+
async function doesAnyOfCommitmentsExistInDB(commitments) {
10+
const count = await countCommitments(commitments);
11+
return Boolean(count);
12+
}
13+
14+
async function doesAnyOfNullifiersExistInDB(nullifiers) {
15+
const count = await countNullifiers(nullifiers);
16+
return Boolean(count);
17+
}
18+
19+
/**
20+
* This handler runs whenever a new transaction is submitted to the blockchain
21+
*/
22+
async function transactionSubmittedEventHandler(eventParams) {
23+
const { offchain = false, ...data } = eventParams;
24+
let saveTxInDb = false;
25+
26+
const transaction = await getTransactionSubmittedCalldata(data);
27+
transaction.blockNumber = data.blockNumber;
28+
transaction.transactionHashL1 = data.transactionHash;
29+
30+
// logic: if any of non zero commitment in transaction alraedy exist in db
31+
// i.e transaction belong to user using this nightfall-client.
32+
// for example: for deposit we store commitment while transaction submit,
33+
// similarly for transfer we store change commitment while transaction submit
34+
35+
// filter out non zero commitments and nullifiers
36+
const nonZeroCommitments = transaction.commitments.filter(c => c !== ZERO);
37+
const nonZeroNullifiers = transaction.nullifiers.filter(n => n !== ZERO);
38+
39+
if (await doesAnyOfCommitmentsExistInDB(nonZeroCommitments)) {
40+
saveTxInDb = true;
41+
} else if (doesAnyOfNullifiersExistInDB(nonZeroNullifiers)) {
42+
saveTxInDb = true;
43+
}
44+
45+
if (saveTxInDb) {
46+
await saveTransaction({ ...transaction });
47+
}
48+
49+
logger.info({
50+
msg: 'Client Transaction Handler - New transaction received.',
51+
transaction,
52+
offchain,
53+
saveTxInDb,
54+
});
55+
}
56+
57+
export default transactionSubmittedEventHandler;

nightfall-client/src/services/commitment-storage.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1072,3 +1072,11 @@ export async function getCommitmentsDepositedRollbacked(compressedZkpPublicKey)
10721072

10731073
return db.collection(COMMITMENTS_COLLECTION).find(query).toArray();
10741074
}
1075+
1076+
// function to delete non nullified commitments
1077+
export async function deleteNonNullifiedCommitments(commitments) {
1078+
const connection = await mongo.connection(MONGO_URL);
1079+
const query = { _id: { $in: commitments }, isNullifiedOnChain: -1 };
1080+
const db = connection.db(COMMITMENTS_DB);
1081+
return db.collection(COMMITMENTS_COLLECTION).deleteMany(query);
1082+
}

nightfall-client/src/services/database.mjs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,3 +292,18 @@ export async function getTransactionsByTransactionHashesByL2Block(transactionHas
292292
);
293293
return transactions;
294294
}
295+
296+
/**
297+
* Function to find duplicate transactions for an array of commitments or nullifiers
298+
* this function is used in blockProposedEventHandler
299+
*/
300+
export async function findDuplicateTransactions(commitments, nullifiers, transactionHashes = []) {
301+
const connection = await mongo.connection(MONGO_URL);
302+
const db = connection.db(COMMITMENTS_DB);
303+
const query = {
304+
$or: [{ commitments: { $in: commitments } }, { nullifiers: { $in: nullifiers } }],
305+
transactionHash: { $nin: transactionHashes },
306+
blockNumberL2: { $exists: false },
307+
};
308+
return db.collection(TRANSACTIONS_COLLECTION).find(query).toArray();
309+
}

nightfall-client/src/services/process-calldata.mjs

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { unpackBlockInfo } from '@polygon-nightfall/common-files/utils/block-uti
1010

1111
const { SIGNATURES } = config;
1212

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

79-
export default getProposeBlockCalldata;
79+
export async function getTransactionSubmittedCalldata(eventData) {
80+
const web3 = Web3.connection();
81+
const { transactionHash } = eventData;
82+
const tx = await web3.eth.getTransaction(transactionHash);
83+
// Remove the '0x' and function signature to recove rhte abi bytecode
84+
const abiBytecode = `0x${tx.input.slice(10)}`;
85+
const transactionData = web3.eth.abi.decodeParameter(SIGNATURES.SUBMIT_TRANSACTION, abiBytecode);
86+
const [
87+
packedTransactionInfo,
88+
historicRootBlockNumberL2Packed,
89+
tokenId,
90+
ercAddress,
91+
recipientAddress,
92+
commitments,
93+
nullifiers,
94+
compressedSecrets,
95+
proof,
96+
] = transactionData;
97+
98+
const { value, fee, circuitHash, tokenType } =
99+
Transaction.unpackTransactionInfo(packedTransactionInfo);
100+
101+
const historicRootBlockNumberL2 = Transaction.unpackHistoricRoot(
102+
nullifiers.length,
103+
historicRootBlockNumberL2Packed,
104+
);
105+
106+
const transaction = {
107+
value,
108+
fee,
109+
circuitHash,
110+
tokenType,
111+
historicRootBlockNumberL2,
112+
tokenId,
113+
ercAddress,
114+
recipientAddress,
115+
commitments,
116+
nullifiers,
117+
compressedSecrets,
118+
proof,
119+
};
120+
transaction.transactionHash = Transaction.calcHash(transaction);
121+
return transaction;
122+
}

0 commit comments

Comments
 (0)