diff --git a/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts b/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts index ea7489409bc8..d209b5957d97 100644 --- a/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts +++ b/yarn-project/aztec-node/src/aztec-node/http_rpc_server.ts @@ -19,6 +19,7 @@ import { NoteSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; +import { BaseHashType } from '@aztec/foundation/hash'; import { JsonRpcServer } from '@aztec/foundation/json-rpc/server'; /** @@ -40,6 +41,7 @@ export function createAztecNodeRpcServer(node: AztecNode) { TxEffect, LogId, TxHash, + BaseHashType, PublicDataWitness, SiblingPath, }, diff --git a/yarn-project/aztec.js/src/rpc_clients/pxe_client.ts b/yarn-project/aztec.js/src/rpc_clients/pxe_client.ts index 7c76f38a7f33..54b2aa81a11a 100644 --- a/yarn-project/aztec.js/src/rpc_clients/pxe_client.ts +++ b/yarn-project/aztec.js/src/rpc_clients/pxe_client.ts @@ -26,6 +26,7 @@ import { Point, } from '@aztec/circuits.js'; import { NoteSelector } from '@aztec/foundation/abi'; +import { BaseHashType } from '@aztec/foundation/hash'; import { createJsonRpcClient, makeFetch } from '@aztec/foundation/json-rpc/client'; /** @@ -54,6 +55,7 @@ export const createPXEClient = (url: string, fetch = makeFetch([1, 2, 3], false) Point, TxExecutionRequest, TxHash, + BaseHashType, }, { EncryptedNoteL2BlockL2Logs, diff --git a/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts b/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts index 6bd10b74e74d..85056113aec4 100644 --- a/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts +++ b/yarn-project/circuit-types/src/aztec_node/rpc/aztec_node_client.ts @@ -3,6 +3,7 @@ import { EventSelector, NoteSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr } from '@aztec/foundation/fields'; +import { BaseHashType } from '@aztec/foundation/hash'; import { createJsonRpcClient, defaultFetch } from '@aztec/foundation/json-rpc/client'; import { type AztecNode } from '../../interfaces/aztec-node.js'; @@ -40,6 +41,7 @@ export function createAztecNodeClient(url: string, fetch = defaultFetch): AztecN TxEffect, LogId, TxHash, + BaseHashType, PublicDataWitness, SiblingPath, }, diff --git a/yarn-project/circuit-types/src/index.ts b/yarn-project/circuit-types/src/index.ts index 3745ffca3eed..93ed958b7a56 100644 --- a/yarn-project/circuit-types/src/index.ts +++ b/yarn-project/circuit-types/src/index.ts @@ -21,3 +21,4 @@ export * from './simulation_error.js'; export * from './tx/index.js'; export * from './tx_effect.js'; export * from './tx_execution_request.js'; +export * from './p2p/index.js'; diff --git a/yarn-project/circuit-types/src/p2p/block_attestation.test.ts b/yarn-project/circuit-types/src/p2p/block_attestation.test.ts new file mode 100644 index 000000000000..75628308ae7e --- /dev/null +++ b/yarn-project/circuit-types/src/p2p/block_attestation.test.ts @@ -0,0 +1,22 @@ +// Serde test for the block attestation type +import { makeHeader } from '@aztec/circuits.js/testing'; + +import { BlockAttestation } from './block_attestation.js'; + +const makeBlockAttestation = (): BlockAttestation => { + const blockHeader = makeHeader(1); + const signature = Buffer.alloc(64, 1); + + return new BlockAttestation(blockHeader, signature); +}; + +describe('Block Attestation serialization / deserialization', () => { + it('Should serialize / deserialize', () => { + const attestation = makeBlockAttestation(); + + const serialized = attestation.toBuffer(); + const deserialized = BlockAttestation.fromBuffer(serialized); + + expect(deserialized).toEqual(attestation); + }); +}); diff --git a/yarn-project/circuit-types/src/p2p/block_attestation.ts b/yarn-project/circuit-types/src/p2p/block_attestation.ts new file mode 100644 index 000000000000..d1d34a1570e1 --- /dev/null +++ b/yarn-project/circuit-types/src/p2p/block_attestation.ts @@ -0,0 +1,48 @@ +import { Header } from '@aztec/circuits.js'; +import { BaseHashType } from '@aztec/foundation/hash'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { Gossipable } from './gossipable.js'; +import { TopicType, createTopicString } from './topic_type.js'; + +export class BlockAttestationHash extends BaseHashType { + constructor(hash: Buffer) { + super(hash); + } +} + +/** + * BlockAttestation + * + * A validator that has attested to seeing the contents of a block + * will produce a block attestation over the header of the block + */ +export class BlockAttestation extends Gossipable { + static override p2pTopic: string; + + constructor( + /** The block header the attestation is made over */ + public readonly header: Header, + /** The signature of the block attester */ + public readonly signature: Buffer, + ) { + super(); + } + + static { + this.p2pTopic = createTopicString(TopicType.block_attestation); + } + + override p2pMessageIdentifier(): BaseHashType { + return BlockAttestationHash.fromField(this.header.hash()); + } + + toBuffer(): Buffer { + return serializeToBuffer([this.header, this.signature.length, this.signature]); + } + + static fromBuffer(buf: Buffer | BufferReader): BlockAttestation { + const reader = BufferReader.asReader(buf); + return new BlockAttestation(reader.readObject(Header), reader.readBuffer()); + } +} diff --git a/yarn-project/circuit-types/src/p2p/block_proposal.test.ts b/yarn-project/circuit-types/src/p2p/block_proposal.test.ts new file mode 100644 index 000000000000..995dfef9cb98 --- /dev/null +++ b/yarn-project/circuit-types/src/p2p/block_proposal.test.ts @@ -0,0 +1,24 @@ +// Serde test for the block proposal type +import { makeHeader } from '@aztec/circuits.js/testing'; + +import { TxHash } from '../index.js'; +import { BlockProposal } from './block_proposal.js'; + +describe('Block Proposal serialization / deserialization', () => { + const makeBlockProposal = (): BlockProposal => { + const blockHeader = makeHeader(1); + const txs = [0, 1, 2, 3, 4, 5].map(() => TxHash.random()); + const signature = Buffer.alloc(64, 1); + + return new BlockProposal(blockHeader, txs, signature); + }; + + it('Should serialize / deserialize', () => { + const proposal = makeBlockProposal(); + + const serialized = proposal.toBuffer(); + const deserialized = BlockProposal.fromBuffer(serialized); + + expect(deserialized).toEqual(proposal); + }); +}); diff --git a/yarn-project/circuit-types/src/p2p/block_proposal.ts b/yarn-project/circuit-types/src/p2p/block_proposal.ts new file mode 100644 index 000000000000..49aae6a5a679 --- /dev/null +++ b/yarn-project/circuit-types/src/p2p/block_proposal.ts @@ -0,0 +1,55 @@ +import { Header } from '@aztec/circuits.js'; +import { BaseHashType } from '@aztec/foundation/hash'; +import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; + +import { TxHash } from '../index.js'; +import { Gossipable } from './gossipable.js'; +import { TopicType, createTopicString } from './topic_type.js'; + +export class BlockProposalHash extends BaseHashType { + constructor(hash: Buffer) { + super(hash); + } +} + +/** + * BlockProposal + * + * A block proposal is created by the leader of the chain proposing a sequence of transactions to + * be included in the head of the chain + */ +export class BlockProposal extends Gossipable { + static override p2pTopic: string; + + constructor( + /** The block header, after execution of the below sequence of transactions */ + public readonly header: Header, + /** The sequence of transactions in the block */ + public readonly txs: TxHash[], + /** The signer of the BlockProposal over the header of the new block*/ + public readonly signature: Buffer, + ) { + super(); + } + + static { + this.p2pTopic = createTopicString(TopicType.block_proposal); + } + + override p2pMessageIdentifier(): BaseHashType { + return BlockProposalHash.fromField(this.header.hash()); + } + + toBuffer(): Buffer { + return serializeToBuffer([this.header, this.txs.length, this.txs, this.signature.length, this.signature]); + } + + static fromBuffer(buf: Buffer | BufferReader): BlockProposal { + const reader = BufferReader.asReader(buf); + return new BlockProposal( + reader.readObject(Header), + reader.readArray(reader.readNumber(), TxHash), + reader.readBuffer(), + ); + } +} diff --git a/yarn-project/circuit-types/src/p2p/gossipable.ts b/yarn-project/circuit-types/src/p2p/gossipable.ts new file mode 100644 index 000000000000..10cfa506ce95 --- /dev/null +++ b/yarn-project/circuit-types/src/p2p/gossipable.ts @@ -0,0 +1,26 @@ +import { type BaseHashType } from '@aztec/foundation/hash'; + +/** + * Gossipable + * + * Any class which extends gossipable will be able to be Gossiped over the p2p network + */ +export abstract class Gossipable { + /** p2p Topic + * + * - The p2p topic identifier, this determines how the message is handled + */ + static p2pTopic: string; + + /** p2p Message Identifier + * + * - A digest of the message information, this key is used for deduplication + */ + abstract p2pMessageIdentifier(): BaseHashType; + + /** To Buffer + * + * - Serialization method + */ + abstract toBuffer(): Buffer; +} diff --git a/yarn-project/circuit-types/src/p2p/index.ts b/yarn-project/circuit-types/src/p2p/index.ts new file mode 100644 index 000000000000..31f8d8c56e39 --- /dev/null +++ b/yarn-project/circuit-types/src/p2p/index.ts @@ -0,0 +1,5 @@ +export * from './block_attestation.js'; +export * from './block_proposal.js'; +export * from './interface.js'; +export * from './gossipable.js'; +export * from './topic_type.js'; diff --git a/yarn-project/circuit-types/src/p2p/interface.ts b/yarn-project/circuit-types/src/p2p/interface.ts new file mode 100644 index 000000000000..c2de043d00e7 --- /dev/null +++ b/yarn-project/circuit-types/src/p2p/interface.ts @@ -0,0 +1,17 @@ +import { Tx } from '../tx/tx.js'; +import { BlockAttestation } from './block_attestation.js'; +import { BlockProposal } from './block_proposal.js'; +import { type Gossipable } from './gossipable.js'; +import { TopicType } from './topic_type.js'; + +export interface RawGossipMessage { + topic: string; + data: Uint8Array; +} + +// Force casts as we know that each field here extends Gossipable, and we just want types from Gossipable +export const TopicTypeMap: Record = { + [TopicType.tx]: Tx as unknown as typeof Gossipable, + [TopicType.block_proposal]: BlockProposal as unknown as typeof Gossipable, + [TopicType.block_attestation]: BlockAttestation as unknown as typeof Gossipable, +}; diff --git a/yarn-project/circuit-types/src/p2p/topic_type.ts b/yarn-project/circuit-types/src/p2p/topic_type.ts new file mode 100644 index 000000000000..2310d85958e6 --- /dev/null +++ b/yarn-project/circuit-types/src/p2p/topic_type.ts @@ -0,0 +1,18 @@ +/** Create Topic String + * + * The topic channel identifier + * @param topicType + * @returns + */ +export function createTopicString(topicType: TopicType) { + return '/aztec/' + topicType + '/0.1.0'; +} + +/** + * + */ +export enum TopicType { + tx = 'tx', + block_proposal = 'block_proposal', + block_attestation = 'block_attestation', +} diff --git a/yarn-project/circuit-types/src/tx/tx.ts b/yarn-project/circuit-types/src/tx/tx.ts index 74d5d26993d9..fc4e8b16eeb6 100644 --- a/yarn-project/circuit-types/src/tx/tx.ts +++ b/yarn-project/circuit-types/src/tx/tx.ts @@ -5,11 +5,14 @@ import { type PublicKernelCircuitPublicInputs, } from '@aztec/circuits.js'; import { arraySerializedSizeOfNonEmpty } from '@aztec/foundation/collection'; +import { type BaseHashType } from '@aztec/foundation/hash'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { type GetUnencryptedLogsResponse } from '../logs/get_unencrypted_logs_response.js'; import { type L2LogsSource } from '../logs/l2_logs_source.js'; import { EncryptedNoteTxL2Logs, EncryptedTxL2Logs, UnencryptedTxL2Logs } from '../logs/tx_l2_logs.js'; +import { Gossipable } from '../p2p/gossipable.js'; +import { TopicType, createTopicString } from '../p2p/topic_type.js'; import { PublicExecutionRequest } from '../public_execution_request.js'; import { type TxStats } from '../stats/stats.js'; import { TxHash } from './tx_hash.js'; @@ -17,7 +20,9 @@ import { TxHash } from './tx_hash.js'; /** * The interface of an L2 transaction. */ -export class Tx { +export class Tx extends Gossipable { + static override p2pTopic: string; + constructor( /** * Output of the private kernel circuit for this tx. @@ -49,7 +54,19 @@ export class Tx { * Public function call to be run by the sequencer as part of teardown. */ public readonly publicTeardownFunctionCall: PublicExecutionRequest, - ) {} + ) { + super(); + } + + // Gossipable method + static { + this.p2pTopic = createTopicString(TopicType.tx); + } + + // Gossipable method + override p2pMessageIdentifier(): BaseHashType { + return this.getTxHash(); + } hasPublicCalls() { return this.data.numberOfPublicCallRequests() > 0; diff --git a/yarn-project/circuit-types/src/tx/tx_hash.ts b/yarn-project/circuit-types/src/tx/tx_hash.ts index c92340c1928b..2530ee5ccf9f 100644 --- a/yarn-project/circuit-types/src/tx/tx_hash.ts +++ b/yarn-project/circuit-types/src/tx/tx_hash.ts @@ -1,117 +1,15 @@ -import { randomBytes } from '@aztec/foundation/crypto'; -import { BufferReader, deserializeBigInt, serializeBigInt } from '@aztec/foundation/serialize'; +import { BaseHashType } from '@aztec/foundation/hash'; /** * A class representing hash of Aztec transaction. */ -export class TxHash { - /** - * The size of the hash in bytes. - */ - public static SIZE = 32; - - /** - * TxHash with value zero. - */ - public static ZERO = new TxHash(Buffer.alloc(TxHash.SIZE)); - +export class TxHash extends BaseHashType { constructor( /** * The buffer containing the hash. */ - public buffer: Buffer, + hash: Buffer, ) { - if (buffer.length !== TxHash.SIZE) { - throw new Error(`Expected buffer to have length ${TxHash.SIZE} but was ${buffer.length}`); - } - } - - /** - * Returns the raw buffer of the hash. - * @returns The buffer containing the hash. - */ - public toBuffer() { - return this.buffer; - } - - /** - * Creates a TxHash from a buffer. - * @param buffer - The buffer to create from. - * @returns A new TxHash object. - */ - public static fromBuffer(buffer: Buffer | BufferReader) { - const reader = BufferReader.asReader(buffer); - return new TxHash(reader.readBytes(TxHash.SIZE)); - } - - /** - * Checks if this hash and another hash are equal. - * @param hash - A hash to compare with. - * @returns True if the hashes are equal, false otherwise. - */ - public equals(hash: TxHash): boolean { - return this.buffer.equals(hash.buffer); - } - - /** - * Returns true if this hash is zero. - * @returns True if this hash is zero. - */ - public isZero(): boolean { - return this.buffer.equals(Buffer.alloc(32, 0)); - } - - /** - * Convert this hash to a hex string. - * @returns The hex string. - */ - public toString() { - return this.buffer.toString('hex'); - } - - /** - * Convert this hash to a big int. - * @returns The big int. - */ - public toBigInt() { - return deserializeBigInt(this.buffer, 0, TxHash.SIZE).elem; - } - /** - * Creates a tx hash from a bigint. - * @param hash - The tx hash as a big int. - * @returns The TxHash. - */ - public static fromBigInt(hash: bigint) { - return new TxHash(serializeBigInt(hash, TxHash.SIZE)); - } - /** - * Converts this hash from a buffer of 28 bytes. - * Verifies the input is 28 bytes. - * @param buffer - The 28 byte buffer to construct from. - * @returns A TxHash created from the input buffer with 4 bytes 0 padding at the front. - */ - public static fromBuffer28(buffer: Buffer) { - if (buffer.length != 28) { - throw new Error(`Expected TxHash input buffer to be 28 bytes`); - } - const padded = Buffer.concat([Buffer.alloc(this.SIZE - 28), buffer]); - return new TxHash(padded); - } - - /** - * Converts a string into a TxHash object. - * @param str - The TX hash in string format. - * @returns A new TxHash object. - */ - public static fromString(str: string): TxHash { - return new TxHash(Buffer.from(str, 'hex')); - } - - /** - * Generates a random TxHash. - * @returns A new TxHash object. - */ - public static random(): TxHash { - return new TxHash(Buffer.from(randomBytes(TxHash.SIZE))); + super(hash); } } diff --git a/yarn-project/foundation/package.json b/yarn-project/foundation/package.json index 17295e60f261..54131f1c9856 100644 --- a/yarn-project/foundation/package.json +++ b/yarn-project/foundation/package.json @@ -18,6 +18,7 @@ "./eth-address": "./dest/eth-address/index.js", "./fifo": "./dest/fifo/index.js", "./fs": "./dest/fs/index.js", + "./hash": "./dest/hash/index.js", "./json-rpc": "./dest/json-rpc/index.js", "./json-rpc/server": "./dest/json-rpc/server/index.js", "./json-rpc/client": "./dest/json-rpc/client/index.js", diff --git a/yarn-project/foundation/src/hash/index.ts b/yarn-project/foundation/src/hash/index.ts new file mode 100644 index 000000000000..e6071e82cf53 --- /dev/null +++ b/yarn-project/foundation/src/hash/index.ts @@ -0,0 +1,123 @@ +import { randomBytes } from '@aztec/foundation/crypto'; +import { BufferReader, deserializeBigInt, serializeBigInt } from '@aztec/foundation/serialize'; + +import { type Fr } from '../fields/fields.js'; + +/** + * A class representing a hash. + */ +export class BaseHashType { + /** + * The size of the hash in bytes. + */ + public static SIZE = 32; + + /** + * HashType with value zero. + */ + public static ZERO = new BaseHashType(Buffer.alloc(BaseHashType.SIZE)); + + constructor( + /** + * The buffer containing the hash. + */ + public buffer: Buffer, + ) { + if (buffer.length !== BaseHashType.SIZE) { + throw new Error(`Expected buffer to have length ${BaseHashType.SIZE} but was ${buffer.length}`); + } + } + + /** + * Returns the raw buffer of the hash. + * @returns The buffer containing the hash. + */ + public toBuffer() { + return this.buffer; + } + + /** + * Creates a HashType from a buffer. + * @param buffer - The buffer to create from. + * @returns A new HashType object. + */ + public static fromBuffer(buffer: Buffer | BufferReader) { + const reader = BufferReader.asReader(buffer); + return new BaseHashType(reader.readBytes(BaseHashType.SIZE)); + } + + /** + * Checks if this hash and another hash are equal. + * @param hash - A hash to compare with. + * @returns True if the hashes are equal, false otherwise. + */ + public equals(hash: BaseHashType): boolean { + return this.buffer.equals(hash.buffer); + } + + /** + * Returns true if this hash is zero. + * @returns True if this hash is zero. + */ + public isZero(): boolean { + return this.buffer.equals(Buffer.alloc(32, 0)); + } + + /** + * Convert this hash to a hex string. + * @returns The hex string. + */ + public toString() { + return this.buffer.toString('hex'); + } + + /** + * Convert this hash to a big int. + * @returns The big int. + */ + public toBigInt() { + return deserializeBigInt(this.buffer, 0, BaseHashType.SIZE).elem; + } + /** + * Creates a tx hash from a bigint. + * @param hash - The tx hash as a big int. + * @returns The HashType. + */ + public static fromBigInt(hash: bigint) { + return new BaseHashType(serializeBigInt(hash, BaseHashType.SIZE)); + } + public static fromField(hash: Fr) { + return new BaseHashType(serializeBigInt(hash.toBigInt())); + } + + /** + * Converts this hash from a buffer of 28 bytes. + * Verifies the input is 28 bytes. + * @param buffer - The 28 byte buffer to construct from. + * @returns A HashType created from the input buffer with 4 bytes 0 padding at the front. + */ + public static fromBuffer28(buffer: Buffer) { + if (buffer.length != 28) { + throw new Error(`Expected HashType input buffer to be 28 bytes`); + } + const padded = Buffer.concat([Buffer.alloc(this.SIZE - 28), buffer]); + return new BaseHashType(padded); + } + + /** + * Converts a string into a HashType object. + * @param str - The TX hash in string format. + * @returns A new HashType object. + */ + public static fromString(str: string): BaseHashType { + return new BaseHashType(Buffer.from(str, 'hex')); + } + + /** + * Generates a random HashType. + * @returns A new HashType object. + */ + public static random(): BaseHashType { + return new BaseHashType(Buffer.from(randomBytes(BaseHashType.SIZE))); + } +} diff --git a/yarn-project/foundation/src/index.ts b/yarn-project/foundation/src/index.ts index 5ef1acbcfd08..fc73842be27c 100644 --- a/yarn-project/foundation/src/index.ts +++ b/yarn-project/foundation/src/index.ts @@ -28,3 +28,4 @@ export * as url from './url/index.js'; export * as wasm from './wasm/index.js'; export * as worker from './worker/index.js'; export * as testing from './testing/index.js'; +export * as hash from './hash/index.js'; diff --git a/yarn-project/p2p/src/config.ts b/yarn-project/p2p/src/config.ts index 0735cbd010be..d46173017601 100644 --- a/yarn-project/p2p/src/config.ts +++ b/yarn-project/p2p/src/config.ts @@ -1,5 +1,3 @@ -import { SemVer } from 'semver'; - /** * P2P client configuration values. */ @@ -79,11 +77,6 @@ export interface P2PConfig { */ dataDirectory?: string; - /** - * The transaction gossiping message version. - */ - txGossipVersion: SemVer; - /** * If announceUdpAddress or announceTcpAddress are not provided, query for the IP address of the machine. Default is false. */ @@ -113,7 +106,6 @@ export function getP2PConfigEnvVars(): P2PConfig { P2P_MIN_PEERS, P2P_MAX_PEERS, DATA_DIRECTORY, - TX_GOSSIP_VERSION, P2P_TX_PROTOCOL, P2P_QUERY_FOR_IP, P2P_TX_POOL_KEEP_PROVEN_FOR, @@ -136,7 +128,6 @@ export function getP2PConfigEnvVars(): P2PConfig { minPeerCount: P2P_MIN_PEERS ? +P2P_MIN_PEERS : 10, maxPeerCount: P2P_MAX_PEERS ? +P2P_MAX_PEERS : 100, dataDirectory: DATA_DIRECTORY, - txGossipVersion: TX_GOSSIP_VERSION ? new SemVer(TX_GOSSIP_VERSION) : new SemVer('0.1.0'), queryForIp: P2P_QUERY_FOR_IP === 'true', keepProvenTxsInPoolFor: P2P_TX_POOL_KEEP_PROVEN_FOR ? +P2P_TX_POOL_KEEP_PROVEN_FOR : 0, }; diff --git a/yarn-project/p2p/src/service/discv5_service.test.ts b/yarn-project/p2p/src/service/discv5_service.test.ts index 4a278f378225..e6dd4d5efb2a 100644 --- a/yarn-project/p2p/src/service/discv5_service.test.ts +++ b/yarn-project/p2p/src/service/discv5_service.test.ts @@ -2,7 +2,6 @@ import { sleep } from '@aztec/foundation/sleep'; import { jest } from '@jest/globals'; import type { PeerId } from '@libp2p/interface'; -import { SemVer } from 'semver'; import { BootstrapNode } from '../bootstrap/bootstrap.js'; import { type P2PConfig } from '../config.js'; @@ -135,7 +134,6 @@ describe('Discv5Service', () => { transactionProtocol: 'aztec/1.0.0', p2pEnabled: true, p2pL2QueueSize: 100, - txGossipVersion: new SemVer('0.1.0'), keepProvenTxsInPoolFor: 0, }; return new DiscV5Service(peerId, config); diff --git a/yarn-project/p2p/src/service/libp2p_service.ts b/yarn-project/p2p/src/service/libp2p_service.ts index 9a2cdc2191fb..3d7472db132e 100644 --- a/yarn-project/p2p/src/service/libp2p_service.ts +++ b/yarn-project/p2p/src/service/libp2p_service.ts @@ -1,4 +1,4 @@ -import { Tx } from '@aztec/circuit-types'; +import { type Gossipable, type RawGossipMessage, TopicType, TopicTypeMap, Tx } from '@aztec/circuit-types'; import { SerialQueue } from '@aztec/foundation/fifo'; import { createDebugLogger } from '@aztec/foundation/log'; import { RunningPromise } from '@aztec/foundation/running-promise'; @@ -21,7 +21,6 @@ import { convertToMultiaddr } from '../util.js'; import { AztecDatastore } from './data_store.js'; import { PeerManager } from './peer_manager.js'; import type { P2PService, PeerDiscoveryService } from './service.js'; -import { AztecTxMessageCreator } from './tx_messages.js'; export interface PubSubLibp2p extends Libp2p { services: { @@ -49,7 +48,6 @@ export async function createLibP2PPeerId(privateKey?: string): Promise { */ export class LibP2PService implements P2PService { private jobQueue: SerialQueue = new SerialQueue(); - private messageCreator: AztecTxMessageCreator; private peerManager: PeerManager; private discoveryRunningPromise?: RunningPromise; constructor( @@ -59,7 +57,6 @@ export class LibP2PService implements P2PService { private txPool: TxPool, private logger = createDebugLogger('aztec:libp2p_service'), ) { - this.messageCreator = new AztecTxMessageCreator(config.txGossipVersion); this.peerManager = new PeerManager(node, peerDiscoveryService, config, logger); } @@ -89,14 +86,16 @@ export class LibP2PService implements P2PService { this.logger.info(`Started P2P client with Peer ID ${this.node.peerId.toString()}`); // Subscribe to standard GossipSub topics by default - this.subscribeToTopic(this.messageCreator.getTopic()); + for (const topic in TopicType) { + this.subscribeToTopic(TopicTypeMap[topic].p2pTopic); + } // add GossipSub listener this.node.services.pubsub.addEventListener('gossipsub:message', async e => { const { msg } = e.detail; this.logger.debug(`Received PUBSUB message.`); - await this.jobQueue.put(() => this.handleNewGossipMessage(msg.topic, msg.data)); + await this.jobQueue.put(() => this.handleNewGossipMessage(msg)); }); // Start running promise for peer discovery @@ -233,14 +232,13 @@ export class LibP2PService implements P2PService { * @param topic - The message's topic. * @param data - The message data */ - private async handleNewGossipMessage(topic: string, data: Uint8Array) { - if (topic !== this.messageCreator.getTopic()) { - // Invalid TX Topic, ignore - return; + private async handleNewGossipMessage(message: RawGossipMessage) { + if (message.topic === Tx.p2pTopic) { + const tx = Tx.fromBuffer(Buffer.from(message.data)); + await this.processTxFromPeer(tx); } - const tx = Tx.fromBuffer(Buffer.from(data)); - await this.processTxFromPeer(tx); + return; } /** @@ -248,7 +246,7 @@ export class LibP2PService implements P2PService { * @param tx - The transaction to propagate. */ public propagateTx(tx: Tx): void { - void this.jobQueue.put(() => Promise.resolve(this.sendTxToPeers(tx))); + void this.jobQueue.put(() => Promise.resolve(this.sendToPeers(tx))); } private async processTxFromPeer(tx: Tx): Promise { @@ -258,11 +256,14 @@ export class LibP2PService implements P2PService { await this.txPool.addTxs([tx]); } - private async sendTxToPeers(tx: Tx) { - const { data: txData } = this.messageCreator.createTxMessage(tx); - this.logger.verbose(`Sending tx ${tx.getTxHash().toString()} to peers`); - const recipientsNum = await this.publishToTopic(this.messageCreator.getTopic(), txData); - this.logger.verbose(`Sent tx ${tx.getTxHash().toString()} to ${recipientsNum} peers`); + private async sendToPeers(message: T) { + const parent = message.constructor as typeof Gossipable; + + const identifier = message.p2pMessageIdentifier().toString(); + this.logger.verbose(`Sending tx ${identifier} to peers`); + + const recipientsNum = await this.publishToTopic(parent.p2pTopic, message.toBuffer()); + this.logger.verbose(`Sent tx ${identifier} to ${recipientsNum} peers`); } // Libp2p seems to hang sometimes if new peers are initiating connections. diff --git a/yarn-project/p2p/src/service/tx_messages.test.ts b/yarn-project/p2p/src/service/tx_messages.test.ts deleted file mode 100644 index 3213a67e6d75..000000000000 --- a/yarn-project/p2p/src/service/tx_messages.test.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Tx, mockTx } from '@aztec/circuit-types'; - -import { expect } from '@jest/globals'; - -const verifyTx = (actual: Tx, expected: Tx) => { - expect(actual.data.toBuffer()).toEqual(expected.data.toBuffer()); - expect(actual.clientIvcProof.toBuffer()).toEqual(expected.clientIvcProof.toBuffer()); - expect(actual.encryptedLogs.toBuffer()).toEqual(expected.encryptedLogs.toBuffer()); -}; - -describe('Messages', () => { - it('Correctly serializes and deserializes a single private transaction', () => { - const transaction = mockTx(); - const message = transaction.toBuffer(); - const decodedTransaction = Tx.fromBuffer(message); - verifyTx(decodedTransaction, transaction); - }); -}); diff --git a/yarn-project/p2p/src/service/tx_messages.ts b/yarn-project/p2p/src/service/tx_messages.ts deleted file mode 100644 index 687b0d90d09b..000000000000 --- a/yarn-project/p2p/src/service/tx_messages.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { type Tx } from '@aztec/circuit-types'; - -import { type SemVer } from 'semver'; - -export const TX_MESSAGE_TOPIC = ''; - -export class AztecTxMessageCreator { - private readonly topic: string; - constructor(version: SemVer) { - this.topic = `/aztec/tx/${version.toString()}`; - } - - createTxMessage(tx: Tx) { - const messageData = tx.toBuffer(); - - return { topic: this.topic, data: messageData }; - } - - getTopic() { - return this.topic; - } -} diff --git a/yarn-project/pxe/src/pxe_http/pxe_http_server.ts b/yarn-project/pxe/src/pxe_http/pxe_http_server.ts index cde4b5da5a2b..c4ab90e3a9d3 100644 --- a/yarn-project/pxe/src/pxe_http/pxe_http_server.ts +++ b/yarn-project/pxe/src/pxe_http/pxe_http_server.ts @@ -22,6 +22,7 @@ import { NoteSelector } from '@aztec/foundation/abi'; import { AztecAddress } from '@aztec/foundation/aztec-address'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Fr, GrumpkinScalar, Point } from '@aztec/foundation/fields'; +import { BaseHashType } from '@aztec/foundation/hash'; import { JsonRpcServer, createNamespacedJsonRpcServer } from '@aztec/foundation/json-rpc/server'; import http from 'http'; @@ -40,6 +41,7 @@ export function createPXERpcServer(pxeService: PXE): JsonRpcServer { ExtendedUnencryptedL2Log, FunctionSelector, TxHash, + BaseHashType, EthAddress, Point, Fr,