Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: domain separate block proposals and attestations #9842

Merged
merged 4 commits into from
Nov 8, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At this point we might as well just do proper signatures full on EIP712 😆

setupEpoch();
_validateHeader({
_header: header,
Expand Down
8 changes: 8 additions & 0 deletions l1-contracts/src/core/libraries/crypto/SignatureLib.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
3 changes: 2 additions & 1 deletion l1-contracts/test/sparta/Sparta.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
6 changes: 3 additions & 3 deletions yarn-project/circuit-types/src/p2p/block_attestation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
}
Expand All @@ -62,7 +62,7 @@ export class BlockAttestation extends Gossipable {
}

getPayload(): Buffer {
return this.payload.getPayloadToSign();
return this.payload.getPayloadToSign(SignatureDomainSeperator.blockAttestation);
}

toBuffer(): Buffer {
Expand Down
12 changes: 8 additions & 4 deletions yarn-project/circuit-types/src/p2p/block_proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -49,7 +53,7 @@ export class BlockProposal extends Gossipable {
payload: ConsensusPayload,
payloadSigner: (payload: Buffer32) => Promise<Signature>,
) {
const hashed = getHashedSignaturePayload(payload);
const hashed = getHashedSignaturePayload(payload, SignatureDomainSeperator.blockProposal);
const sig = await payloadSigner(hashed);

return new BlockProposal(payload, sig);
Expand All @@ -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);
}
Expand All @@ -69,7 +73,7 @@ export class BlockProposal extends Gossipable {
}

getPayload() {
return this.payload.getPayloadToSign();
return this.payload.getPayloadToSign(SignatureDomainSeperator.blockProposal);
}

toBuffer(): Buffer {
Expand Down
8 changes: 4 additions & 4 deletions yarn-project/circuit-types/src/p2p/consensus_payload.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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');
}
Expand Down
13 changes: 8 additions & 5 deletions yarn-project/circuit-types/src/p2p/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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),
Expand All @@ -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);
};
18 changes: 13 additions & 5 deletions yarn-project/circuit-types/src/p2p/signature_utils.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,33 @@
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;
}

/**
* Get the hashed payload for the signature of the `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)));
}

/**
* Get the hashed payload for the signature of the `Signable` as an Ethereum signed message EIP-712
* @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);
}
6 changes: 4 additions & 2 deletions yarn-project/p2p/src/mem_pools/attestation_pool/mocks.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion yarn-project/sequencer-client/src/publisher/l1-publisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
type EpochProofClaim,
type EpochProofQuote,
type L2Block,
SignatureDomainSeperator,
type TxHash,
getHashedSignaturePayload,
} from '@aztec/circuit-types';
Expand Down Expand Up @@ -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(),
Expand Down
12 changes: 10 additions & 2 deletions yarn-project/validator-client/src/duties/validation_service.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -36,7 +42,9 @@ export class ValidationService {
async attestToProposal(proposal: BlockProposal): Promise<BlockAttestation> {
// 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);
}
Expand Down
Loading