diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 68d89f05dae..e841e038179 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -4,6 +4,7 @@ import { type AztecNode, type ClientProtocolCircuitVerifier, type EpochProofQuote, + EpochProofQuoteHasher, type GetUnencryptedLogsResponse, type InBlock, type L1ToL2MessageSource, @@ -167,6 +168,7 @@ export class AztecNodeService implements AztecNode, Traceable { log.warn(`Aztec node is accepting fake proofs`); } + const epochProofQuoteHasher = new EpochProofQuoteHasher(config.l1Contracts.rollupAddress, config.l1ChainId); const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config, { dateProvider }); // create the tx pool and the p2p client, which will need the l2 block source @@ -177,6 +179,7 @@ export class AztecNodeService implements AztecNode, Traceable { proofVerifier, worldStateSynchronizer, epochCache, + epochProofQuoteHasher, telemetry, ); diff --git a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.test.ts b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.test.ts index fe29fcb941d..07010c68aff 100644 --- a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.test.ts +++ b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.test.ts @@ -1,14 +1,20 @@ import { EthAddress } from '@aztec/circuits.js'; -import { Signature } from '@aztec/foundation/eth-signature'; +import { Secp256k1Signer } from '@aztec/foundation/crypto'; import { jsonParseWithSchema, jsonStringify } from '@aztec/foundation/json-rpc'; +import { getHashedSignaturePayloadEthSignedMessage } from '../p2p/signature_utils.js'; import { EpochProofQuote } from './epoch_proof_quote.js'; +import { EpochProofQuoteHasher } from './epoch_proof_quote_hasher.js'; import { EpochProofQuotePayload } from './epoch_proof_quote_payload.js'; describe('epoch proof quote', () => { let quote: EpochProofQuote; + let signer: Secp256k1Signer; + let hasher: EpochProofQuoteHasher; beforeEach(() => { + signer = Secp256k1Signer.random(); + const payload = EpochProofQuotePayload.from({ basisPointFee: 5000, bondAmount: 1000000000000000000n, @@ -17,7 +23,11 @@ describe('epoch proof quote', () => { validUntilSlot: 100n, }); - quote = new EpochProofQuote(payload, Signature.random()); + hasher = new EpochProofQuoteHasher(EthAddress.random(), 1); + + const digest = hasher.hash(payload); + const signature = signer.sign(digest); + quote = new EpochProofQuote(payload, signature); }); const checkEquivalence = (serialized: EpochProofQuote, deserialized: EpochProofQuote) => { @@ -28,6 +38,10 @@ describe('epoch proof quote', () => { it('should serialize and deserialize from buffer', () => { const deserialised = EpochProofQuote.fromBuffer(quote.toBuffer()); checkEquivalence(quote, deserialised); + + // Recover the signer + const recovered = deserialised.getSender(hasher); + expect(recovered).toEqual(signer.address); }); it('should serialize and deserialize from JSON', () => { diff --git a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts index 454d01aa585..b6bc8bdc533 100644 --- a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts +++ b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote.ts @@ -1,5 +1,5 @@ import { Buffer32 } from '@aztec/foundation/buffer'; -import { type Secp256k1Signer, keccak256 } from '@aztec/foundation/crypto'; +import { type Secp256k1Signer, keccak256, recoverAddress } from '@aztec/foundation/crypto'; import { Signature } from '@aztec/foundation/eth-signature'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { type FieldsOf } from '@aztec/foundation/types'; @@ -7,7 +7,9 @@ import { type FieldsOf } from '@aztec/foundation/types'; import { z } from 'zod'; import { Gossipable } from '../p2p/gossipable.js'; +import { getHashedSignaturePayloadEthSignedMessage } from '../p2p/signature_utils.js'; import { TopicType, createTopicString } from '../p2p/topic_type.js'; +import { EpochProofQuoteHasher } from './epoch_proof_quote_hasher.js'; import { EpochProofQuotePayload } from './epoch_proof_quote_payload.js'; export class EpochProofQuote extends Gossipable { @@ -34,6 +36,14 @@ export class EpochProofQuote extends Gossipable { return new Buffer32(keccak256(this.signature.toBuffer())); } + /** + * Return the address of the signer of the signature + */ + getSender(quoteHasher: EpochProofQuoteHasher) { + const hashed = quoteHasher.hash(this.payload); + return recoverAddress(hashed, this.signature); + } + override toBuffer(): Buffer { return serializeToBuffer(...EpochProofQuote.getFields(this)); } @@ -62,13 +72,14 @@ export class EpochProofQuote extends Gossipable { * @param signer the signer * @returns a quote with an accompanying signature */ - static new(digest: Buffer32, payload: EpochProofQuotePayload, signer: Secp256k1Signer): EpochProofQuote { + static new(hasher: EpochProofQuoteHasher, payload: EpochProofQuotePayload, signer: Secp256k1Signer): EpochProofQuote { if (!payload.prover.equals(signer.address)) { throw new Error(`Quote prover does not match signer. Prover [${payload.prover}], Signer [${signer.address}]`); } + + const digest = hasher.hash(payload); const signature = signer.sign(digest); - const quote = new EpochProofQuote(payload, signature); - return quote; + return new EpochProofQuote(payload, signature); } toViemArgs() { diff --git a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote_hasher.ts b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote_hasher.ts new file mode 100644 index 00000000000..5ad212f5f67 --- /dev/null +++ b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote_hasher.ts @@ -0,0 +1,53 @@ +import { Buffer32 } from '@aztec/foundation/buffer'; + +import { hashTypedData } from 'viem'; + +import { EpochProofQuotePayload, EthAddress } from './epoch_proof_quote_payload.js'; + +/** + * A utility class to hash EpochProofQuotePayloads following the EIP-712 standard. + */ +export class EpochProofQuoteHasher { + // Domain information + private readonly domain: { + name: string; + version: string; + chainId: number; + verifyingContract: `0x${string}`; + }; + private readonly types: { + EpochProofQuote: { + name: string; + type: string; + }[]; + }; + + constructor(rollupAddress: EthAddress, chainId: number) { + this.domain = { + name: 'Aztec Rollup', + version: '1', + chainId, + verifyingContract: rollupAddress.toString(), + }; + this.types = { + EpochProofQuote: [ + { name: 'epochToProve', type: 'uint256' }, + { name: 'validUntilSlot', type: 'uint256' }, + { name: 'bondAmount', type: 'uint256' }, + { name: 'prover', type: 'address' }, + { name: 'basisPointFee', type: 'uint32' }, + ], + }; + } + + hash(payload: EpochProofQuotePayload): Buffer32 { + return Buffer32.fromString( + hashTypedData({ + domain: this.domain, + types: this.types, + primaryType: 'EpochProofQuote', + message: payload.toViemArgs(), + }), + ); + } +} diff --git a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote_payload.ts b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote_payload.ts index 60c06e39501..1aa39acedc6 100644 --- a/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote_payload.ts +++ b/yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote_payload.ts @@ -1,20 +1,31 @@ import { EthAddress } from '@aztec/foundation/eth-address'; import { schemas } from '@aztec/foundation/schemas'; import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; +import { hexToBuffer } from '@aztec/foundation/string'; import { type FieldsOf } from '@aztec/foundation/types'; import omit from 'lodash.omit'; import { inspect } from 'util'; +import { encodeAbiParameters, keccak256 } from 'viem'; +import { parseAbiParameters } from 'viem'; import { z } from 'zod'; +import { Signable } from '../p2p/index.js'; + // Required so typescript can properly annotate the exported schema export { type EthAddress }; -export class EpochProofQuotePayload { +export class EpochProofQuotePayload implements Signable { // Cached values private asBuffer: Buffer | undefined; private size: number | undefined; + private typeHash: `0x${string}` = keccak256( + Buffer.from( + 'EpochProofQuote(uint256 epochToProve,uint256 validUntilSlot,uint256 bondAmount,address prover,uint32 basisPointFee)', + ), + ); + constructor( public readonly epochToProve: bigint, public readonly validUntilSlot: bigint, @@ -72,6 +83,19 @@ export class EpochProofQuotePayload { ); } + getPayloadToSign(): Buffer { + const abi = parseAbiParameters('bytes32, uint256, uint256, uint256, address, uint256'); + const encodedData = encodeAbiParameters(abi, [ + this.typeHash, + this.epochToProve, + this.validUntilSlot, + this.bondAmount, + this.prover.toString(), + BigInt(this.basisPointFee), + ]); + return hexToBuffer(encodedData); + } + static from(fields: FieldsOf): EpochProofQuotePayload { return new EpochProofQuotePayload( fields.epochToProve, diff --git a/yarn-project/circuit-types/src/prover_coordination/index.ts b/yarn-project/circuit-types/src/prover_coordination/index.ts index 33b8a68050b..1297be0069e 100644 --- a/yarn-project/circuit-types/src/prover_coordination/index.ts +++ b/yarn-project/circuit-types/src/prover_coordination/index.ts @@ -1,3 +1,4 @@ export * from './epoch_proof_quote.js'; +export * from './epoch_proof_quote_hasher.js'; export * from './epoch_proof_quote_payload.js'; export * from './epoch_proof_claim.js'; diff --git a/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts b/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts index 7e70f26a566..a73d2e29c2f 100644 --- a/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts +++ b/yarn-project/end-to-end/src/prover-coordination/e2e_prover_coordination.test.ts @@ -10,6 +10,7 @@ import { retryUntil, sleep, } from '@aztec/aztec.js'; +import { EpochProofQuoteHasher } from '@aztec/circuit-types'; import { type AztecAddress, EthAddress } from '@aztec/circuits.js'; import { Buffer32 } from '@aztec/foundation/buffer'; import { times } from '@aztec/foundation/collection'; @@ -240,9 +241,11 @@ describe('e2e_prover_coordination', () => { signer.address, basisPointFee ?? randomInt(100), ); - const digest = await rollupContract.read.quoteToDigest([quotePayload.toViemArgs()]); - return EpochProofQuote.new(Buffer32.fromString(digest), quotePayload, signer); + const { rollupAddress } = ctx.deployL1ContractsValues.l1ContractAddresses; + const { l1ChainId } = ctx.aztecNodeConfig; + const hasher = new EpochProofQuoteHasher(rollupAddress, l1ChainId); + return EpochProofQuote.new(hasher, quotePayload, signer); }; it('Sequencer selects best valid proving quote for each block', async () => { diff --git a/yarn-project/p2p/src/client/factory.ts b/yarn-project/p2p/src/client/factory.ts index 926286c19ba..6299c0ccf14 100644 --- a/yarn-project/p2p/src/client/factory.ts +++ b/yarn-project/p2p/src/client/factory.ts @@ -1,5 +1,6 @@ import { type ClientProtocolCircuitVerifier, + EpochProofQuoteHasher, type L2BlockSource, P2PClientType, type WorldStateSynchronizer, @@ -39,6 +40,7 @@ export const createP2PClient = async ( proofVerifier: ClientProtocolCircuitVerifier, worldStateSynchronizer: WorldStateSynchronizer, epochCache: EpochCache, + epochProofQuoteHasher: EpochProofQuoteHasher, telemetry: TelemetryClient = new NoopTelemetryClient(), deps: P2PClientDeps = {}, ) => { @@ -76,6 +78,7 @@ export const createP2PClient = async ( mempools, l2BlockSource, epochCache, + epochProofQuoteHasher, proofVerifier, worldStateSynchronizer, store, diff --git a/yarn-project/p2p/src/mem_pools/epoch_proof_quote_pool/test_utils.ts b/yarn-project/p2p/src/mem_pools/epoch_proof_quote_pool/test_utils.ts index a59de8daa3c..241ec3a64f9 100644 --- a/yarn-project/p2p/src/mem_pools/epoch_proof_quote_pool/test_utils.ts +++ b/yarn-project/p2p/src/mem_pools/epoch_proof_quote_pool/test_utils.ts @@ -1,6 +1,5 @@ -import { EpochProofQuote, EpochProofQuotePayload } from '@aztec/circuit-types'; +import { EpochProofQuote, EpochProofQuoteHasher, EpochProofQuotePayload } from '@aztec/circuit-types'; import { EthAddress } from '@aztec/circuits.js'; -import { Buffer32 } from '@aztec/foundation/buffer'; import { Secp256k1Signer, randomBigInt, randomInt } from '@aztec/foundation/crypto'; export function makeRandomEpochProofQuotePayload(): EpochProofQuotePayload { @@ -17,10 +16,11 @@ export function makeRandomEpochProofQuote(payload?: EpochProofQuotePayload): { quote: EpochProofQuote; signer: Secp256k1Signer; } { + const hasher = new EpochProofQuoteHasher(EthAddress.random(), 1); const signer = Secp256k1Signer.random(); return { - quote: EpochProofQuote.new(Buffer32.random(), payload ?? makeRandomEpochProofQuotePayload(), signer), + quote: EpochProofQuote.new(hasher, payload ?? makeRandomEpochProofQuotePayload(), signer), signer, }; } diff --git a/yarn-project/p2p/src/mocks/index.ts b/yarn-project/p2p/src/mocks/index.ts index ff5996d0908..ca3d8286846 100644 --- a/yarn-project/p2p/src/mocks/index.ts +++ b/yarn-project/p2p/src/mocks/index.ts @@ -1,5 +1,6 @@ import { type ClientProtocolCircuitVerifier, + EpochProofQuoteHasher, type L2BlockSource, type P2PClientType, type Tx, @@ -103,6 +104,7 @@ export async function createTestLibP2PService( l2BlockSource: L2BlockSource, worldStateSynchronizer: WorldStateSynchronizer, epochCache: EpochCache, + epochProofQuoteHasher: EpochProofQuoteHasher, mempools: MemPools, telemetry: TelemetryClient, port: number = 0, @@ -135,6 +137,7 @@ export async function createTestLibP2PService( mempools, l2BlockSource, epochCache, + epochProofQuoteHasher, proofVerifier, worldStateSynchronizer, telemetry, diff --git a/yarn-project/p2p/src/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.test.ts b/yarn-project/p2p/src/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.test.ts index de90b81f9c3..358e9eeb77f 100644 --- a/yarn-project/p2p/src/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.test.ts +++ b/yarn-project/p2p/src/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.test.ts @@ -1,4 +1,9 @@ -import { EpochProofQuote, EpochProofQuotePayload, PeerErrorSeverity } from '@aztec/circuit-types'; +import { + EpochProofQuote, + EpochProofQuoteHasher, + EpochProofQuotePayload, + PeerErrorSeverity, +} from '@aztec/circuit-types'; import { type EpochCache } from '@aztec/epoch-cache'; import { EthAddress } from '@aztec/foundation/eth-address'; import { Signature } from '@aztec/foundation/eth-signature'; @@ -10,10 +15,12 @@ import { EpochProofQuoteValidator } from './epoch_proof_quote_validator.js'; describe('EpochProofQuoteValidator', () => { let epochCache: EpochCache; let validator: EpochProofQuoteValidator; + let epochProofQuoteHasher: EpochProofQuoteHasher; beforeEach(() => { epochCache = mock(); - validator = new EpochProofQuoteValidator(epochCache); + epochProofQuoteHasher = mock(); + validator = new EpochProofQuoteValidator(epochCache, epochProofQuoteHasher); }); const makeEpochProofQuote = (epochToProve: bigint) => { diff --git a/yarn-project/p2p/src/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.ts b/yarn-project/p2p/src/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.ts index 99eec5be73d..864d564ed90 100644 --- a/yarn-project/p2p/src/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.ts +++ b/yarn-project/p2p/src/msg_validators/epoch_proof_quote_validator/epoch_proof_quote_validator.ts @@ -1,11 +1,18 @@ -import { type EpochProofQuote, type P2PValidator, PeerErrorSeverity } from '@aztec/circuit-types'; +import { + type EpochProofQuote, + EpochProofQuoteHasher, + type P2PValidator, + PeerErrorSeverity, +} from '@aztec/circuit-types'; import { type EpochCache } from '@aztec/epoch-cache'; export class EpochProofQuoteValidator implements P2PValidator { private epochCache: EpochCache; + private quoteHasher: EpochProofQuoteHasher; - constructor(epochCache: EpochCache) { + constructor(epochCache: EpochCache, quoteHasher: EpochProofQuoteHasher) { this.epochCache = epochCache; + this.quoteHasher = quoteHasher; } validate(message: EpochProofQuote): Promise { @@ -17,6 +24,12 @@ export class EpochProofQuoteValidator implements P2PValidator { return Promise.resolve(PeerErrorSeverity.HighToleranceError); } + // Check that the message signer is the prover + const signer = message.getSender(this.quoteHasher); + if (!signer.equals(message.payload.prover)) { + return Promise.resolve(PeerErrorSeverity.HighToleranceError); + } + return Promise.resolve(undefined); } } diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index 2056cf80f28..9ca2b0f02cc 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -3,6 +3,7 @@ import { BlockProposal, type ClientProtocolCircuitVerifier, EpochProofQuote, + EpochProofQuoteHasher, type Gossipable, type L2BlockSource, MerkleTreeId, @@ -111,6 +112,7 @@ export class LibP2PService extends WithTracer implement private mempools: MemPools, private l2BlockSource: L2BlockSource, private epochCache: EpochCache, + private proofQuoteHasher: EpochProofQuoteHasher, private proofVerifier: ClientProtocolCircuitVerifier, private worldStateSynchronizer: WorldStateSynchronizer, private telemetry: TelemetryClient, @@ -128,7 +130,7 @@ export class LibP2PService extends WithTracer implement this.attestationValidator = new AttestationValidator(epochCache); this.blockProposalValidator = new BlockProposalValidator(epochCache); - this.epochProofQuoteValidator = new EpochProofQuoteValidator(epochCache); + this.epochProofQuoteValidator = new EpochProofQuoteValidator(epochCache, this.proofQuoteHasher); this.blockReceivedCallback = (block: BlockProposal): Promise => { this.logger.verbose( @@ -239,6 +241,7 @@ export class LibP2PService extends WithTracer implement mempools: MemPools, l2BlockSource: L2BlockSource, epochCache: EpochCache, + proofQuoteHasher: EpochProofQuoteHasher, proofVerifier: ClientProtocolCircuitVerifier, worldStateSynchronizer: WorldStateSynchronizer, store: AztecKVStore, @@ -354,6 +357,7 @@ export class LibP2PService extends WithTracer implement mempools, l2BlockSource, epochCache, + proofQuoteHasher, proofVerifier, worldStateSynchronizer, telemetry, diff --git a/yarn-project/p2p/src/services/reqresp/reqresp.integration.test.ts b/yarn-project/p2p/src/services/reqresp/reqresp.integration.test.ts index d930341268c..6b5e7d9aefa 100644 --- a/yarn-project/p2p/src/services/reqresp/reqresp.integration.test.ts +++ b/yarn-project/p2p/src/services/reqresp/reqresp.integration.test.ts @@ -2,6 +2,7 @@ import { MockL2BlockSource } from '@aztec/archiver/test'; import { type ClientProtocolCircuitVerifier, + EpochProofQuoteHasher, P2PClientType, PeerErrorSeverity, type Tx, @@ -50,6 +51,7 @@ describe('Req Resp p2p client integration', () => { let attestationPool: MockProxy; let epochProofQuotePool: MockProxy; let epochCache: MockProxy; + let epochProofQuoteHasher: MockProxy; let l2BlockSource: MockL2BlockSource; let kvStore: AztecKVStore; let worldState: WorldStateSynchronizer; @@ -61,6 +63,7 @@ describe('Req Resp p2p client integration', () => { attestationPool = mock(); epochProofQuotePool = mock(); epochCache = mock(); + epochProofQuoteHasher = mock(); txPool.getAllTxs.mockImplementation(() => { return [] as Tx[]; @@ -134,6 +137,7 @@ describe('Req Resp p2p client integration', () => { proofVerifier, worldState, epochCache, + epochProofQuoteHasher, undefined, deps, ); diff --git a/yarn-project/prover-node/src/factory.ts b/yarn-project/prover-node/src/factory.ts index c1a5f27026a..b01c96e1f21 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -1,7 +1,8 @@ import { type Archiver, createArchiver } from '@aztec/archiver'; -import { type ProverCoordination, type ProvingJobBroker } from '@aztec/circuit-types'; -import { createEthereumChain } from '@aztec/ethereum'; +import { EpochProofQuoteHasher, type ProverCoordination, type ProvingJobBroker } from '@aztec/circuit-types'; +import { EpochCache } from '@aztec/epoch-cache'; import { Buffer32 } from '@aztec/foundation/buffer'; +import { EthAddress } from '@aztec/foundation/eth-address'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { type DataStoreConfig } from '@aztec/kv-store/config'; import { RollupAbi } from '@aztec/l1-artifacts'; @@ -12,8 +13,6 @@ import { type TelemetryClient } from '@aztec/telemetry-client'; import { NoopTelemetryClient } from '@aztec/telemetry-client/noop'; import { createWorldStateSynchronizer } from '@aztec/world-state'; -import { createPublicClient, getAddress, getContract, http } from 'viem'; - import { createBondManager } from './bond/factory.js'; import { type ProverNodeConfig, type QuoteProviderConfig } from './config.js'; import { ClaimsMonitor } from './monitors/claims-monitor.js'; @@ -51,6 +50,10 @@ export async function createProverNode( // REFACTOR: Move publisher out of sequencer package and into an L1-related package const publisher = deps.publisher ?? new L1Publisher(config, telemetry); + // Dependencies of the p2p client + const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config); + const epochProofQuoteHasher = new EpochProofQuoteHasher(config.l1Contracts.rollupAddress, config.l1ChainId); + // If config.p2pEnabled is true, createProverCoordination will create a p2p client where quotes will be shared and tx's requested // If config.p2pEnabled is false, createProverCoordination request information from the AztecNode const proverCoordination = await createProverCoordination(config, { @@ -58,10 +61,12 @@ export async function createProverNode( worldStateSynchronizer, archiver, telemetry, + epochCache, + epochProofQuoteHasher, }); const quoteProvider = createQuoteProvider(config); - const quoteSigner = createQuoteSigner(config); + const quoteSigner = createQuoteSigner(config, epochProofQuoteHasher); const proverNodeConfig: ProverNodeOptions = { maxPendingJobs: config.proverNodeMaxPendingJobs, @@ -100,13 +105,11 @@ function createQuoteProvider(config: QuoteProviderConfig) { : new SimpleQuoteProvider(config.quoteProviderBasisPointFee, config.quoteProviderBondAmount); } -function createQuoteSigner(config: ProverNodeConfig) { - // REFACTOR: We need a package that just returns an instance of a rollup contract ready to use - const { l1RpcUrl: rpcUrl, l1ChainId: chainId, l1Contracts } = config; - const chain = createEthereumChain(rpcUrl, chainId); - const client = createPublicClient({ chain: chain.chainInfo, transport: http(chain.rpcUrl) }); - const address = getAddress(l1Contracts.rollupAddress.toString()); - const rollupContract = getContract({ address, abi: RollupAbi, client }); - const privateKey = config.publisherPrivateKey; - return QuoteSigner.new(Buffer32.fromString(privateKey), rollupContract); +function createEpochProofQuoteHasher(config: ProverNodeConfig) {} + +function createQuoteSigner(config: ProverNodeConfig, epochProofQuoteHasher: EpochProofQuoteHasher) { + const { publisherPrivateKey } = config; + + const privateKey = Buffer32.fromString(publisherPrivateKey); + return QuoteSigner.new(epochProofQuoteHasher, privateKey); } diff --git a/yarn-project/prover-node/src/prover-coordination/factory.ts b/yarn-project/prover-node/src/prover-coordination/factory.ts index 88731deec2a..9ba0683694c 100644 --- a/yarn-project/prover-node/src/prover-coordination/factory.ts +++ b/yarn-project/prover-node/src/prover-coordination/factory.ts @@ -1,6 +1,7 @@ import { type ArchiveSource, type Archiver } from '@aztec/archiver'; import { BBCircuitVerifier, TestCircuitVerifier } from '@aztec/bb-prover'; import { + EpochProofQuoteHasher, P2PClientType, type ProverCoordination, type WorldStateSynchronizer, @@ -21,6 +22,7 @@ type ProverCoordinationDeps = { archiver?: Archiver | ArchiveSource; telemetry?: TelemetryClient; epochCache?: EpochCache; + epochProofQuoteHasher?: EpochProofQuoteHasher; }; /** @@ -43,7 +45,7 @@ export async function createProverCoordination( if (config.p2pEnabled) { log.info('Using prover coordination via p2p'); - if (!deps.archiver || !deps.worldStateSynchronizer || !deps.telemetry || !deps.epochCache) { + if (!deps.archiver || !deps.worldStateSynchronizer || !deps.telemetry || !deps.epochCache || !deps.epochProofQuoteHasher) { throw new Error('Missing dependencies for p2p prover coordination'); } @@ -55,6 +57,7 @@ export async function createProverCoordination( proofVerifier, deps.worldStateSynchronizer, deps.epochCache, + deps.epochProofQuoteHasher, deps.telemetry, ); await p2pClient.start(); diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index a9aee8551e8..836308aa8a7 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -1,6 +1,7 @@ import { type EpochProofClaim, EpochProofQuote, + EpochProofQuoteHasher, EpochProofQuotePayload, type EpochProverManager, type L1ToL2MessageSource, @@ -320,12 +321,14 @@ describe('prover-node', () => { epochProofQuotePool: new MemoryEpochProofQuotePool(telemetryClient), }; const epochCache = mock(); + const epochProofQuoteHasher = new EpochProofQuoteHasher(EthAddress.random(), 1); const libp2pService = await createTestLibP2PService( P2PClientType.Prover, [bootnodeAddr], l2BlockSource, worldState, epochCache, + epochProofQuoteHasher, mempools, telemetryClient, port, diff --git a/yarn-project/prover-node/src/quote-signer.ts b/yarn-project/prover-node/src/quote-signer.ts index 0c2f4bd9c4e..735d3547fb9 100644 --- a/yarn-project/prover-node/src/quote-signer.ts +++ b/yarn-project/prover-node/src/quote-signer.ts @@ -1,24 +1,15 @@ -import { EpochProofQuote, type EpochProofQuotePayload } from '@aztec/circuit-types'; +import { EpochProofQuote, EpochProofQuoteHasher, type EpochProofQuotePayload } from '@aztec/circuit-types'; import { Buffer32 } from '@aztec/foundation/buffer'; import { Secp256k1Signer } from '@aztec/foundation/crypto'; -import { type RollupAbi } from '@aztec/l1-artifacts'; - -import { type GetContractReturnType, type PublicClient } from 'viem'; export class QuoteSigner { - constructor( - private readonly signer: Secp256k1Signer, - private readonly quoteToDigest: (payload: EpochProofQuotePayload) => Promise, - ) {} + constructor(private readonly hasher: EpochProofQuoteHasher, private readonly signer: Secp256k1Signer) {} - static new(privateKey: Buffer32, rollupContract: GetContractReturnType): QuoteSigner { - const quoteToDigest = (payload: EpochProofQuotePayload) => - rollupContract.read.quoteToDigest([payload.toViemArgs()]).then(Buffer32.fromString); - return new QuoteSigner(new Secp256k1Signer(privateKey), quoteToDigest); + static new(hasher: EpochProofQuoteHasher, privateKey: Buffer32): QuoteSigner { + return new QuoteSigner(hasher, new Secp256k1Signer(privateKey)); } public async sign(payload: EpochProofQuotePayload) { - const digest = await this.quoteToDigest(payload); - return EpochProofQuote.new(digest, payload, this.signer); + return EpochProofQuote.new(this.hasher, payload, this.signer); } }