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: validators ensure transactions live in their p2p pool before attesting #8410

Merged
merged 33 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
b51e908
feat: cleanup publisher
LHerskind Aug 22, 2024
da2eb7a
refactor: get rid of timetraveler from l1-publisher
LHerskind Aug 28, 2024
3388966
feat: revert if timestamp in future
LHerskind Aug 28, 2024
13a60a3
feat: including txhashes explicitly in the rollup attestations
Maddiaa0 Aug 29, 2024
86026f2
temp
Maddiaa0 Aug 30, 2024
f3eac5b
Merge branch 'master' into md/check-tx-requests-before-signing
Maddiaa0 Aug 30, 2024
fc7a04a
temp
Maddiaa0 Aug 30, 2024
9eed298
temp
Maddiaa0 Sep 2, 2024
cc09455
temp
Maddiaa0 Sep 2, 2024
06f950f
Merge branch 'master' into md/check-tx-requests-before-signing
Maddiaa0 Sep 2, 2024
4727cd9
temp: get passing with txhash payloads
Maddiaa0 Sep 3, 2024
b4c2a46
fix: make sure transactions are available in the tx pool
Maddiaa0 Sep 5, 2024
4a8d178
chore: remove logs
Maddiaa0 Sep 5, 2024
b4324fc
fmt
Maddiaa0 Sep 5, 2024
052641a
Merge branch 'master' into md/check-tx-requests-before-signing
Maddiaa0 Sep 5, 2024
a803a94
🪿
Maddiaa0 Sep 5, 2024
164c117
chore: validator tests
Maddiaa0 Sep 6, 2024
c10260c
Merge branch 'master' into md/check-tx-requests-before-signing
Maddiaa0 Sep 6, 2024
045af5a
clean
Maddiaa0 Sep 6, 2024
73d26ec
🧹
Maddiaa0 Sep 6, 2024
d358228
chore: fix sequencing tests
Maddiaa0 Sep 7, 2024
4b31953
Merge branch 'master' into md/check-tx-requests-before-signing
Maddiaa0 Sep 7, 2024
2e3f80b
fmt solidity
Maddiaa0 Sep 7, 2024
5734006
Merge branch 'master' into md/check-tx-requests-before-signing
Maddiaa0 Sep 7, 2024
1bde1fe
fix: test hash
Maddiaa0 Sep 8, 2024
7a50a2b
exp: adjust test nodes
Maddiaa0 Sep 8, 2024
1c2b151
fix: use abi.encode vs encodePacked
Maddiaa0 Sep 11, 2024
e6e7f6b
fix
Maddiaa0 Sep 11, 2024
6f417fc
Merge branch 'master' into md/check-tx-requests-before-signing
Maddiaa0 Sep 11, 2024
cde6283
fix: merge fix
Maddiaa0 Sep 11, 2024
8290c99
fmt
Maddiaa0 Sep 11, 2024
1452017
Merge branch 'master' into md/check-tx-requests-before-signing
Maddiaa0 Sep 11, 2024
b0b8239
fix: sequencer client test after merge
Maddiaa0 Sep 11, 2024
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
6 changes: 5 additions & 1 deletion l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -191,18 +191,21 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
bytes calldata _header,
bytes32 _archive,
bytes32 _blockHash,
bytes32[] memory _txHashes,
SignatureLib.Signature[] memory _signatures,
bytes calldata _body
) external override(IRollup) {
bytes32 txsEffectsHash = TxsDecoder.decode(_body);

// Decode and validate header
HeaderLib.Header memory header = HeaderLib.decode(_header);

bytes32 digest = keccak256(abi.encode(_archive, _txHashes));
setupEpoch();
_validateHeader({
_header: header,
_signatures: _signatures,
_digest: _archive,
_digest: digest,
_currentTime: block.timestamp,
_txEffectsHash: txsEffectsHash,
_flags: DataStructures.ExecutionFlags({ignoreDA: false, ignoreSignatures: false})
Expand Down Expand Up @@ -419,6 +422,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
revert Errors.Rollup__SlotAlreadyInChain(lastSlot, slot);
}

// Make sure that the proposer is up to date
bytes32 tipArchive = archive();
if (tipArchive != _archive) {
revert Errors.Rollup__InvalidArchive(tipArchive, _archive);
Expand Down
1 change: 1 addition & 0 deletions l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ interface IRollup {
bytes calldata _header,
bytes32 _archive,
bytes32 _blockHash,
bytes32[] memory _txHashes,
SignatureLib.Signature[] memory _signatures,
bytes calldata _body
) external;
Expand Down
3 changes: 0 additions & 3 deletions l1-contracts/src/core/sequencer_selection/Leonidas.sol
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,6 @@ contract Leonidas is Ownable, ILeonidas {

/**
* @notice Get the validator set for the current epoch
*
* @dev Makes a call to setupEpoch under the hood, this should ONLY be called as a view function, and not from within
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks 🫡

* this contract.
* @return The validator set for the current epoch
*/
function getCurrentEpochCommittee() external view override(ILeonidas) returns (address[] memory) {
Expand Down
25 changes: 16 additions & 9 deletions l1-contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,12 @@ contract RollupTest is DecoderBase {
bytes memory header = data.header;
bytes32 archive = data.archive;
bytes memory body = data.body;
bytes32[] memory txHashes = new bytes32[](0);

// We jump to the time of the block. (unless it is in the past)
vm.warp(max(block.timestamp, data.decodedHeader.globalVariables.timestamp));

rollup.propose(header, archive, bytes32(0), signatures, body);
rollup.propose(header, archive, bytes32(0), txHashes, signatures, body);

rollup.submitBlockRootProof(header, archive, bytes32(0), "", "");

Expand Down Expand Up @@ -186,6 +187,7 @@ contract RollupTest is DecoderBase {
bytes memory header = data.header;
bytes32 archive = data.archive;
bytes memory body = data.body;
bytes32[] memory txHashes = new bytes32[](0);

// Progress time as necessary
vm.warp(max(block.timestamp, data.decodedHeader.globalVariables.timestamp));
Expand All @@ -206,7 +208,7 @@ contract RollupTest is DecoderBase {
assertEq(coinbaseBalance, 0, "invalid initial coinbase balance");

// Assert that balance have NOT been increased by proposing the block
rollup.propose(header, archive, bytes32(0), signatures, body);
rollup.propose(header, archive, bytes32(0), txHashes, signatures, body);
assertEq(portalERC20.balanceOf(coinbase), 0, "invalid coinbase balance");

vm.expectRevert(
Expand Down Expand Up @@ -251,9 +253,10 @@ contract RollupTest is DecoderBase {
bytes memory header = data.header;
bytes32 archive = data.archive;
bytes memory body = data.body;
bytes32[] memory txHashes = new bytes32[](0);

vm.warp(max(block.timestamp, data.decodedHeader.globalVariables.timestamp));
rollup.propose(header, archive, bytes32(0), signatures, body);
rollup.propose(header, archive, bytes32(0), txHashes, signatures, body);

vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__NonSequentialProving.selector));
rollup.submitBlockRootProof(header, archive, bytes32(0), "", "");
Expand Down Expand Up @@ -282,50 +285,53 @@ contract RollupTest is DecoderBase {
bytes memory header = data.header;
bytes32 archive = data.archive;
bytes memory body = data.body;
bytes32[] memory txHashes = new bytes32[](0);

assembly {
// TODO: Hardcoding offsets in the middle of tests is annoying to say the least.
mstore(add(header, add(0x20, 0x0174)), 0x420)
}

vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidBlockNumber.selector, 1, 0x420));
rollup.propose(header, archive, bytes32(0), signatures, body);
rollup.propose(header, archive, bytes32(0), txHashes, signatures, body);
}

function testRevertInvalidChainId() public setUpFor("empty_block_1") {
DecoderBase.Data memory data = load("empty_block_1").block;
bytes memory header = data.header;
bytes32 archive = data.archive;
bytes memory body = data.body;
bytes32[] memory txHashes = new bytes32[](0);

assembly {
// TODO: Hardcoding offsets in the middle of tests is annoying to say the least.
mstore(add(header, add(0x20, 0x0134)), 0x420)
}

vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidChainId.selector, 31337, 0x420));
rollup.propose(header, archive, bytes32(0), signatures, body);
rollup.propose(header, archive, bytes32(0), txHashes, signatures, body);
}

function testRevertInvalidVersion() public setUpFor("empty_block_1") {
DecoderBase.Data memory data = load("empty_block_1").block;
bytes memory header = data.header;
bytes32 archive = data.archive;
bytes memory body = data.body;
bytes32[] memory txHashes = new bytes32[](0);

assembly {
mstore(add(header, add(0x20, 0x0154)), 0x420)
}

vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidVersion.selector, 1, 0x420));
rollup.propose(header, archive, bytes32(0), signatures, body);
rollup.propose(header, archive, bytes32(0), txHashes, signatures, body);
}

function testRevertInvalidTimestamp() public setUpFor("empty_block_1") {
DecoderBase.Data memory data = load("empty_block_1").block;
bytes memory header = data.header;
bytes32 archive = data.archive;
bytes memory body = data.body;
bytes32[] memory txHashes = new bytes32[](0);

uint256 realTs = data.decodedHeader.globalVariables.timestamp;
uint256 badTs = realTs + 1;
Expand All @@ -337,7 +343,7 @@ contract RollupTest is DecoderBase {
}

vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidTimestamp.selector, realTs, badTs));
rollup.propose(header, archive, bytes32(0), signatures, body);
rollup.propose(header, archive, bytes32(0), txHashes, signatures, body);
}

function testBlocksWithAssumeProven() public setUpFor("mixed_block_1") {
Expand Down Expand Up @@ -418,6 +424,7 @@ contract RollupTest is DecoderBase {
bytes32 archive = full.block.archive;
bytes memory body = full.block.body;
uint32 numTxs = full.block.numTxs;
bytes32[] memory txHashes = new bytes32[](0);

// Overwrite some timestamps if needed
if (_slotNumber != 0) {
Expand All @@ -436,7 +443,7 @@ contract RollupTest is DecoderBase {

_populateInbox(full.populate.sender, full.populate.recipient, full.populate.l1ToL2Content);

rollup.propose(header, archive, bytes32(0), signatures, body);
rollup.propose(header, archive, bytes32(0), txHashes, signatures, body);

if (_submitProof) {
uint256 pre = rollup.provenBlockCount();
Expand Down
9 changes: 6 additions & 3 deletions l1-contracts/test/sparta/Sparta.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,17 @@ contract SpartaTest is DecoderBase {

rollup.setupEpoch();

bytes32[] memory txHashes = new bytes32[](0);

if (_signatureCount > 0 && ree.proposer != address(0)) {
address[] memory validators = rollup.getEpochCommittee(rollup.getCurrentEpoch());
ree.needed = validators.length * 2 / 3 + 1;

SignatureLib.Signature[] memory signatures = new SignatureLib.Signature[](_signatureCount);

bytes32 digest = keccak256(abi.encode(archive, txHashes));
for (uint256 i = 0; i < _signatureCount; i++) {
signatures[i] = createSignature(validators[i], archive);
signatures[i] = createSignature(validators[i], digest);
}

if (_expectRevert) {
Expand Down Expand Up @@ -208,15 +211,15 @@ contract SpartaTest is DecoderBase {
}

vm.prank(ree.proposer);
rollup.propose(header, archive, bytes32(0), signatures, body);
rollup.propose(header, archive, bytes32(0), txHashes, signatures, body);

if (ree.shouldRevert) {
return;
}
} else {
SignatureLib.Signature[] memory signatures = new SignatureLib.Signature[](0);

rollup.propose(header, archive, bytes32(0), signatures, body);
rollup.propose(header, archive, bytes32(0), txHashes, signatures, body);
}

assertEq(_expectRevert, ree.shouldRevert, "Does not match revert expectation");
Expand Down
2 changes: 1 addition & 1 deletion yarn-project/archiver/src/archiver/archiver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ function makeRollupTx(l2Block: L2Block) {
const input = encodeFunctionData({
abi: RollupAbi,
functionName: 'propose',
args: [header, archive, blockHash, [], body],
args: [header, archive, blockHash, [], [], body],
});
return { input } as Transaction<bigint, number>;
}
2 changes: 1 addition & 1 deletion yarn-project/archiver/src/archiver/eth_log_handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ async function getBlockFromRollupTx(
if (!(functionName === 'propose')) {
throw new Error(`Unexpected method called ${functionName}`);
}
const [headerHex, archiveRootHex, , , bodyHex] = args! as readonly [Hex, Hex, Hex, ViemSignature[], Hex];
const [headerHex, archiveRootHex, , , , bodyHex] = args! as readonly [Hex, Hex, Hex, Hex[], ViemSignature[], Hex];

const header = Header.fromBuffer(Buffer.from(hexToBytes(headerHex)));
const blockBody = Body.fromBuffer(Buffer.from(hexToBytes(bodyHex)));
Expand Down
21 changes: 17 additions & 4 deletions yarn-project/circuit-types/src/p2p/block_attestation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';

import { recoverMessageAddress } from 'viem';

import { TxHash } from '../tx/tx_hash.js';
import { get0xStringHashedSignaturePayload, getSignaturePayload } from './block_utils.js';
import { Gossipable } from './gossipable.js';
import { Signature } from './signature.js';
import { TopicType, createTopicString } from './topic_type.js';
Expand All @@ -31,6 +33,7 @@ export class BlockAttestation extends Gossipable {
public readonly header: Header,
// TODO(https://github.com/AztecProtocol/aztec-packages/pull/7727#discussion_r1713670830): temporary
public readonly archive: Fr,
public readonly txHashes: TxHash[],
/** The signature of the block attester */
public readonly signature: Signature,
) {
Expand All @@ -53,8 +56,9 @@ export class BlockAttestation extends Gossipable {
async getSender() {
if (!this.sender) {
// Recover the sender from the attestation
const hashed = get0xStringHashedSignaturePayload(this.archive, this.txHashes);
const address = await recoverMessageAddress({
message: { raw: this.p2pMessageIdentifier().to0xString() },
message: { raw: hashed },
signature: this.signature.to0xString(),
});
// Cache the sender for later use
Expand All @@ -64,16 +68,25 @@ export class BlockAttestation extends Gossipable {
return this.sender;
}

getPayload(): Buffer {
return getSignaturePayload(this.archive, this.txHashes);
}

toBuffer(): Buffer {
return serializeToBuffer([this.header, this.archive, this.signature]);
return serializeToBuffer([this.header, this.archive, this.txHashes.length, this.txHashes, this.signature]);
}

static fromBuffer(buf: Buffer | BufferReader): BlockAttestation {
const reader = BufferReader.asReader(buf);
return new BlockAttestation(reader.readObject(Header), reader.readObject(Fr), reader.readObject(Signature));
return new BlockAttestation(
reader.readObject(Header),
reader.readObject(Fr),
reader.readArray(reader.readNumber(), TxHash),
reader.readObject(Signature),
);
}

static empty(): BlockAttestation {
return new BlockAttestation(Header.empty(), Fr.ZERO, Signature.empty());
return new BlockAttestation(Header.empty(), Fr.ZERO, [], Signature.empty());
}
}
20 changes: 19 additions & 1 deletion yarn-project/circuit-types/src/p2p/block_proposal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { BufferReader, serializeToBuffer } from '@aztec/foundation/serialize';
import { recoverMessageAddress } from 'viem';

import { TxHash } from '../tx/tx_hash.js';
import { get0xStringHashedSignaturePayload, getHashedSignaturePayload, getSignaturePayload } from './block_utils.js';
import { Gossipable } from './gossipable.js';
import { Signature } from './signature.js';
import { TopicType, createTopicString } from './topic_type.js';
Expand Down Expand Up @@ -49,14 +50,27 @@ export class BlockProposal extends Gossipable {
return BlockProposalHash.fromField(this.archive);
}

static async createProposalFromSigner(
header: Header,
archive: Fr,
txs: TxHash[],
payloadSigner: (payload: Buffer) => Promise<Signature>,
) {
const hashed = getHashedSignaturePayload(archive, txs);
const sig = await payloadSigner(hashed);

return new BlockProposal(header, archive, txs, sig);
}

/**Get Sender
* Lazily evaluate the sender of the proposal; result is cached
*/
async getSender() {
if (!this.sender) {
// performance note(): this signature method requires another hash behind the scenes
const hashed = get0xStringHashedSignaturePayload(this.archive, this.txs);
const address = await recoverMessageAddress({
message: { raw: this.p2pMessageIdentifier().to0xString() },
message: { raw: hashed },
signature: this.signature.to0xString(),
});
// Cache the sender for later use
Expand All @@ -66,6 +80,10 @@ export class BlockProposal extends Gossipable {
return this.sender;
}

getPayload() {
return getSignaturePayload(this.archive, this.txs);
}

toBuffer(): Buffer {
return serializeToBuffer([this.header, this.archive, this.txs.length, this.txs, this.signature]);
}
Expand Down
34 changes: 34 additions & 0 deletions yarn-project/circuit-types/src/p2p/block_utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { keccak256 as keccak256Buffer } from '@aztec/foundation/crypto';
import { type Fr } from '@aztec/foundation/fields';

import { encodeAbiParameters, keccak256 as keccak2560xString, parseAbiParameters } from 'viem';

import { type TxHash } from '../tx/tx_hash.js';

/**
* Get the payload for the signature of the block proposal
* @param archive - The archive of the block
* @param txs - The transactions in the block
* @returns The payload for the signature of the block proposal
*/
export function getSignaturePayload(archive: Fr, txs: TxHash[]) {
const abi = parseAbiParameters('bytes32, bytes32[]');
const txArray = txs.map(tx => tx.to0xString());
const encodedData = encodeAbiParameters(abi, [archive.toString(), txArray] as const);

return Buffer.from(encodedData.slice(2), 'hex');
}

/**
* Get the hashed payload for the signature of the block proposal
* @param archive - The archive of the block
* @param txs - The transactions in the block
* @returns The hashed payload for the signature of the block proposal
*/
export function getHashedSignaturePayload(archive: Fr, txs: TxHash[]): Buffer {
return keccak256Buffer(getSignaturePayload(archive, txs));
}

export function get0xStringHashedSignaturePayload(archive: Fr, txs: TxHash[]): `0x${string}` {
return keccak2560xString(getSignaturePayload(archive, txs));
}
1 change: 1 addition & 0 deletions yarn-project/circuit-types/src/p2p/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ export * from './interface.js';
export * from './gossipable.js';
export * from './topic_type.js';
export * from './signature.js';
export * from './block_utils.js';
Loading
Loading