From a73836fa473dcb650db4264753023f0b0aa5dc83 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:48:06 +0000 Subject: [PATCH 1/6] feat: extend epoch quote validation --- .../aztec-node/src/aztec-node/server.ts | 3 ++ .../epoch_proof_quote.test.ts | 18 ++++++- .../prover_coordination/epoch_proof_quote.ts | 19 +++++-- .../epoch_proof_quote_hasher.ts | 53 +++++++++++++++++++ .../epoch_proof_quote_payload.ts | 26 ++++++++- .../src/prover_coordination/index.ts | 1 + .../e2e_prover_coordination.test.ts | 7 ++- yarn-project/p2p/src/client/factory.ts | 3 ++ .../epoch_proof_quote_pool/test_utils.ts | 6 +-- yarn-project/p2p/src/mocks/index.ts | 3 ++ .../epoch_proof_quote_validator.test.ts | 11 +++- .../epoch_proof_quote_validator.ts | 17 +++++- .../p2p/src/services/libp2p/libp2p_service.ts | 6 ++- .../reqresp/reqresp.integration.test.ts | 4 ++ yarn-project/prover-node/src/factory.ts | 25 ++++----- .../src/prover-coordination/factory.ts | 11 +++- .../prover-node/src/prover-node.test.ts | 5 +- yarn-project/prover-node/src/prover-node.ts | 2 +- yarn-project/prover-node/src/quote-signer.ts | 23 +++----- 19 files changed, 192 insertions(+), 51 deletions(-) create mode 100644 yarn-project/circuit-types/src/prover_coordination/epoch_proof_quote_hasher.ts diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index 83a73c1335ee..bda3d5e13331 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -5,6 +5,7 @@ import { type AztecNode, type ClientProtocolCircuitVerifier, type EpochProofQuote, + EpochProofQuoteHasher, type GetUnencryptedLogsResponse, type InBlock, type L1ToL2MessageSource, @@ -174,6 +175,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 @@ -184,6 +186,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 fe29fcb941d3..07010c68aff8 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 26ed60e0d35f..9df0cc3d2b68 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 000000000000..5ad212f5f67a --- /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 71d9c7ccc8e4..d5d2cb3683b4 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 33b8a68050b7..1297be0069e2 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 7e70f26a566c..a73d2e29c2f4 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 ede72a17819a..ff0ea51e3e8b 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, @@ -38,6 +39,7 @@ export const createP2PClient = async ( proofVerifier: ClientProtocolCircuitVerifier, worldStateSynchronizer: WorldStateSynchronizer, epochCache: EpochCache, + epochProofQuoteHasher: EpochProofQuoteHasher, telemetry: TelemetryClient = getTelemetryClient(), 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 a59de8daa3c0..241ec3a64f9f 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 179288c4f835..ffd5a0ba24f0 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, @@ -100,6 +101,7 @@ export async function createTestLibP2PService( l2BlockSource: L2BlockSource, worldStateSynchronizer: WorldStateSynchronizer, epochCache: EpochCache, + epochProofQuoteHasher: EpochProofQuoteHasher, mempools: MemPools, telemetry: TelemetryClient, port: number = 0, @@ -132,6 +134,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 de90b81f9c30..358e9eeb77f3 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 99eec5be73d4..864d564ed908 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 8c0a1b24b09b..92f5b5bd2490 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, @@ -112,6 +113,7 @@ export class LibP2PService extends WithTracer implement private mempools: MemPools, private l2BlockSource: L2BlockSource, epochCache: EpochCache, + private proofQuoteHasher: EpochProofQuoteHasher, private proofVerifier: ClientProtocolCircuitVerifier, private worldStateSynchronizer: WorldStateSynchronizer, telemetry: TelemetryClient, @@ -140,7 +142,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( @@ -164,6 +166,7 @@ export class LibP2PService extends WithTracer implement mempools: MemPools, l2BlockSource: L2BlockSource, epochCache: EpochCache, + proofQuoteHasher: EpochProofQuoteHasher, proofVerifier: ClientProtocolCircuitVerifier, worldStateSynchronizer: WorldStateSynchronizer, store: AztecKVStore, @@ -263,6 +266,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 70e6c6b51ba5..149203dfa5e2 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 c129933070d1..1dc14bd39ddf 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -1,20 +1,16 @@ import { type Archiver, createArchiver } from '@aztec/archiver'; import { type BlobSinkClientInterface, createBlobSinkClient } from '@aztec/blob-sink/client'; -import { type ProverCoordination, type ProvingJobBroker } from '@aztec/circuit-types'; +import { EpochProofQuoteHasher, type ProverCoordination, type ProvingJobBroker } from '@aztec/circuit-types'; import { EpochCache } from '@aztec/epoch-cache'; -import { createEthereumChain } from '@aztec/ethereum'; import { Buffer32 } from '@aztec/foundation/buffer'; import { type Logger, createLogger } from '@aztec/foundation/log'; import { type DataStoreConfig } from '@aztec/kv-store/config'; -import { RollupAbi } from '@aztec/l1-artifacts'; import { createProverClient } from '@aztec/prover-client'; import { createAndStartProvingBroker } from '@aztec/prover-client/broker'; import { L1Publisher } from '@aztec/sequencer-client'; import { type TelemetryClient, getTelemetryClient } from '@aztec/telemetry-client'; 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'; @@ -54,7 +50,9 @@ 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, blobSinkClient }); + // 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 @@ -63,11 +61,12 @@ export async function createProverNode( worldStateSynchronizer, archiver, epochCache, + epochProofQuoteHasher, telemetry, }); const quoteProvider = createQuoteProvider(config); - const quoteSigner = createQuoteSigner(config); + const quoteSigner = createQuoteSigner(config, epochProofQuoteHasher); const proverNodeConfig: ProverNodeOptions = { maxPendingJobs: config.proverNodeMaxPendingJobs, @@ -109,13 +108,9 @@ 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 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 6a308f0830ee..b38d9f4c01b4 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 { + type EpochProofQuoteHasher, P2PClientType, type ProverCoordination, type WorldStateSynchronizer, @@ -21,6 +22,7 @@ type ProverCoordinationDeps = { archiver?: Archiver | ArchiveSource; telemetry?: TelemetryClient; epochCache?: EpochCache; + epochProofQuoteHasher?: EpochProofQuoteHasher; }; /** @@ -43,7 +45,13 @@ 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 +63,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 ecc182fb6ff5..edc1b27e72a7 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -2,6 +2,7 @@ import { EmptyL1RollupConstants, type EpochProofClaim, EpochProofQuote, + EpochProofQuoteHasher, EpochProofQuotePayload, type EpochProverManager, type L1ToL2MessageSource, @@ -141,7 +142,7 @@ describe('prover-node', () => { quoteProvider.getQuote.mockResolvedValue(partialQuote); // Signer returns an empty signature - quoteSigner.sign.mockImplementation(payload => Promise.resolve(new EpochProofQuote(payload, Signature.empty()))); + quoteSigner.sign.mockImplementation(payload => new EpochProofQuote(payload, Signature.empty())); // We create 3 fake blocks with 1 tx effect each blocks = times(3, i => L2Block.random(i + 20, 1)); @@ -373,12 +374,14 @@ describe('prover-node', () => { epochProofQuotePool: new MemoryEpochProofQuotePool(), }; epochCache = mock(); + const epochProofQuoteHasher = new EpochProofQuoteHasher(EthAddress.random(), 1); const libp2pService = await createTestLibP2PService( P2PClientType.Prover, [bootnodeAddr], l2BlockSource, worldState, epochCache, + epochProofQuoteHasher, mempools, getTelemetryClient(), port, diff --git a/yarn-project/prover-node/src/prover-node.ts b/yarn-project/prover-node/src/prover-node.ts index 80b0f5b19d35..d3293531785a 100644 --- a/yarn-project/prover-node/src/prover-node.ts +++ b/yarn-project/prover-node/src/prover-node.ts @@ -182,7 +182,7 @@ export class ProverNode implements ClaimsMonitorHandler, EpochMonitorHandler, Pr prover: this.publisher.getSenderAddress(), validUntilSlot: partialQuote.validUntilSlot ?? BigInt(Number.MAX_SAFE_INTEGER), // Should we constrain this? }); - const signed = await this.quoteSigner.sign(quote); + const signed = this.quoteSigner.sign(quote); // Send it to the coordinator this.log.info( diff --git a/yarn-project/prover-node/src/quote-signer.ts b/yarn-project/prover-node/src/quote-signer.ts index 0c2f4bd9c4eb..1bb4ce912de7 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 { Buffer32 } from '@aztec/foundation/buffer'; +import { EpochProofQuote, type EpochProofQuoteHasher, type EpochProofQuotePayload } from '@aztec/circuit-types'; +import { type 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); + public sign(payload: EpochProofQuotePayload) { + return EpochProofQuote.new(this.hasher, payload, this.signer); } } From 74e47e84362f12213c7ecb8b676d818b6ec2610f Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Wed, 18 Dec 2024 22:01:45 +0000 Subject: [PATCH 2/6] change: configure rollup version in constructor --- docker-compose.yml | 4 +- l1-contracts/src/core/Rollup.sol | 4 +- l1-contracts/test/fees/FeeRollup.t.sol | 1 + l1-contracts/test/harnesses/Rollup.sol | 1 + l1-contracts/test/harnesses/TestConstants.sol | 1 + .../aztec-node/src/aztec-node/server.ts | 8 +++- yarn-project/aztec/src/sandbox.ts | 5 ++- .../epoch_proof_quote.test.ts | 3 +- .../prover_coordination/epoch_proof_quote.ts | 3 +- .../epoch_proof_quote_hasher.ts | 6 +-- .../epoch_proof_quote_payload.ts | 5 +-- .../composed/integration_l1_publisher.test.ts | 6 +-- .../end-to-end/src/e2e_fees/fees_test.ts | 2 +- yarn-project/end-to-end/src/fixtures/utils.ts | 2 +- .../e2e_prover_coordination.test.ts | 37 +++++++++---------- yarn-project/ethereum/src/config.ts | 8 ++++ .../ethereum/src/deploy_l1_contracts.ts | 2 + yarn-project/foundation/src/config/env_var.ts | 2 +- yarn-project/p2p/src/client/factory.ts | 2 +- .../epoch_proof_quote_pool/test_utils.ts | 2 +- yarn-project/p2p/src/mocks/index.ts | 2 +- .../epoch_proof_quote_validator.test.ts | 2 +- .../epoch_proof_quote_validator.ts | 2 +- .../p2p/src/services/libp2p/libp2p_service.ts | 2 +- .../reqresp/reqresp.integration.test.ts | 2 +- yarn-project/prover-node/src/factory.ts | 6 ++- .../prover-node/src/prover-node.test.ts | 2 +- yarn-project/sequencer-client/src/config.ts | 7 ---- 28 files changed, 72 insertions(+), 57 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index c0928538e49f..bef8e72fe48b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -7,7 +7,7 @@ services: environment: LOG_LEVEL: ${LOG_LEVEL:-info} L1_CHAIN_ID: 31337 - VERSION: 1 + ROLLUP_VERSION: 1 PXE_PROVER_ENABLED: ${PXE_PROVER_ENABLED:-1} PXE_DATA_DIRECTORY: /var/lib/aztec/pxe NODE_NO_WARNINGS: 1 @@ -37,7 +37,7 @@ services: environment: LOG_LEVEL: ${LOG_LEVEL:-info} L1_CHAIN_ID: 31337 - VERSION: 1 + ROLLUP_VERSION: 1 NODE_NO_WARNINGS: 1 PROVER_REAL_PROOFS: ${PROVER_REAL_PROOFS:-1} DATA_DIRECTORY: /var/lib/aztec diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index 7a97711b2cca..9f210b1fbbb0 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -53,6 +53,7 @@ struct Config { uint256 minimumStake; uint256 slashingQuorum; uint256 slashingRoundSize; + string rollupVersion; } /** @@ -62,7 +63,7 @@ struct Config { * not giving a damn about gas costs. * @dev WARNING: This contract is VERY close to the size limit (500B at time of writing). */ -contract Rollup is EIP712("Aztec Rollup", "1"), Ownable, Leonidas, IRollup, ITestRollup { +contract Rollup is EIP712, Ownable, Leonidas, IRollup, ITestRollup { using SlotLib for Slot; using EpochLib for Epoch; using ProposeLib for ProposeArgs; @@ -110,6 +111,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Ownable, Leonidas, IRollup, ITes address _ares, Config memory _config ) + EIP712("Aztec Rollup", _config.rollupVersion) Ownable(_ares) Leonidas( _stakingAsset, diff --git a/l1-contracts/test/fees/FeeRollup.t.sol b/l1-contracts/test/fees/FeeRollup.t.sol index 5cba0a7467b9..630c83b47a55 100644 --- a/l1-contracts/test/fees/FeeRollup.t.sol +++ b/l1-contracts/test/fees/FeeRollup.t.sol @@ -127,6 +127,7 @@ contract FeeRollupTest is FeeModelTestPoints, DecoderBase { bytes32(0), address(this), Config({ + rollupVersion: "1", aztecSlotDuration: SLOT_DURATION, aztecEpochDuration: EPOCH_DURATION, targetCommitteeSize: 48, diff --git a/l1-contracts/test/harnesses/Rollup.sol b/l1-contracts/test/harnesses/Rollup.sol index 27d55a9913e3..fbb6897416fc 100644 --- a/l1-contracts/test/harnesses/Rollup.sol +++ b/l1-contracts/test/harnesses/Rollup.sol @@ -32,6 +32,7 @@ contract Rollup is RealRollup { minimumStake: TestConstants.AZTEC_MINIMUM_STAKE, slashingQuorum: TestConstants.AZTEC_SLASHING_QUORUM, slashingRoundSize: TestConstants.AZTEC_SLASHING_ROUND_SIZE + rollupVersion: TestConstants.ROLLUP_VERSION }) ) {} diff --git a/l1-contracts/test/harnesses/TestConstants.sol b/l1-contracts/test/harnesses/TestConstants.sol index aad8edd6db0d..0cb36346b6c6 100644 --- a/l1-contracts/test/harnesses/TestConstants.sol +++ b/l1-contracts/test/harnesses/TestConstants.sol @@ -12,4 +12,5 @@ library TestConstants { uint256 internal constant AZTEC_MINIMUM_STAKE = 100e18; uint256 internal constant AZTEC_SLASHING_QUORUM = 6; uint256 internal constant AZTEC_SLASHING_ROUND_SIZE = 10; + string internal constant ROLLUP_VERSION = "1"; } diff --git a/yarn-project/aztec-node/src/aztec-node/server.ts b/yarn-project/aztec-node/src/aztec-node/server.ts index bda3d5e13331..f01389753881 100644 --- a/yarn-project/aztec-node/src/aztec-node/server.ts +++ b/yarn-project/aztec-node/src/aztec-node/server.ts @@ -175,7 +175,11 @@ export class AztecNodeService implements AztecNode, Traceable { log.warn(`Aztec node is accepting fake proofs`); } - const epochProofQuoteHasher = new EpochProofQuoteHasher(config.l1Contracts.rollupAddress, config.l1ChainId); + const epochProofQuoteHasher = new EpochProofQuoteHasher( + config.l1Contracts.rollupAddress, + config.rollupVersion, + 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 @@ -225,7 +229,7 @@ export class AztecNodeService implements AztecNode, Traceable { worldStateSynchronizer, sequencer, ethereumChain.chainInfo.id, - config.version, + +config.rollupVersion, new GlobalVariableBuilder(config), proofVerifier, telemetry, diff --git a/yarn-project/aztec/src/sandbox.ts b/yarn-project/aztec/src/sandbox.ts index 1cfb2178e071..15ada97b16cd 100644 --- a/yarn-project/aztec/src/sandbox.ts +++ b/yarn-project/aztec/src/sandbox.ts @@ -152,7 +152,10 @@ export async function createSandbox(config: Partial = {}) { if (config.enableGas) { await setupCanonicalL2FeeJuice( - new SignerlessWallet(pxe, new DefaultMultiCallEntrypoint(aztecNodeConfig.l1ChainId, aztecNodeConfig.version)), + new SignerlessWallet( + pxe, + new DefaultMultiCallEntrypoint(aztecNodeConfig.l1ChainId, aztecNodeConfig.rollupVersion), + ), aztecNodeConfig.l1Contracts.feeJuicePortalAddress, undefined, logger.info, 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 07010c68aff8..a1be03a45c5e 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 @@ -2,7 +2,6 @@ import { EthAddress } from '@aztec/circuits.js'; 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'; @@ -23,7 +22,7 @@ describe('epoch proof quote', () => { validUntilSlot: 100n, }); - hasher = new EpochProofQuoteHasher(EthAddress.random(), 1); + hasher = new EpochProofQuoteHasher(EthAddress.random(), 1, 1); const digest = hasher.hash(payload); const signature = signer.sign(digest); 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 9df0cc3d2b68..8742be0f2c86 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 @@ -7,9 +7,8 @@ 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 { type EpochProofQuoteHasher } from './epoch_proof_quote_hasher.js'; import { EpochProofQuotePayload } from './epoch_proof_quote_payload.js'; export class EpochProofQuote extends Gossipable { 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 index 5ad212f5f67a..eded7389fbdb 100644 --- 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 @@ -2,7 +2,7 @@ import { Buffer32 } from '@aztec/foundation/buffer'; import { hashTypedData } from 'viem'; -import { EpochProofQuotePayload, EthAddress } from './epoch_proof_quote_payload.js'; +import { type EpochProofQuotePayload, type EthAddress } from './epoch_proof_quote_payload.js'; /** * A utility class to hash EpochProofQuotePayloads following the EIP-712 standard. @@ -22,10 +22,10 @@ export class EpochProofQuoteHasher { }[]; }; - constructor(rollupAddress: EthAddress, chainId: number) { + constructor(rollupAddress: EthAddress, version: number, chainId: number) { this.domain = { name: 'Aztec Rollup', - version: '1', + version: version.toString(), chainId, verifyingContract: rollupAddress.toString(), }; 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 d5d2cb3683b4..73b2be78081a 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 @@ -6,11 +6,10 @@ 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 { encodeAbiParameters, keccak256, parseAbiParameters } from 'viem'; import { z } from 'zod'; -import { Signable } from '../p2p/index.js'; +import { type Signable } from '../p2p/index.js'; // Required so typescript can properly annotate the exported schema export { type EthAddress }; diff --git a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index 1334d39aadf0..f43f845a22fc 100644 --- a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts @@ -206,7 +206,7 @@ describe('L1Publisher integration', () => { makeBloatedProcessedTx({ header: prevHeader, chainId: fr(chainId), - version: fr(config.version), + version: fr(config.rollupVersion), vkTreeRoot: getVKTreeRoot(), gasSettings: GasSettings.default({ maxFeesPerGas: baseFee }), protocolContractTreeRoot, @@ -375,7 +375,7 @@ describe('L1Publisher integration', () => { const globalVariables = new GlobalVariables( new Fr(chainId), - new Fr(config.version), + new Fr(config.rollupVersion), new Fr(1 + i), new Fr(slot), new Fr(timestamp), @@ -509,7 +509,7 @@ describe('L1Publisher integration', () => { const timestamp = await rollup.read.getTimestampForSlot([slot]); const globalVariables = new GlobalVariables( new Fr(chainId), - new Fr(config.version), + new Fr(config.rollupVersion), new Fr(1), new Fr(slot), new Fr(timestamp), diff --git a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts index 518f545c8de3..056da274d950 100644 --- a/yarn-project/end-to-end/src/e2e_fees/fees_test.ts +++ b/yarn-project/end-to-end/src/e2e_fees/fees_test.ts @@ -177,7 +177,7 @@ export class FeesTest { await setupCanonicalFeeJuice( new SignerlessWallet( context.pxe, - new DefaultMultiCallEntrypoint(context.aztecNodeConfig.l1ChainId, context.aztecNodeConfig.version), + new DefaultMultiCallEntrypoint(context.aztecNodeConfig.l1ChainId, context.aztecNodeConfig.rollupVersion), ), ); }, diff --git a/yarn-project/end-to-end/src/fixtures/utils.ts b/yarn-project/end-to-end/src/fixtures/utils.ts index aa85ef06c524..25f88e0a4f23 100644 --- a/yarn-project/end-to-end/src/fixtures/utils.ts +++ b/yarn-project/end-to-end/src/fixtures/utils.ts @@ -499,7 +499,7 @@ export async function setup( if (!config.skipProtocolContracts) { logger.verbose('Setting up Fee Juice...'); await setupCanonicalFeeJuice( - new SignerlessWallet(pxe, new DefaultMultiCallEntrypoint(config.l1ChainId, config.version)), + new SignerlessWallet(pxe, new DefaultMultiCallEntrypoint(config.l1ChainId, config.rollupVersion)), ); } 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 a73d2e29c2f4..4e7ae97d36b9 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'; +// eslint-disable-next-line no-restricted-imports import { EpochProofQuoteHasher } from '@aztec/circuit-types'; import { type AztecAddress, EthAddress } from '@aztec/circuits.js'; import { Buffer32 } from '@aztec/foundation/buffer'; @@ -220,7 +221,7 @@ describe('e2e_prover_coordination', () => { await logState(); }; - const makeEpochProofQuote = async ({ + const makeEpochProofQuote = ({ epochToProve, validUntilSlot, bondAmount, @@ -243,8 +244,8 @@ describe('e2e_prover_coordination', () => { ); const { rollupAddress } = ctx.deployL1ContractsValues.l1ContractAddresses; - const { l1ChainId } = ctx.aztecNodeConfig; - const hasher = new EpochProofQuoteHasher(rollupAddress, l1ChainId); + const { l1ChainId, rollupVersion } = ctx.aztecNodeConfig; + const hasher = new EpochProofQuoteHasher(rollupAddress, rollupVersion, l1ChainId); return EpochProofQuote.new(hasher, quotePayload, signer); }; @@ -256,7 +257,7 @@ describe('e2e_prover_coordination', () => { await performEscrow(10000000n); // Here we are creating a proof quote for epoch 0 - const quoteForEpoch0 = await makeEpochProofQuote({ + const quoteForEpoch0 = makeEpochProofQuote({ epochToProve: 0n, validUntilSlot: BigInt(ctx.aztecNodeConfig.aztecEpochDuration + 10), bondAmount: 10000n, @@ -301,19 +302,17 @@ describe('e2e_prover_coordination', () => { const currentSlot = await getSlot(); // Now create a number of quotes, some valid some invalid for epoch 1, the lowest priced valid quote should be chosen - const validQuotes = await Promise.all( - times(3, (i: number) => - makeEpochProofQuote({ - epochToProve: 1n, - validUntilSlot: currentSlot + 2n, - bondAmount: 10000n, - basisPointFee: 10 + i, - signer: proverSigner, - }), - ), + const validQuotes = times(3, (i: number) => + makeEpochProofQuote({ + epochToProve: 1n, + validUntilSlot: currentSlot + 2n, + bondAmount: 10000n, + basisPointFee: 10 + i, + signer: proverSigner, + }), ); - const proofQuoteInvalidSlot = await makeEpochProofQuote({ + const proofQuoteInvalidSlot = makeEpochProofQuote({ epochToProve: 1n, validUntilSlot: 3n, bondAmount: 10000n, @@ -321,7 +320,7 @@ describe('e2e_prover_coordination', () => { signer: proverSigner, }); - const proofQuoteInvalidEpoch = await makeEpochProofQuote({ + const proofQuoteInvalidEpoch = makeEpochProofQuote({ epochToProve: 4n, validUntilSlot: currentSlot + 4n, bondAmount: 10000n, @@ -329,7 +328,7 @@ describe('e2e_prover_coordination', () => { signer: proverSigner, }); - const proofQuoteInsufficientBond = await makeEpochProofQuote({ + const proofQuoteInsufficientBond = makeEpochProofQuote({ epochToProve: 1n, validUntilSlot: currentSlot + 4n, bondAmount: 0n, @@ -374,7 +373,7 @@ describe('e2e_prover_coordination', () => { let currentPending = tips.pending; let currentProven = tips.proven; // Here we are creating a proof quote for epoch 0 - const quoteForEpoch0 = await makeEpochProofQuote({ + const quoteForEpoch0 = makeEpochProofQuote({ epochToProve: 0n, validUntilSlot: BigInt(ctx.aztecNodeConfig.aztecEpochDuration + 10), bondAmount: 10000n, @@ -461,7 +460,7 @@ describe('e2e_prover_coordination', () => { await expectTips({ pending: currentPending, proven: currentProven }); // Submit proof claim for the new epoch - const quoteForEpoch4 = await makeEpochProofQuote({ + const quoteForEpoch4 = makeEpochProofQuote({ epochToProve: 4n, validUntilSlot: BigInt(ctx.aztecNodeConfig.aztecEpochDuration * 4 + 10), bondAmount: 10000n, diff --git a/yarn-project/ethereum/src/config.ts b/yarn-project/ethereum/src/config.ts index 2a2bff7e4fa3..af0dc1b2e00c 100644 --- a/yarn-project/ethereum/src/config.ts +++ b/yarn-project/ethereum/src/config.ts @@ -26,6 +26,8 @@ export type L1ContractsConfig = { governanceProposerQuorum: number; /** Governance proposing round size */ governanceProposerRoundSize: number; + /** The version of the rollup. */ + rollupVersion: number; }; export const DefaultL1ContractsConfig = { @@ -39,6 +41,7 @@ export const DefaultL1ContractsConfig = { slashingRoundSize: 10, governanceProposerQuorum: 6, governanceProposerRoundSize: 10, + rollupVersion: 1, } satisfies L1ContractsConfig; export const l1ContractsConfigMappings: ConfigMappingsType = { @@ -91,6 +94,11 @@ export const l1ContractsConfigMappings: ConfigMappingsType = env: 'AZTEC_GOVERNANCE_PROPOSER_ROUND_SIZE', description: 'The governance proposing round size', ...numberConfigHelper(DefaultL1ContractsConfig.governanceProposerRoundSize), + rollupVersion: { + env: 'ROLLUP_VERSION', + description: 'The version of the rollup.', + defaultValue: DefaultL1ContractsConfig.rollupVersion, + ...numberConfigHelper(DefaultL1ContractsConfig.rollupVersion), }, }; diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index 09e49baf2627..fae6e1af647b 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -336,6 +336,8 @@ export const deployL1Contracts = async ( minimumStake: args.minimumStake, slashingQuorum: args.slashingQuorum, slashingRoundSize: args.slashingRoundSize, + rollupVersion: args.rollupVersion, + minimumStake: MINIMUM_STAKE, }; const rollupArgs = [ feeJuicePortalAddress.toString(), diff --git a/yarn-project/foundation/src/config/env_var.ts b/yarn-project/foundation/src/config/env_var.ts index 2fef4f1dcace..175d6d639fda 100644 --- a/yarn-project/foundation/src/config/env_var.ts +++ b/yarn-project/foundation/src/config/env_var.ts @@ -166,7 +166,7 @@ export type EnvVar = | 'VALIDATOR_DISABLED' | 'VALIDATOR_PRIVATE_KEY' | 'VALIDATOR_REEXECUTE' - | 'VERSION' + | 'ROLLUP_VERSION' | 'WS_BLOCK_CHECK_INTERVAL_MS' | 'WS_PROVEN_BLOCKS_ONLY' | 'WS_BLOCK_REQUEST_BATCH_SIZE' diff --git a/yarn-project/p2p/src/client/factory.ts b/yarn-project/p2p/src/client/factory.ts index ff0ea51e3e8b..25151bc9ab7e 100644 --- a/yarn-project/p2p/src/client/factory.ts +++ b/yarn-project/p2p/src/client/factory.ts @@ -1,6 +1,6 @@ import { type ClientProtocolCircuitVerifier, - EpochProofQuoteHasher, + type EpochProofQuoteHasher, type L2BlockSource, P2PClientType, type WorldStateSynchronizer, 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 241ec3a64f9f..8a7ac38ffe60 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 @@ -16,7 +16,7 @@ export function makeRandomEpochProofQuote(payload?: EpochProofQuotePayload): { quote: EpochProofQuote; signer: Secp256k1Signer; } { - const hasher = new EpochProofQuoteHasher(EthAddress.random(), 1); + const hasher = new EpochProofQuoteHasher(EthAddress.random(), 1, 1); const signer = Secp256k1Signer.random(); return { diff --git a/yarn-project/p2p/src/mocks/index.ts b/yarn-project/p2p/src/mocks/index.ts index ffd5a0ba24f0..1b9ed60781fd 100644 --- a/yarn-project/p2p/src/mocks/index.ts +++ b/yarn-project/p2p/src/mocks/index.ts @@ -1,6 +1,6 @@ import { type ClientProtocolCircuitVerifier, - EpochProofQuoteHasher, + type EpochProofQuoteHasher, type L2BlockSource, type P2PClientType, type Tx, 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 358e9eeb77f3..3b82b37f453c 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,6 +1,6 @@ import { EpochProofQuote, - EpochProofQuoteHasher, + type EpochProofQuoteHasher, EpochProofQuotePayload, PeerErrorSeverity, } from '@aztec/circuit-types'; 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 864d564ed908..21f9874826db 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,6 +1,6 @@ import { type EpochProofQuote, - EpochProofQuoteHasher, + type EpochProofQuoteHasher, type P2PValidator, PeerErrorSeverity, } from '@aztec/circuit-types'; diff --git a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts index 92f5b5bd2490..08bb91e52261 100644 --- a/yarn-project/p2p/src/services/libp2p/libp2p_service.ts +++ b/yarn-project/p2p/src/services/libp2p/libp2p_service.ts @@ -3,7 +3,7 @@ import { BlockProposal, type ClientProtocolCircuitVerifier, EpochProofQuote, - EpochProofQuoteHasher, + type EpochProofQuoteHasher, type Gossipable, type L2BlockSource, MerkleTreeId, 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 149203dfa5e2..5a531e4e51c2 100644 --- a/yarn-project/p2p/src/services/reqresp/reqresp.integration.test.ts +++ b/yarn-project/p2p/src/services/reqresp/reqresp.integration.test.ts @@ -2,7 +2,7 @@ import { MockL2BlockSource } from '@aztec/archiver/test'; import { type ClientProtocolCircuitVerifier, - EpochProofQuoteHasher, + type EpochProofQuoteHasher, P2PClientType, PeerErrorSeverity, type Tx, diff --git a/yarn-project/prover-node/src/factory.ts b/yarn-project/prover-node/src/factory.ts index 1dc14bd39ddf..6f4225db47e1 100644 --- a/yarn-project/prover-node/src/factory.ts +++ b/yarn-project/prover-node/src/factory.ts @@ -52,7 +52,11 @@ export async function createProverNode( // Dependencies of the p2p client const epochCache = await EpochCache.create(config.l1Contracts.rollupAddress, config); - const epochProofQuoteHasher = new EpochProofQuoteHasher(config.l1Contracts.rollupAddress, config.l1ChainId); + const epochProofQuoteHasher = new EpochProofQuoteHasher( + config.l1Contracts.rollupAddress, + config.rollupVersion, + 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 diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index edc1b27e72a7..04482dc0412f 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -374,7 +374,7 @@ describe('prover-node', () => { epochProofQuotePool: new MemoryEpochProofQuotePool(), }; epochCache = mock(); - const epochProofQuoteHasher = new EpochProofQuoteHasher(EthAddress.random(), 1); + const epochProofQuoteHasher = new EpochProofQuoteHasher(EthAddress.random(), /*version=*/ 1, /*chainId=*/ 1); const libp2pService = await createTestLibP2PService( P2PClientType.Prover, [bootnodeAddr], diff --git a/yarn-project/sequencer-client/src/config.ts b/yarn-project/sequencer-client/src/config.ts index 9db77ac13b35..a1b6a7ccdb60 100644 --- a/yarn-project/sequencer-client/src/config.ts +++ b/yarn-project/sequencer-client/src/config.ts @@ -29,8 +29,6 @@ export { SequencerConfig }; type ChainConfig = { /** The chain id of the ethereum host. */ l1ChainId: number; - /** The version of the rollup. */ - version: number; }; /** @@ -125,11 +123,6 @@ export const sequencerConfigMappings: ConfigMappingsType = { export const chainConfigMappings: ConfigMappingsType = { l1ChainId: l1ReaderConfigMappings.l1ChainId, - version: { - env: 'VERSION', - description: 'The version of the rollup.', - ...numberConfigHelper(1), - }, }; export const sequencerClientConfigMappings: ConfigMappingsType = { From 42b260ec2e79d45f4a7a2e2b74d2cde7b3fcaa65 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Sat, 18 Jan 2025 01:36:21 +0000 Subject: [PATCH 3/6] fix: merge --- yarn-project/ethereum/src/config.ts | 1 + yarn-project/ethereum/src/deploy_l1_contracts.ts | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/yarn-project/ethereum/src/config.ts b/yarn-project/ethereum/src/config.ts index af0dc1b2e00c..c8e740ee5b76 100644 --- a/yarn-project/ethereum/src/config.ts +++ b/yarn-project/ethereum/src/config.ts @@ -94,6 +94,7 @@ export const l1ContractsConfigMappings: ConfigMappingsType = env: 'AZTEC_GOVERNANCE_PROPOSER_ROUND_SIZE', description: 'The governance proposing round size', ...numberConfigHelper(DefaultL1ContractsConfig.governanceProposerRoundSize), + }, rollupVersion: { env: 'ROLLUP_VERSION', description: 'The version of the rollup.', diff --git a/yarn-project/ethereum/src/deploy_l1_contracts.ts b/yarn-project/ethereum/src/deploy_l1_contracts.ts index fae6e1af647b..fc85e5e0c23a 100644 --- a/yarn-project/ethereum/src/deploy_l1_contracts.ts +++ b/yarn-project/ethereum/src/deploy_l1_contracts.ts @@ -337,7 +337,6 @@ export const deployL1Contracts = async ( slashingQuorum: args.slashingQuorum, slashingRoundSize: args.slashingRoundSize, rollupVersion: args.rollupVersion, - minimumStake: MINIMUM_STAKE, }; const rollupArgs = [ feeJuicePortalAddress.toString(), From eb82767f37a805ab3c9dd6ec89b6754393af11ae Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Sat, 18 Jan 2025 01:45:22 +0000 Subject: [PATCH 4/6] fix: dirty merge --- l1-contracts/test/governance/scenario/slashing/Slashing.t.sol | 3 ++- l1-contracts/test/harnesses/Rollup.sol | 2 +- l1-contracts/test/sparta/Sparta.t.sol | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/l1-contracts/test/governance/scenario/slashing/Slashing.t.sol b/l1-contracts/test/governance/scenario/slashing/Slashing.t.sol index e2a40cec054a..472b8b588a45 100644 --- a/l1-contracts/test/governance/scenario/slashing/Slashing.t.sol +++ b/l1-contracts/test/governance/scenario/slashing/Slashing.t.sol @@ -72,7 +72,8 @@ contract SlashingScenario is TestBase { aztecEpochProofClaimWindowInL2Slots: TestConstants.AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS, minimumStake: TestConstants.AZTEC_MINIMUM_STAKE, slashingQuorum: TestConstants.AZTEC_SLASHING_QUORUM, - slashingRoundSize: TestConstants.AZTEC_SLASHING_ROUND_SIZE + slashingRoundSize: TestConstants.AZTEC_SLASHING_ROUND_SIZE, + rollupVersion: TestConstants.ROLLUP_VERSION }) }); slasher = rollup.SLASHER(); diff --git a/l1-contracts/test/harnesses/Rollup.sol b/l1-contracts/test/harnesses/Rollup.sol index fbb6897416fc..a66d5196d6e9 100644 --- a/l1-contracts/test/harnesses/Rollup.sol +++ b/l1-contracts/test/harnesses/Rollup.sol @@ -31,7 +31,7 @@ contract Rollup is RealRollup { aztecEpochProofClaimWindowInL2Slots: TestConstants.AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS, minimumStake: TestConstants.AZTEC_MINIMUM_STAKE, slashingQuorum: TestConstants.AZTEC_SLASHING_QUORUM, - slashingRoundSize: TestConstants.AZTEC_SLASHING_ROUND_SIZE + slashingRoundSize: TestConstants.AZTEC_SLASHING_ROUND_SIZE, rollupVersion: TestConstants.ROLLUP_VERSION }) ) diff --git a/l1-contracts/test/sparta/Sparta.t.sol b/l1-contracts/test/sparta/Sparta.t.sol index eccf797ee166..ba6cd46bd5b8 100644 --- a/l1-contracts/test/sparta/Sparta.t.sol +++ b/l1-contracts/test/sparta/Sparta.t.sol @@ -124,7 +124,8 @@ contract SpartaTest is DecoderBase { aztecEpochProofClaimWindowInL2Slots: TestConstants.AZTEC_EPOCH_PROOF_CLAIM_WINDOW_IN_L2_SLOTS, minimumStake: TestConstants.AZTEC_MINIMUM_STAKE, slashingQuorum: TestConstants.AZTEC_SLASHING_QUORUM, - slashingRoundSize: TestConstants.AZTEC_SLASHING_ROUND_SIZE + slashingRoundSize: TestConstants.AZTEC_SLASHING_ROUND_SIZE, + rollupVersion: TestConstants.ROLLUP_VERSION }) }); slasher = rollup.SLASHER(); From ea9487708fa20fe7c93484ed0c36d4b1bc091bb9 Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Sun, 19 Jan 2025 20:50:33 +0000 Subject: [PATCH 5/6] fix: rebase tests + fmt --- .../epoch_proof_quote_validator.test.ts | 20 ++++++++++--------- .../prover-node/src/prover-node.test.ts | 13 +++++++----- 2 files changed, 19 insertions(+), 14 deletions(-) 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 3b82b37f453c..8d58a00ecf1a 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,12 +1,12 @@ import { EpochProofQuote, - type EpochProofQuoteHasher, + EpochProofQuoteHasher, EpochProofQuotePayload, PeerErrorSeverity, } from '@aztec/circuit-types'; import { type EpochCache } from '@aztec/epoch-cache'; +import { Secp256k1Signer } from '@aztec/foundation/crypto'; import { EthAddress } from '@aztec/foundation/eth-address'; -import { Signature } from '@aztec/foundation/eth-signature'; import { mock } from 'jest-mock-extended'; @@ -16,27 +16,29 @@ describe('EpochProofQuoteValidator', () => { let epochCache: EpochCache; let validator: EpochProofQuoteValidator; let epochProofQuoteHasher: EpochProofQuoteHasher; + let signer: Secp256k1Signer; beforeEach(() => { epochCache = mock(); - epochProofQuoteHasher = mock(); + epochProofQuoteHasher = new EpochProofQuoteHasher(EthAddress.random(), 1, 1); + signer = Secp256k1Signer.random(); validator = new EpochProofQuoteValidator(epochCache, epochProofQuoteHasher); }); - const makeEpochProofQuote = (epochToProve: bigint) => { + const makeEpochProofQuote = (epochToProve: bigint, signer: Secp256k1Signer) => { const payload = EpochProofQuotePayload.from({ basisPointFee: 5000, bondAmount: 1000000000000000000n, epochToProve, - prover: EthAddress.random(), + prover: signer.address, validUntilSlot: 100n, }); - return new EpochProofQuote(payload, Signature.random()); + return EpochProofQuote.new(epochProofQuoteHasher, payload, signer); }; it('returns high tolerance error if epoch to prove is not current or previous epoch', async () => { // Create an epoch proof quote for epoch 5 - const mockQuote = makeEpochProofQuote(5n); + const mockQuote = makeEpochProofQuote(5n, signer); // Mock epoch cache to return different epoch (epochCache.getEpochAndSlotNow as jest.Mock).mockReturnValue({ @@ -49,7 +51,7 @@ describe('EpochProofQuoteValidator', () => { it('returns no error if epoch to prove is current epoch', async () => { // Create an epoch proof quote for current epoch - const mockQuote = makeEpochProofQuote(7n); + const mockQuote = makeEpochProofQuote(7n, signer); // Mock epoch cache to return matching epoch (epochCache.getEpochAndSlotNow as jest.Mock).mockReturnValue({ @@ -62,7 +64,7 @@ describe('EpochProofQuoteValidator', () => { it('returns no error if epoch to prove is previous epoch', async () => { // Create an epoch proof quote for previous epoch - const mockQuote = makeEpochProofQuote(6n); + const mockQuote = makeEpochProofQuote(6n, signer); // Mock epoch cache to return current epoch (epochCache.getEpochAndSlotNow as jest.Mock).mockReturnValue({ diff --git a/yarn-project/prover-node/src/prover-node.test.ts b/yarn-project/prover-node/src/prover-node.test.ts index 04482dc0412f..ac0a2e28f050 100644 --- a/yarn-project/prover-node/src/prover-node.test.ts +++ b/yarn-project/prover-node/src/prover-node.test.ts @@ -19,7 +19,7 @@ import { import { type ContractDataSource, EthAddress } from '@aztec/circuits.js'; import { type EpochCache } from '@aztec/epoch-cache'; import { times } from '@aztec/foundation/collection'; -import { Signature } from '@aztec/foundation/eth-signature'; +import { Secp256k1Signer } from '@aztec/foundation/crypto'; import { makeBackoff, retry } from '@aztec/foundation/retry'; import { sleep } from '@aztec/foundation/sleep'; import { openTmpStore } from '@aztec/kv-store/lmdb'; @@ -52,6 +52,7 @@ describe('prover-node', () => { let mockCoordination: MockProxy; let quoteProvider: MockProxy; let quoteSigner: MockProxy; + let epochProofQuoteHasher: EpochProofQuoteHasher; let bondManager: MockProxy; let config: ProverNodeOptions; @@ -117,6 +118,9 @@ describe('prover-node', () => { quoteSigner = mock(); bondManager = mock(); + epochProofQuoteHasher = new EpochProofQuoteHasher(EthAddress.random(), /*version=*/ 1, /*chainId=*/ 1); + const signer = Secp256k1Signer.random(); + config = { maxPendingJobs: 3, pollingIntervalMs: 10, @@ -134,15 +138,15 @@ describe('prover-node', () => { }); // Publisher returns its sender address - address = EthAddress.random(); + address = signer.address; publisher.getSenderAddress.mockReturnValue(address); // Quote provider returns a mock partialQuote = { basisPointFee: 100, bondAmount: 0n, validUntilSlot: 30n }; quoteProvider.getQuote.mockResolvedValue(partialQuote); - // Signer returns an empty signature - quoteSigner.sign.mockImplementation(payload => new EpochProofQuote(payload, Signature.empty())); + // Signer return the signature of quoteSigner + quoteSigner.sign.mockImplementation(payload => EpochProofQuote.new(epochProofQuoteHasher, payload, signer)); // We create 3 fake blocks with 1 tx effect each blocks = times(3, i => L2Block.random(i + 20, 1)); @@ -374,7 +378,6 @@ describe('prover-node', () => { epochProofQuotePool: new MemoryEpochProofQuotePool(), }; epochCache = mock(); - const epochProofQuoteHasher = new EpochProofQuoteHasher(EthAddress.random(), /*version=*/ 1, /*chainId=*/ 1); const libp2pService = await createTestLibP2PService( P2PClientType.Prover, [bootnodeAddr], From 6b5aa6f74d645d644cc25c77a0e09922dbe9818a Mon Sep 17 00:00:00 2001 From: Maddiaa0 <47148561+Maddiaa0@users.noreply.github.com> Date: Mon, 20 Jan 2025 15:33:39 +0000 Subject: [PATCH 6/6] fix: typesafe --- .../epoch_proof_quote_hasher.ts | 87 +++++++++++++++---- .../epoch_proof_quote_payload.ts | 13 +-- yarn-project/ethereum/src/contracts/rollup.ts | 2 + 3 files changed, 77 insertions(+), 25 deletions(-) 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 index eded7389fbdb..f14b90b72fbf 100644 --- 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 @@ -1,9 +1,58 @@ +import { RollupAbi } from '@aztec/ethereum/contracts'; import { Buffer32 } from '@aztec/foundation/buffer'; -import { hashTypedData } from 'viem'; +import { getAbiItem, hashTypedData, keccak256, parseAbiParameters } from 'viem'; import { type EpochProofQuotePayload, type EthAddress } from './epoch_proof_quote_payload.js'; +type EpochProofQuoteTypeHash = { + EpochProofQuote: { + name: string; + type: string; + }[]; +}; + +/** + * Constructs the type hash for the EpochProofQuote struct. + * + * Given an abi type like so { + * EpochProofQuote: { + * name: 'epochToProve', + * type: 'uint256', + * } + * } + * + * it would construct the hash of the following string: + * E.g: EpochProofQuote(uint256 epochToProve) + * + * @param types - The types of the EpochProofQuote struct + */ +function constructTypeHash(types: EpochProofQuoteTypeHash): `0x${string}` { + const paramsString = types.EpochProofQuote.map(({ name, type }) => `${type} ${name}`).join(','); + const typeString = `EpochProofQuote(${paramsString})`; + return keccak256(Buffer.from(typeString)); +} + +/** + * Constructs the abi for the quoteToDigest function. + * + * Given an abi type like so { + * EpochProofQuote: { + * name: 'epochToProve', + * type: 'uint256', + * } + * } + * + * it would construct the abi of the following string: + * [name (EpochProofQuote), ...params] + * E.g: bytes32, uint256 epochToProve + * + * @param types - The types of the EpochProofQuote struct + */ +function constructHasherAbi(types: EpochProofQuoteTypeHash) { + return parseAbiParameters(`bytes32, ${types.EpochProofQuote.map(({ name, type }) => `${type} ${name}`).join(',')}`); +} + /** * A utility class to hash EpochProofQuotePayloads following the EIP-712 standard. */ @@ -15,13 +64,28 @@ export class EpochProofQuoteHasher { chainId: number; verifyingContract: `0x${string}`; }; - private readonly types: { - EpochProofQuote: { - name: string; - type: string; - }[]; + + /** + * Reads the abi of the quoteToDigest function from the rollup contract and extracts the types + * of the EpochProofQuote struct. + * function quoteToDigest(EpochProofQuote quote) + * + * This avoids re-declaring the types and values here that could go out of sync with + * the rollup contract + */ + static readonly types: EpochProofQuoteTypeHash = { + EpochProofQuote: getAbiItem({ abi: RollupAbi, name: 'quoteToDigest' }).inputs[0].components.map(component => ({ + name: component.name, + type: component.type, + })), }; + // Type hash for the types defined in the types object + static readonly EPOCH_PROOF_QUOTE_TYPE_HASH = constructTypeHash(EpochProofQuoteHasher.types); + + // Viem abi object for the types defined in the types object + static readonly HASHER_ABI = constructHasherAbi(EpochProofQuoteHasher.types); + constructor(rollupAddress: EthAddress, version: number, chainId: number) { this.domain = { name: 'Aztec Rollup', @@ -29,22 +93,13 @@ export class EpochProofQuoteHasher { 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, + types: EpochProofQuoteHasher.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 73b2be78081a..0f84e23e3039 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 @@ -6,10 +6,11 @@ import { type FieldsOf } from '@aztec/foundation/types'; import omit from 'lodash.omit'; import { inspect } from 'util'; -import { encodeAbiParameters, keccak256, parseAbiParameters } from 'viem'; +import { encodeAbiParameters } from 'viem'; import { z } from 'zod'; import { type Signable } from '../p2p/index.js'; +import { EpochProofQuoteHasher } from './epoch_proof_quote_hasher.js'; // Required so typescript can properly annotate the exported schema export { type EthAddress }; @@ -19,12 +20,6 @@ export class EpochProofQuotePayload implements Signable { 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, @@ -83,9 +78,9 @@ export class EpochProofQuotePayload implements Signable { } getPayloadToSign(): Buffer { - const abi = parseAbiParameters('bytes32, uint256, uint256, uint256, address, uint256'); + const abi = EpochProofQuoteHasher.HASHER_ABI; const encodedData = encodeAbiParameters(abi, [ - this.typeHash, + EpochProofQuoteHasher.EPOCH_PROOF_QUOTE_TYPE_HASH, this.epochToProve, this.validUntilSlot, this.bondAmount, diff --git a/yarn-project/ethereum/src/contracts/rollup.ts b/yarn-project/ethereum/src/contracts/rollup.ts index cbaf9d689e09..1900e1baaca8 100644 --- a/yarn-project/ethereum/src/contracts/rollup.ts +++ b/yarn-project/ethereum/src/contracts/rollup.ts @@ -17,6 +17,8 @@ import { type DeployL1Contracts } from '../deploy_l1_contracts.js'; import { createEthereumChain } from '../ethereum_chain.js'; import { type L1ReaderConfig } from '../l1_reader.js'; +export { RollupAbi } from '@aztec/l1-artifacts'; + export class RollupContract { private readonly rollup: GetContractReturnType>;