diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index d6f8b2c8577..ac9a5cd3eaa 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -468,7 +468,8 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup { // Decode and validate header HeaderLib.Header memory header = HeaderLib.decode(_header); - bytes32 digest = keccak256(abi.encode(_archive, _txHashes)); + uint8 domainSeperator = uint8(SignatureLib.SignatureDomainSeperator.blockAttestation); + bytes32 digest = keccak256(abi.encode(domainSeperator, _archive, _txHashes)); setupEpoch(); _validateHeader({ _header: header, diff --git a/l1-contracts/src/core/libraries/crypto/SignatureLib.sol b/l1-contracts/src/core/libraries/crypto/SignatureLib.sol index bcae500df8c..29e37357bc8 100644 --- a/l1-contracts/src/core/libraries/crypto/SignatureLib.sol +++ b/l1-contracts/src/core/libraries/crypto/SignatureLib.sol @@ -12,6 +12,14 @@ library SignatureLib { bytes32 s; } + /** + * @notice The domain seperator for the signatures + */ + enum SignatureDomainSeperator { + blockProposal, + blockAttestation + } + /** * @notice Verified a signature, throws if the signature is invalid or empty * diff --git a/l1-contracts/test/sparta/Sparta.t.sol b/l1-contracts/test/sparta/Sparta.t.sol index e892c5e54a5..caaac46645f 100644 --- a/l1-contracts/test/sparta/Sparta.t.sol +++ b/l1-contracts/test/sparta/Sparta.t.sol @@ -189,7 +189,8 @@ contract SpartaTest is DecoderBase { SignatureLib.Signature[] memory signatures = new SignatureLib.Signature[](_signatureCount); - bytes32 digest = keccak256(abi.encode(archive, txHashes)); + uint8 domainSeperator = uint8(SignatureLib.SignatureDomainSeperator.blockAttestation); + bytes32 digest = keccak256(abi.encode(domainSeperator, archive, txHashes)); for (uint256 i = 0; i < _signatureCount; i++) { signatures[i] = createSignature(validators[i], digest); } diff --git a/yarn-project/circuit-types/src/p2p/block_attestation.ts b/yarn-project/circuit-types/src/p2p/block_attestation.ts index a847452f8b9..04ccfdf4d52 100644 --- a/yarn-project/circuit-types/src/p2p/block_attestation.ts +++ b/yarn-project/circuit-types/src/p2p/block_attestation.ts @@ -7,7 +7,7 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { ConsensusPayload } from './consensus_payload.js'; import { Gossipable } from './gossipable.js'; -import { getHashedSignaturePayloadEthSignedMessage } from './signature_utils.js'; +import { SignatureDomainSeperator, getHashedSignaturePayloadEthSignedMessage } from './signature_utils.js'; import { TopicType, createTopicString } from './topic_type.js'; export class BlockAttestationHash extends Buffer32 { @@ -53,7 +53,7 @@ export class BlockAttestation extends Gossipable { getSender() { if (!this.sender) { // Recover the sender from the attestation - const hashed = getHashedSignaturePayloadEthSignedMessage(this.payload); + const hashed = getHashedSignaturePayloadEthSignedMessage(this.payload, SignatureDomainSeperator.blockAttestation); // Cache the sender for later use this.sender = recoverAddress(hashed, this.signature); } @@ -62,7 +62,7 @@ export class BlockAttestation extends Gossipable { } getPayload(): Buffer { - return this.payload.getPayloadToSign(); + return this.payload.getPayloadToSign(SignatureDomainSeperator.blockAttestation); } toBuffer(): Buffer { diff --git a/yarn-project/circuit-types/src/p2p/block_proposal.ts b/yarn-project/circuit-types/src/p2p/block_proposal.ts index 2733f03db19..8e64f4c1fd9 100644 --- a/yarn-project/circuit-types/src/p2p/block_proposal.ts +++ b/yarn-project/circuit-types/src/p2p/block_proposal.ts @@ -7,7 +7,11 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize'; import { ConsensusPayload } from './consensus_payload.js'; import { Gossipable } from './gossipable.js'; -import { getHashedSignaturePayload, getHashedSignaturePayloadEthSignedMessage } from './signature_utils.js'; +import { + SignatureDomainSeperator, + getHashedSignaturePayload, + getHashedSignaturePayloadEthSignedMessage, +} from './signature_utils.js'; import { TopicType, createTopicString } from './topic_type.js'; export class BlockProposalHash extends Buffer32 { @@ -49,7 +53,7 @@ export class BlockProposal extends Gossipable { payload: ConsensusPayload, payloadSigner: (payload: Buffer32) => Promise, ) { - const hashed = getHashedSignaturePayload(payload); + const hashed = getHashedSignaturePayload(payload, SignatureDomainSeperator.blockProposal); const sig = await payloadSigner(hashed); return new BlockProposal(payload, sig); @@ -60,7 +64,7 @@ export class BlockProposal extends Gossipable { */ getSender() { if (!this.sender) { - const hashed = getHashedSignaturePayloadEthSignedMessage(this.payload); + const hashed = getHashedSignaturePayloadEthSignedMessage(this.payload, SignatureDomainSeperator.blockProposal); // Cache the sender for later use this.sender = recoverAddress(hashed, this.signature); } @@ -69,7 +73,7 @@ export class BlockProposal extends Gossipable { } getPayload() { - return this.payload.getPayloadToSign(); + return this.payload.getPayloadToSign(SignatureDomainSeperator.blockProposal); } toBuffer(): Buffer { diff --git a/yarn-project/circuit-types/src/p2p/consensus_payload.ts b/yarn-project/circuit-types/src/p2p/consensus_payload.ts index d3d13f2d2d0..f6b70a5f354 100644 --- a/yarn-project/circuit-types/src/p2p/consensus_payload.ts +++ b/yarn-project/circuit-types/src/p2p/consensus_payload.ts @@ -6,7 +6,7 @@ import { type FieldsOf } from '@aztec/foundation/types'; import { encodeAbiParameters, parseAbiParameters } from 'viem'; import { TxHash } from '../tx/tx_hash.js'; -import { type Signable } from './signature_utils.js'; +import { type Signable, type SignatureDomainSeperator } from './signature_utils.js'; export class ConsensusPayload implements Signable { private size: number | undefined; @@ -24,10 +24,10 @@ export class ConsensusPayload implements Signable { return [fields.header, fields.archive, fields.txHashes] as const; } - getPayloadToSign(): Buffer { - const abi = parseAbiParameters('bytes32, bytes32[]'); + getPayloadToSign(domainSeperator: SignatureDomainSeperator): Buffer { + const abi = parseAbiParameters('uint8, bytes32, bytes32[]'); const txArray = this.txHashes.map(tx => tx.to0xString()); - const encodedData = encodeAbiParameters(abi, [this.archive.toString(), txArray] as const); + const encodedData = encodeAbiParameters(abi, [domainSeperator, this.archive.toString(), txArray] as const); return Buffer.from(encodedData.slice(2), 'hex'); } diff --git a/yarn-project/circuit-types/src/p2p/mocks.ts b/yarn-project/circuit-types/src/p2p/mocks.ts index 0286c4207ed..821d4fd3b4b 100644 --- a/yarn-project/circuit-types/src/p2p/mocks.ts +++ b/yarn-project/circuit-types/src/p2p/mocks.ts @@ -7,7 +7,7 @@ import { TxHash } from '../tx/tx_hash.js'; import { BlockAttestation } from './block_attestation.js'; import { BlockProposal } from './block_proposal.js'; import { ConsensusPayload } from './consensus_payload.js'; -import { getHashedSignaturePayloadEthSignedMessage } from './signature_utils.js'; +import { SignatureDomainSeperator, getHashedSignaturePayloadEthSignedMessage } from './signature_utils.js'; export interface MakeConsensusPayloadOptions { signer?: Secp256k1Signer; @@ -16,7 +16,10 @@ export interface MakeConsensusPayloadOptions { txHashes?: TxHash[]; } -const makeAndSignConsensusPayload = (options?: MakeConsensusPayloadOptions) => { +const makeAndSignConsensusPayload = ( + domainSeperator: SignatureDomainSeperator, + options?: MakeConsensusPayloadOptions, +) => { const { signer = Secp256k1Signer.random(), header = makeHeader(1), @@ -30,19 +33,19 @@ const makeAndSignConsensusPayload = (options?: MakeConsensusPayloadOptions) => { txHashes, }); - const hash = getHashedSignaturePayloadEthSignedMessage(payload); + const hash = getHashedSignaturePayloadEthSignedMessage(payload, domainSeperator); const signature = signer.sign(hash); return { payload, signature }; }; export const makeBlockProposal = (options?: MakeConsensusPayloadOptions): BlockProposal => { - const { payload, signature } = makeAndSignConsensusPayload(options); + const { payload, signature } = makeAndSignConsensusPayload(SignatureDomainSeperator.blockProposal, options); return new BlockProposal(payload, signature); }; // TODO(https://github.com/AztecProtocol/aztec-packages/issues/8028) export const makeBlockAttestation = (options?: MakeConsensusPayloadOptions): BlockAttestation => { - const { payload, signature } = makeAndSignConsensusPayload(options); + const { payload, signature } = makeAndSignConsensusPayload(SignatureDomainSeperator.blockAttestation, options); return new BlockAttestation(payload, signature); }; diff --git a/yarn-project/circuit-types/src/p2p/signature_utils.ts b/yarn-project/circuit-types/src/p2p/signature_utils.ts index b06cbdf5f92..25e20ded5d8 100644 --- a/yarn-project/circuit-types/src/p2p/signature_utils.ts +++ b/yarn-project/circuit-types/src/p2p/signature_utils.ts @@ -1,8 +1,13 @@ import { Buffer32 } from '@aztec/foundation/buffer'; import { keccak256, makeEthSignDigest } from '@aztec/foundation/crypto'; +export enum SignatureDomainSeperator { + blockProposal = 0, + blockAttestation = 1, +} + export interface Signable { - getPayloadToSign(): Buffer; + getPayloadToSign(domainSeperator: SignatureDomainSeperator): Buffer; } /** @@ -10,8 +15,8 @@ export interface Signable { * @param s - The `Signable` to sign * @returns The hashed payload for the signature of the `Signable` */ -export function getHashedSignaturePayload(s: Signable): Buffer32 { - return Buffer32.fromBuffer(keccak256(s.getPayloadToSign())); +export function getHashedSignaturePayload(s: Signable, domainSeperator: SignatureDomainSeperator): Buffer32 { + return Buffer32.fromBuffer(keccak256(s.getPayloadToSign(domainSeperator))); } /** @@ -19,7 +24,10 @@ export function getHashedSignaturePayload(s: Signable): Buffer32 { * @param s - the `Signable` to sign * @returns The hashed payload for the signature of the `Signable` as an Ethereum signed message */ -export function getHashedSignaturePayloadEthSignedMessage(s: Signable): Buffer32 { - const payload = getHashedSignaturePayload(s); +export function getHashedSignaturePayloadEthSignedMessage( + s: Signable, + domainSeperator: SignatureDomainSeperator, +): Buffer32 { + const payload = getHashedSignaturePayload(s, domainSeperator); return makeEthSignDigest(payload); } diff --git a/yarn-project/p2p/src/mem_pools/attestation_pool/mocks.ts b/yarn-project/p2p/src/mem_pools/attestation_pool/mocks.ts index bf75e7c3905..97bf92329b8 100644 --- a/yarn-project/p2p/src/mem_pools/attestation_pool/mocks.ts +++ b/yarn-project/p2p/src/mem_pools/attestation_pool/mocks.ts @@ -1,4 +1,4 @@ -import { BlockAttestation, ConsensusPayload, TxHash } from '@aztec/circuit-types'; +import { BlockAttestation, ConsensusPayload, SignatureDomainSeperator, TxHash } from '@aztec/circuit-types'; import { makeHeader } from '@aztec/circuits.js/testing'; import { Signature } from '@aztec/foundation/eth-signature'; import { Fr } from '@aztec/foundation/fields'; @@ -33,7 +33,9 @@ export const mockAttestation = async ( const payload = new ConsensusPayload(header, archive, txs); - const message: `0x${string}` = `0x${payload.getPayloadToSign().toString('hex')}`; + const message: `0x${string}` = `0x${payload + .getPayloadToSign(SignatureDomainSeperator.blockAttestation) + .toString('hex')}`; const sigString = await signer.signMessage({ message }); const signature = Signature.from0xString(sigString); diff --git a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts index 3a1fc87b18e..1553e4e07a5 100644 --- a/yarn-project/sequencer-client/src/publisher/l1-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/l1-publisher.ts @@ -3,6 +3,7 @@ import { type EpochProofClaim, type EpochProofQuote, type L2Block, + SignatureDomainSeperator, type TxHash, getHashedSignaturePayload, } from '@aztec/circuit-types'; @@ -464,7 +465,7 @@ export class L1Publisher { const consensusPayload = new ConsensusPayload(block.header, block.archive.root, txHashes ?? []); - const digest = getHashedSignaturePayload(consensusPayload); + const digest = getHashedSignaturePayload(consensusPayload, SignatureDomainSeperator.blockAttestation); const proposeTxArgs = { header: block.header.toBuffer(), archive: block.archive.root.toBuffer(), diff --git a/yarn-project/validator-client/src/duties/validation_service.ts b/yarn-project/validator-client/src/duties/validation_service.ts index 55699c13d51..ee5718b2e00 100644 --- a/yarn-project/validator-client/src/duties/validation_service.ts +++ b/yarn-project/validator-client/src/duties/validation_service.ts @@ -1,4 +1,10 @@ -import { BlockAttestation, BlockProposal, ConsensusPayload, type TxHash } from '@aztec/circuit-types'; +import { + BlockAttestation, + BlockProposal, + ConsensusPayload, + SignatureDomainSeperator, + type TxHash, +} from '@aztec/circuit-types'; import { type Header } from '@aztec/circuits.js'; import { Buffer32 } from '@aztec/foundation/buffer'; import { keccak256 } from '@aztec/foundation/crypto'; @@ -36,7 +42,9 @@ export class ValidationService { async attestToProposal(proposal: BlockProposal): Promise { // TODO(https://github.com/AztecProtocol/aztec-packages/issues/7961): check that the current validator is correct - const buf = Buffer32.fromBuffer(keccak256(proposal.getPayload())); + const buf = Buffer32.fromBuffer( + keccak256(proposal.payload.getPayloadToSign(SignatureDomainSeperator.blockAttestation)), + ); const sig = await this.keyStore.signMessage(buf); return new BlockAttestation(proposal.payload, sig); }