From 6d680e458c5bdd6680f0f05153fe66e202014aa6 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:51:43 +0000 Subject: [PATCH 1/6] chore(prover): prover node should not listen to attestations --- .../prover-node/src/prover-coordination/factory.ts | 7 +------ yarn-project/prover-node/src/prover-node.test.ts | 7 ++++++- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/yarn-project/prover-node/src/prover-coordination/factory.ts b/yarn-project/prover-node/src/prover-coordination/factory.ts index 48194d44c47f..a2d4ac39b445 100644 --- a/yarn-project/prover-node/src/prover-coordination/factory.ts +++ b/yarn-project/prover-node/src/prover-coordination/factory.ts @@ -1,11 +1,6 @@ import { type ArchiveSource, type Archiver } from '@aztec/archiver'; import { BBCircuitVerifier, TestCircuitVerifier } from '@aztec/bb-prover'; -import { - P2PClientType, - type ProverCoordination, - type WorldStateSynchronizer, - createAztecNodeClient, -} from '@aztec/circuit-types'; +import { P2PClientType, type ProverCoordination, type WorldStateSynchronizer, createAztecNodeClient } from '@aztec/circuit-types'; import { createLogger } from '@aztec/foundation/log'; import { type DataStoreConfig } from '@aztec/kv-store/config'; import { createP2PClient } from '@aztec/p2p'; diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index 931396a26764..1394ccb93dd2 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -18,7 +18,12 @@ import { Signature } from '@aztec/foundation/eth-signature'; import { makeBackoff, retry } from '@aztec/foundation/retry'; import { sleep } from '@aztec/foundation/sleep'; import { openTmpStore } from '@aztec/kv-store/lmdb'; -import { type BootstrapNode, InMemoryTxPool, MemoryEpochProofQuotePool, P2PClient } from '@aztec/p2p'; +import { + type BootstrapNode, + InMemoryTxPool, + MemoryEpochProofQuotePool, + P2PClient, +} from '@aztec/p2p'; import { createBootstrapNode, createTestLibP2PService } from '@aztec/p2p/mocks'; import { type L1Publisher } from '@aztec/sequencer-client'; import { type PublicProcessorFactory } from '@aztec/simulator'; From 864b9955e67a95ee205a4af30366a04fe6c8d153 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Fri, 13 Dec 2024 08:02:58 +0000 Subject: [PATCH 2/6] fmt --- .../prover-node/src/prover-coordination/factory.ts | 7 ++++++- yarn-project/prover-node/src/prover-node.test.ts | 7 +------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/yarn-project/prover-node/src/prover-coordination/factory.ts b/yarn-project/prover-node/src/prover-coordination/factory.ts index a2d4ac39b445..48194d44c47f 100644 --- a/yarn-project/prover-node/src/prover-coordination/factory.ts +++ b/yarn-project/prover-node/src/prover-coordination/factory.ts @@ -1,6 +1,11 @@ import { type ArchiveSource, type Archiver } from '@aztec/archiver'; import { BBCircuitVerifier, TestCircuitVerifier } from '@aztec/bb-prover'; -import { P2PClientType, type ProverCoordination, type WorldStateSynchronizer, createAztecNodeClient } from '@aztec/circuit-types'; +import { + P2PClientType, + type ProverCoordination, + type WorldStateSynchronizer, + createAztecNodeClient, +} from '@aztec/circuit-types'; import { createLogger } from '@aztec/foundation/log'; import { type DataStoreConfig } from '@aztec/kv-store/config'; import { createP2PClient } from '@aztec/p2p'; diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index 1394ccb93dd2..931396a26764 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -18,12 +18,7 @@ import { Signature } from '@aztec/foundation/eth-signature'; import { makeBackoff, retry } from '@aztec/foundation/retry'; import { sleep } from '@aztec/foundation/sleep'; import { openTmpStore } from '@aztec/kv-store/lmdb'; -import { - type BootstrapNode, - InMemoryTxPool, - MemoryEpochProofQuotePool, - P2PClient, -} from '@aztec/p2p'; +import { type BootstrapNode, InMemoryTxPool, MemoryEpochProofQuotePool, P2PClient } from '@aztec/p2p'; import { createBootstrapNode, createTestLibP2PService } from '@aztec/p2p/mocks'; import { type L1Publisher } from '@aztec/sequencer-client'; import { type PublicProcessorFactory } from '@aztec/simulator'; From 0b8402bd8ac9876d108a6b7fc50c31fe0db1df04 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:25:12 +0000 Subject: [PATCH 3/6] feat: topic validator for txs --- .../p2p/src/services/libp2p/libp2p_service.ts | 188 +++++++++++++----- 1 file changed, 140 insertions(+), 48 deletions(-) diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index e4a6449a539f..a553fb4a4aad 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -29,7 +29,7 @@ import { createPeerScoreParams, createTopicScoreParams } from '@chainsafe/libp2p import { noise } from '@chainsafe/libp2p-noise'; import { yamux } from '@chainsafe/libp2p-yamux'; import { identify } from '@libp2p/identify'; -import type { PeerId } from '@libp2p/interface'; +import { Message, PeerId, TopicValidatorResult } from '@libp2p/interface'; import '@libp2p/kad-dht'; import { mplex } from '@libp2p/mplex'; import { tcp } from '@libp2p/tcp'; @@ -61,6 +61,24 @@ import { } from '../reqresp/interface.js'; import { ReqResp } from '../reqresp/reqresp.js'; import type { P2PService, PeerDiscoveryService } from '../service.js'; +import { Timer } from '@aztec/foundation/timer'; + +interface MessageValidator { + validator: { + validateTx(tx: Tx): Promise; + }; + severity: PeerErrorSeverity; +} + +interface ValidationResult { + name: string; + isValid: boolean; + severity: PeerErrorSeverity; +} + +type ValidationOutcome = + | { allPassed: true } + | { allPassed: false; failure: ValidationResult }; /** * Lib P2P implementation of the P2PService interface. @@ -137,6 +155,9 @@ export class LibP2PService extends WithTracer implement this.subscribeToTopic(TopicTypeMap[topic].p2pTopic); } + // Add p2p topic validators + this.node.services.pubsub.topicValidators.set(Tx.p2pTopic, this.validatePropagatedTxFromMessage.bind(this)); + // add GossipSub listener this.node.services.pubsub.addEventListener('gossipsub:message', async e => { const { msg, propagationSource: peerId } = e.detail; @@ -256,6 +277,7 @@ export class LibP2PService extends WithTracer implement dataTransform: new SnappyTransform(), metricsRegister: otelMetricsAdapter, metricsTopicStrToLabel: metricsTopicStrToLabels(), + asyncValidation: true, scoreParams: createPeerScoreParams({ topics: { [Tx.p2pTopic]: createTopicScoreParams({ @@ -284,6 +306,7 @@ export class LibP2PService extends WithTracer implement }, }); + // Create request response protocol handlers /** * Handler for tx requests @@ -479,7 +502,9 @@ export class LibP2PService extends WithTracer implement const txHashString = txHash.toString(); this.logger.verbose(`Received tx ${txHashString} from external peer.`); + const timer = new Timer(); const isValidTx = await this.validatePropagatedTx(tx, peerId); + this.logger.info(`\n\n\n validatePropagatedTx took ${timer.ms()}ms \n\n\n`); if (isValidTx) { await this.mempools.txPool.addTxs([tx]); @@ -523,70 +548,137 @@ export class LibP2PService extends WithTracer implement return true; } + private async validatePropagatedTxFromMessage(propagationSource: PeerId, msg: Message): Promise { + const tx = Tx.fromBuffer(Buffer.from(msg.data)); + const isValid = await this.validatePropagatedTx(tx, propagationSource); + return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject; + } + @trackSpan('Libp2pService.validatePropagatedTx', tx => ({ [Attributes.TX_HASH]: tx.getTxHash().toString(), })) private async validatePropagatedTx(tx: Tx, peerId: PeerId): Promise { const blockNumber = (await this.l2BlockSource.getBlockNumber()) + 1; - // basic data validation - const dataValidator = new DataTxValidator(); - const validData = await dataValidator.validateTx(tx); - if (!validData) { - // penalize - this.node.services.pubsub.score.markInvalidMessageDelivery(peerId.toString(), Tx.p2pTopic); - return false; + const messageValidators = this.createMessageValidators(blockNumber); + const outcome = await this.runValidations(tx, messageValidators); + + if (outcome.allPassed) { + return true; } - // metadata validation - const metadataValidator = new MetadataTxValidator(new Fr(this.config.l1ChainId), new Fr(blockNumber)); - const validMetadata = await metadataValidator.validateTx(tx); - if (!validMetadata) { - // penalize - this.node.services.pubsub.score.markInvalidMessageDelivery(peerId.toString(), Tx.p2pTopic); - return false; + const { name, severity } = outcome.failure; + + // Double spend validator has a special case handler + if (name === 'doubleSpendValidator') { + const isValid = await this.handleDoubleSpendFailure(tx, blockNumber, peerId); + if (isValid) return true; } - // double spend validation - const doubleSpendValidator = new DoubleSpendTxValidator({ - getNullifierIndex: async (nullifier: Fr) => { - const merkleTree = this.worldStateSynchronizer.getCommitted(); - const index = (await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0]; - return index; + this.peerManager.penalizePeer(peerId, severity); + return false; + } + + /** + * Create message validators for the given block number. + * + * Each validator is a pair of a validator and a severity. + * If a validator fails, the peer is penalized with the severity of the validator. + * + * @param blockNumber - The block number to create validators for. + * @returns The message validators. + */ + private createMessageValidators(blockNumber: number): Record { + return { + dataValidator: { + validator: new DataTxValidator(), + severity: PeerErrorSeverity.HighToleranceError, }, - }); - const validDoubleSpend = await doubleSpendValidator.validateTx(tx); - if (!validDoubleSpend) { - // check if nullifier is older than 20 blocks - if (blockNumber - this.config.severePeerPenaltyBlockLength > 0) { - const snapshotValidator = new DoubleSpendTxValidator({ + metadataValidator: { + validator: new MetadataTxValidator(new Fr(this.config.l1ChainId), new Fr(blockNumber)), + severity: PeerErrorSeverity.HighToleranceError, + }, + proofValidator: { + validator: new TxProofValidator(this.proofVerifier), + severity: PeerErrorSeverity.MidToleranceError, + }, + doubleSpendValidator: { + validator: new DoubleSpendTxValidator({ getNullifierIndex: async (nullifier: Fr) => { - const merkleTree = this.worldStateSynchronizer.getSnapshot( - blockNumber - this.config.severePeerPenaltyBlockLength, - ); + const merkleTree = this.worldStateSynchronizer.getCommitted(); const index = (await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0]; return index; }, - }); - - const validSnapshot = await snapshotValidator.validateTx(tx); - // High penalty if nullifier is older than 20 blocks - if (!validSnapshot) { - // penalize - this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError); - return false; - } - } - // penalize - this.peerManager.penalizePeer(peerId, PeerErrorSeverity.HighToleranceError); + }), + severity: PeerErrorSeverity.HighToleranceError, + }, + }; + } + + /** + * Run validations on a tx. + * @param tx - The tx to validate. + * @param messageValidators - The message validators to run. + * @returns The validation outcome. + */ + private async runValidations( + tx: Tx, + messageValidators: Record + ): Promise { + const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => { + const isValid = await validator.validateTx(tx); + return { name, isValid, severity }; + }); + + // A promise that resolves when all validations have been run + const allValidations = Promise.all(validationPromises); + + // A promise that resolves when the first validation fails + const firstFailure = Promise.race( + validationPromises.map(async (promise) => { + const result = await promise; + return result.isValid ? new Promise(() => {}) : result; + }) + ); + + // Wait for the first validation to fail or all validations to pass + const result = await Promise.race([ + allValidations.then(() => ({ allPassed: true as const })), + firstFailure.then(failure => ({ allPassed: false as const, failure: failure as ValidationResult })) + ]); + + // If all validations pass, allPassed will be true, if failed, then the failure will be the first validation to fail + return result; + } + + /** + * Handle a double spend failure. + * + * Double spend failures are managed on their own because they are a special case. + * We must check if the double spend is recent or old, if it is past a threshold, then we heavily penalize the peer. + * + * @param tx - The tx that failed the double spend validator. + * @param blockNumber - The block number of the tx. + * @param peerId - The peer ID of the peer that sent the tx. + * @returns True if the tx is valid, false otherwise. + */ + private async handleDoubleSpendFailure(tx: Tx, blockNumber: number, peerId: PeerId): Promise { + if (blockNumber <= this.config.severePeerPenaltyBlockLength) { return false; } - // proof validation - const proofValidator = new TxProofValidator(this.proofVerifier); - const validProof = await proofValidator.validateTx(tx); - if (!validProof) { - // penalize - this.peerManager.penalizePeer(peerId, PeerErrorSeverity.MidToleranceError); + const snapshotValidator = new DoubleSpendTxValidator({ + getNullifierIndex: async (nullifier: Fr) => { + const merkleTree = this.worldStateSynchronizer.getSnapshot( + blockNumber - this.config.severePeerPenaltyBlockLength, + ); + const index = (await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0]; + return index; + }, + }); + + const validSnapshot = await snapshotValidator.validateTx(tx); + if (!validSnapshot) { + this.peerManager.penalizePeer(peerId, PeerErrorSeverity.LowToleranceError); return false; } From 52f311ee601d52417b10caf3ce95896ca6432b5c Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Fri, 13 Dec 2024 10:44:00 +0000 Subject: [PATCH 4/6] dbg: validator timings --- .../p2p/src/services/libp2p/libp2p_service.ts | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index a553fb4a4aad..378cfd7f78c7 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -497,18 +497,12 @@ export class LibP2PService extends WithTracer implement }); } - private async processTxFromPeer(tx: Tx, peerId: PeerId): Promise { + private async processTxFromPeer(tx: Tx, _peerId: PeerId): Promise { const txHash = tx.getTxHash(); const txHashString = txHash.toString(); this.logger.verbose(`Received tx ${txHashString} from external peer.`); - const timer = new Timer(); - const isValidTx = await this.validatePropagatedTx(tx, peerId); - this.logger.info(`\n\n\n validatePropagatedTx took ${timer.ms()}ms \n\n\n`); - if (isValidTx) { - await this.mempools.txPool.addTxs([tx]); - } } /** @@ -550,7 +544,13 @@ export class LibP2PService extends WithTracer implement private async validatePropagatedTxFromMessage(propagationSource: PeerId, msg: Message): Promise { const tx = Tx.fromBuffer(Buffer.from(msg.data)); + const timer = new Timer(); const isValid = await this.validatePropagatedTx(tx, propagationSource); + this.logger.info(`\n\n\n validatePropagatedTx took ${timer.ms()}ms \n\n\n`); + this.logger.trace(`validatePropagatedTx: ${isValid}`, { + [Attributes.TX_HASH]: tx.getTxHash().toString(), + [Attributes.P2P_ID]: propagationSource.toString(), + }); return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject; } @@ -625,7 +625,9 @@ export class LibP2PService extends WithTracer implement messageValidators: Record ): Promise { const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => { + const timer = new Timer(); const isValid = await validator.validateTx(tx); + this.logger.info(`\n\n\n VALIDATOR: ${name} took ${timer.ms()}ms \n\n\n`); return { name, isValid, severity }; }); From 1b882d82efce10b90390aed610ea442654c0bdf7 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:23:46 +0000 Subject: [PATCH 5/6] tmp --- yarn-project/aztec-node/src/aztec-node/server.ts | 7 ++++--- .../p2p/src/services/libp2p/libp2p_service.ts | 12 ++++++------ .../src/tx_validator/double_spend_validator.test.ts | 4 ++-- .../p2p/src/tx_validator/double_spend_validator.ts | 11 +++++------ .../src/tx_validator/tx_validator_factory.ts | 8 ++++---- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index dd29b991f0cf..6049767b8fee 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -845,6 +845,8 @@ export class AztecNodeService implements AztecNode, Traceable { } } + // TODO(md): change this to run in parrel the same as in the p2p client - maybe not required + // as everything is sub ms apart from the double spend validator public async isValidTx(tx: Tx, isSimulation: boolean = false): Promise { const blockNumber = (await this.blockSource.getBlockNumber()) + 1; const db = this.worldStateSynchronizer.getCommitted(); @@ -855,9 +857,8 @@ export class AztecNodeService implements AztecNode, Traceable { new DataTxValidator(), new MetadataTxValidator(new Fr(this.l1ChainId), new Fr(blockNumber)), new DoubleSpendTxValidator({ - getNullifierIndex(nullifier) { - return db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]).then(x => x[0]); - }, + getNullifierIndices: nullifiers => + db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers).then(x => x.filter(index => index !== undefined) as bigint[]), }), ]; diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index 378cfd7f78c7..f7f196135bd9 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -603,10 +603,10 @@ export class LibP2PService extends WithTracer implement }, doubleSpendValidator: { validator: new DoubleSpendTxValidator({ - getNullifierIndex: async (nullifier: Fr) => { + getNullifierIndices: async (nullifiers: Buffer[]) => { const merkleTree = this.worldStateSynchronizer.getCommitted(); - const index = (await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0]; - return index; + const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers); + return indices.filter(index => index !== undefined) as bigint[]; }, }), severity: PeerErrorSeverity.HighToleranceError, @@ -669,12 +669,12 @@ export class LibP2PService extends WithTracer implement } const snapshotValidator = new DoubleSpendTxValidator({ - getNullifierIndex: async (nullifier: Fr) => { + getNullifierIndices: async (nullifiers: Buffer[]) => { const merkleTree = this.worldStateSynchronizer.getSnapshot( blockNumber - this.config.severePeerPenaltyBlockLength, ); - const index = (await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]))[0]; - return index; + const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers); + return indices.filter(index => index !== undefined) as bigint[]; }, }); diff --git a/yarn-project/p2p/src/tx_validator/double_spend_validator.test.ts b/yarn-project/p2p/src/tx_validator/double_spend_validator.test.ts index 1c123319f33e..e90d4b242f14 100644 --- a/yarn-project/p2p/src/tx_validator/double_spend_validator.test.ts +++ b/yarn-project/p2p/src/tx_validator/double_spend_validator.test.ts @@ -10,7 +10,7 @@ describe('DoubleSpendTxValidator', () => { beforeEach(() => { nullifierSource = mock({ - getNullifierIndex: mockFn().mockImplementation(() => { + getNullifierIndices: mockFn().mockImplementation(() => { return Promise.resolve(undefined); }), }); @@ -48,7 +48,7 @@ describe('DoubleSpendTxValidator', () => { it('rejects duplicates against history', async () => { const badTx = mockTx(); - nullifierSource.getNullifierIndex.mockReturnValueOnce(Promise.resolve(1n)); + nullifierSource.getNullifierIndices.mockReturnValueOnce(Promise.resolve([1n])); await expect(txValidator.validateTxs([badTx])).resolves.toEqual([[], [badTx]]); }); }); diff --git a/yarn-project/p2p/src/tx_validator/double_spend_validator.ts b/yarn-project/p2p/src/tx_validator/double_spend_validator.ts index 5bb06bf1fa9d..81538eaea342 100644 --- a/yarn-project/p2p/src/tx_validator/double_spend_validator.ts +++ b/yarn-project/p2p/src/tx_validator/double_spend_validator.ts @@ -3,7 +3,7 @@ import { Fr } from '@aztec/circuits.js'; import { createLogger } from '@aztec/foundation/log'; export interface NullifierSource { - getNullifierIndex: (nullifier: Fr) => Promise; + getNullifierIndices: (nullifiers: Buffer[]) => Promise; } export class DoubleSpendTxValidator implements TxValidator { @@ -36,9 +36,7 @@ export class DoubleSpendTxValidator implements TxValidator { } async #uniqueNullifiers(tx: AnyTx, thisBlockNullifiers: Set): Promise { - const nullifiers = (tx instanceof Tx ? tx.data.getNonEmptyNullifiers() : tx.txEffect.nullifiers).map(x => - x.toBigInt(), - ); + const nullifiers = (tx instanceof Tx ? tx.data.getNonEmptyNullifiers() : tx.txEffect.nullifiers); // Ditch this tx if it has repeated nullifiers const uniqueNullifiers = new Set(nullifiers); @@ -48,7 +46,8 @@ export class DoubleSpendTxValidator implements TxValidator { } if (this.isValidatingBlock) { - for (const nullifier of nullifiers) { + // TODO: remove all this type casting + for (const nullifier of nullifiers.map(n => n.toBigInt())) { if (thisBlockNullifiers.has(nullifier)) { this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} for repeating a nullifier in the same block`); return false; @@ -58,7 +57,7 @@ export class DoubleSpendTxValidator implements TxValidator { } } - const nullifierIndexes = await Promise.all(nullifiers.map(n => this.#nullifierSource.getNullifierIndex(new Fr(n)))); + const nullifierIndexes = await this.#nullifierSource.getNullifierIndices(nullifiers.map(n => n.toBuffer())); const hasDuplicates = nullifierIndexes.some(index => index !== undefined); if (hasDuplicates) { diff --git a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts index a3647b2d710c..80a9fb486c87 100644 --- a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts +++ b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts @@ -29,8 +29,8 @@ export class TxValidatorFactory { private enforceFees: boolean, ) { this.nullifierSource = { - getNullifierIndex: nullifier => - this.committedDb.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]).then(x => x[0]), + getNullifierIndices: nullifiers => + this.committedDb.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers).then(x => x.filter(index => index !== undefined) as bigint[]), }; this.publicStateSource = { @@ -57,8 +57,8 @@ export class TxValidatorFactory { validatorForProcessedTxs(fork: MerkleTreeReadOperations): TxValidator { return new DoubleSpendTxValidator({ - getNullifierIndex: nullifier => - fork.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, [nullifier.toBuffer()]).then(x => x[0]), + getNullifierIndices: nullifiers => + fork.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers).then(x => x.filter(index => index !== undefined) as bigint[]), }); } } From 361753baf81425446f930072d446ced1d12b1047 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:14:33 +0000 Subject: [PATCH 6/6] fmt --- .../aztec-node/src/aztec-node/server.ts | 5 +- .../p2p/src/services/libp2p/libp2p_service.ts | 58 +++++++++---------- .../double_spend_validator.test.ts | 2 +- .../tx_validator/double_spend_validator.ts | 13 ++--- .../src/tx_validator/tx_validator_factory.ts | 7 ++- 5 files changed, 41 insertions(+), 44 deletions(-) diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 6049767b8fee..60e2ce1ca221 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -845,8 +845,6 @@ export class AztecNodeService implements AztecNode, Traceable { } } - // TODO(md): change this to run in parrel the same as in the p2p client - maybe not required - // as everything is sub ms apart from the double spend validator public async isValidTx(tx: Tx, isSimulation: boolean = false): Promise { const blockNumber = (await this.blockSource.getBlockNumber()) + 1; const db = this.worldStateSynchronizer.getCommitted(); @@ -857,8 +855,7 @@ export class AztecNodeService implements AztecNode, Traceable { new DataTxValidator(), new MetadataTxValidator(new Fr(this.l1ChainId), new Fr(blockNumber)), new DoubleSpendTxValidator({ - getNullifierIndices: nullifiers => - db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers).then(x => x.filter(index => index !== undefined) as bigint[]), + getNullifierIndices: nullifiers => db.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers), }), ]; diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index f7f196135bd9..bfbf5b7df20b 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -29,7 +29,7 @@ import { createPeerScoreParams, createTopicScoreParams } from '@chainsafe/libp2p import { noise } from '@chainsafe/libp2p-noise'; import { yamux } from '@chainsafe/libp2p-yamux'; import { identify } from '@libp2p/identify'; -import { Message, PeerId, TopicValidatorResult } from '@libp2p/interface'; +import { type Message, type PeerId, TopicValidatorResult } from '@libp2p/interface'; import '@libp2p/kad-dht'; import { mplex } from '@libp2p/mplex'; import { tcp } from '@libp2p/tcp'; @@ -61,7 +61,6 @@ import { } from '../reqresp/interface.js'; import { ReqResp } from '../reqresp/reqresp.js'; import type { P2PService, PeerDiscoveryService } from '../service.js'; -import { Timer } from '@aztec/foundation/timer'; interface MessageValidator { validator: { @@ -76,9 +75,7 @@ interface ValidationResult { severity: PeerErrorSeverity; } -type ValidationOutcome = - | { allPassed: true } - | { allPassed: false; failure: ValidationResult }; +type ValidationOutcome = { allPassed: true } | { allPassed: false; failure: ValidationResult }; /** * Lib P2P implementation of the P2PService interface. @@ -160,10 +157,10 @@ export class LibP2PService extends WithTracer implement // add GossipSub listener this.node.services.pubsub.addEventListener('gossipsub:message', async e => { - const { msg, propagationSource: peerId } = e.detail; + const { msg } = e.detail; this.logger.trace(`Received PUBSUB message.`); - await this.jobQueue.put(() => this.handleNewGossipMessage(msg, peerId)); + await this.jobQueue.put(() => this.handleNewGossipMessage(msg)); }); // Start running promise for peer discovery @@ -306,7 +303,6 @@ export class LibP2PService extends WithTracer implement }, }); - // Create request response protocol handlers /** * Handler for tx requests @@ -405,10 +401,10 @@ export class LibP2PService extends WithTracer implement * @param topic - The message's topic. * @param data - The message data */ - private async handleNewGossipMessage(message: RawGossipMessage, peerId: PeerId) { + private async handleNewGossipMessage(message: RawGossipMessage) { if (message.topic === Tx.p2pTopic) { const tx = Tx.fromBuffer(Buffer.from(message.data)); - await this.processTxFromPeer(tx, peerId); + await this.processTxFromPeer(tx); } if (message.topic === BlockAttestation.p2pTopic && this.clientType === P2PClientType.Full) { const attestation = BlockAttestation.fromBuffer(Buffer.from(message.data)); @@ -497,12 +493,11 @@ export class LibP2PService extends WithTracer implement }); } - private async processTxFromPeer(tx: Tx, _peerId: PeerId): Promise { + private async processTxFromPeer(tx: Tx): Promise { const txHash = tx.getTxHash(); const txHashString = txHash.toString(); this.logger.verbose(`Received tx ${txHashString} from external peer.`); - - + await this.mempools.txPool.addTxs([tx]); } /** @@ -542,11 +537,12 @@ export class LibP2PService extends WithTracer implement return true; } - private async validatePropagatedTxFromMessage(propagationSource: PeerId, msg: Message): Promise { + private async validatePropagatedTxFromMessage( + propagationSource: PeerId, + msg: Message, + ): Promise { const tx = Tx.fromBuffer(Buffer.from(msg.data)); - const timer = new Timer(); const isValid = await this.validatePropagatedTx(tx, propagationSource); - this.logger.info(`\n\n\n validatePropagatedTx took ${timer.ms()}ms \n\n\n`); this.logger.trace(`validatePropagatedTx: ${isValid}`, { [Attributes.TX_HASH]: tx.getTxHash().toString(), [Attributes.P2P_ID]: propagationSource.toString(), @@ -554,6 +550,12 @@ export class LibP2PService extends WithTracer implement return isValid ? TopicValidatorResult.Accept : TopicValidatorResult.Reject; } + /** + * Validate a tx that has been propagated from a peer. + * @param tx - The tx to validate. + * @param peerId - The peer ID of the peer that sent the tx. + * @returns True if the tx is valid, false otherwise. + */ @trackSpan('Libp2pService.validatePropagatedTx', tx => ({ [Attributes.TX_HASH]: tx.getTxHash().toString(), })) @@ -571,7 +573,9 @@ export class LibP2PService extends WithTracer implement // Double spend validator has a special case handler if (name === 'doubleSpendValidator') { const isValid = await this.handleDoubleSpendFailure(tx, blockNumber, peerId); - if (isValid) return true; + if (isValid) { + return true; + } } this.peerManager.penalizePeer(peerId, severity); @@ -603,10 +607,9 @@ export class LibP2PService extends WithTracer implement }, doubleSpendValidator: { validator: new DoubleSpendTxValidator({ - getNullifierIndices: async (nullifiers: Buffer[]) => { + getNullifierIndices: (nullifiers: Buffer[]) => { const merkleTree = this.worldStateSynchronizer.getCommitted(); - const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers); - return indices.filter(index => index !== undefined) as bigint[]; + return merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers); }, }), severity: PeerErrorSeverity.HighToleranceError, @@ -622,12 +625,10 @@ export class LibP2PService extends WithTracer implement */ private async runValidations( tx: Tx, - messageValidators: Record + messageValidators: Record, ): Promise { const validationPromises = Object.entries(messageValidators).map(async ([name, { validator, severity }]) => { - const timer = new Timer(); const isValid = await validator.validateTx(tx); - this.logger.info(`\n\n\n VALIDATOR: ${name} took ${timer.ms()}ms \n\n\n`); return { name, isValid, severity }; }); @@ -636,16 +637,16 @@ export class LibP2PService extends WithTracer implement // A promise that resolves when the first validation fails const firstFailure = Promise.race( - validationPromises.map(async (promise) => { + validationPromises.map(async promise => { const result = await promise; return result.isValid ? new Promise(() => {}) : result; - }) + }), ); // Wait for the first validation to fail or all validations to pass const result = await Promise.race([ allValidations.then(() => ({ allPassed: true as const })), - firstFailure.then(failure => ({ allPassed: false as const, failure: failure as ValidationResult })) + firstFailure.then(failure => ({ allPassed: false as const, failure: failure as ValidationResult })), ]); // If all validations pass, allPassed will be true, if failed, then the failure will be the first validation to fail @@ -669,12 +670,11 @@ export class LibP2PService extends WithTracer implement } const snapshotValidator = new DoubleSpendTxValidator({ - getNullifierIndices: async (nullifiers: Buffer[]) => { + getNullifierIndices: (nullifiers: Buffer[]) => { const merkleTree = this.worldStateSynchronizer.getSnapshot( blockNumber - this.config.severePeerPenaltyBlockLength, ); - const indices = await merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers); - return indices.filter(index => index !== undefined) as bigint[]; + return merkleTree.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers); }, }); diff --git a/yarn-project/p2p/src/tx_validator/double_spend_validator.test.ts b/yarn-project/p2p/src/tx_validator/double_spend_validator.test.ts index e90d4b242f14..7b0fbb139742 100644 --- a/yarn-project/p2p/src/tx_validator/double_spend_validator.test.ts +++ b/yarn-project/p2p/src/tx_validator/double_spend_validator.test.ts @@ -11,7 +11,7 @@ describe('DoubleSpendTxValidator', () => { beforeEach(() => { nullifierSource = mock({ getNullifierIndices: mockFn().mockImplementation(() => { - return Promise.resolve(undefined); + return Promise.resolve([undefined]); }), }); txValidator = new DoubleSpendTxValidator(nullifierSource); diff --git a/yarn-project/p2p/src/tx_validator/double_spend_validator.ts b/yarn-project/p2p/src/tx_validator/double_spend_validator.ts index 81538eaea342..9f735e197b02 100644 --- a/yarn-project/p2p/src/tx_validator/double_spend_validator.ts +++ b/yarn-project/p2p/src/tx_validator/double_spend_validator.ts @@ -1,9 +1,8 @@ import { type AnyTx, Tx, type TxValidator } from '@aztec/circuit-types'; -import { Fr } from '@aztec/circuits.js'; import { createLogger } from '@aztec/foundation/log'; export interface NullifierSource { - getNullifierIndices: (nullifiers: Buffer[]) => Promise; + getNullifierIndices: (nullifiers: Buffer[]) => Promise<(bigint | undefined)[]>; } export class DoubleSpendTxValidator implements TxValidator { @@ -36,7 +35,7 @@ export class DoubleSpendTxValidator implements TxValidator { } async #uniqueNullifiers(tx: AnyTx, thisBlockNullifiers: Set): Promise { - const nullifiers = (tx instanceof Tx ? tx.data.getNonEmptyNullifiers() : tx.txEffect.nullifiers); + const nullifiers = tx instanceof Tx ? tx.data.getNonEmptyNullifiers() : tx.txEffect.nullifiers; // Ditch this tx if it has repeated nullifiers const uniqueNullifiers = new Set(nullifiers); @@ -46,14 +45,14 @@ export class DoubleSpendTxValidator implements TxValidator { } if (this.isValidatingBlock) { - // TODO: remove all this type casting - for (const nullifier of nullifiers.map(n => n.toBigInt())) { - if (thisBlockNullifiers.has(nullifier)) { + for (const nullifier of nullifiers) { + const nullifierBigInt = nullifier.toBigInt(); + if (thisBlockNullifiers.has(nullifierBigInt)) { this.#log.warn(`Rejecting tx ${Tx.getHash(tx)} for repeating a nullifier in the same block`); return false; } - thisBlockNullifiers.add(nullifier); + thisBlockNullifiers.add(nullifierBigInt); } } diff --git a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts index 80a9fb486c87..59b6baab1cf6 100644 --- a/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts +++ b/yarn-project/sequencer-client/src/tx_validator/tx_validator_factory.ts @@ -30,7 +30,9 @@ export class TxValidatorFactory { ) { this.nullifierSource = { getNullifierIndices: nullifiers => - this.committedDb.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers).then(x => x.filter(index => index !== undefined) as bigint[]), + this.committedDb + .findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers) + .then(x => x.filter(index => index !== undefined) as bigint[]), }; this.publicStateSource = { @@ -57,8 +59,7 @@ export class TxValidatorFactory { validatorForProcessedTxs(fork: MerkleTreeReadOperations): TxValidator { return new DoubleSpendTxValidator({ - getNullifierIndices: nullifiers => - fork.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers).then(x => x.filter(index => index !== undefined) as bigint[]), + getNullifierIndices: nullifiers => fork.findLeafIndices(MerkleTreeId.NULLIFIER_TREE, nullifiers), }); } }