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: simulate validateEpochProofQuoteHeader in the future #9641

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
96 changes: 48 additions & 48 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -121,15 +121,6 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
setupEpoch();
}

function quoteToDigest(EpochProofQuoteLib.EpochProofQuote memory quote)
public
view
override(IRollup)
returns (bytes32)
{
return _hashTypedDataV4(EpochProofQuoteLib.hash(quote));
}

/**
* @notice Prune the pending chain up to the last proven block
*
Expand All @@ -153,25 +144,6 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
assumeProvenThroughBlockNumber = blockNumber;
}

function fakeBlockNumberAsProven(uint256 blockNumber) private {
if (blockNumber > tips.provenBlockNumber && blockNumber <= tips.pendingBlockNumber) {
tips.provenBlockNumber = blockNumber;

// If this results on a new epoch, create a fake claim for it
// Otherwise nextEpochToProve will report an old epoch
Epoch epoch = getEpochForBlock(blockNumber);
if (Epoch.unwrap(epoch) == 0 || Epoch.unwrap(epoch) > Epoch.unwrap(proofClaim.epochToProve)) {
proofClaim = DataStructures.EpochProofClaim({
epochToProve: epoch,
basisPointFee: 0,
bondAmount: 0,
bondProvider: address(0),
proposerClaimant: msg.sender
});
}
}
}

/**
* @notice Set the verifier contract
*
Expand Down Expand Up @@ -367,7 +339,8 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
Slot slot = getSlotAt(_ts);

// Consider if a prune will hit in this slot
uint256 pendingBlockNumber = _canPruneAt(_ts) ? tips.provenBlockNumber : tips.pendingBlockNumber;
uint256 pendingBlockNumber =
_canPruneAtTime(_ts) ? tips.provenBlockNumber : tips.pendingBlockNumber;

Slot lastSlot = blocks[pendingBlockNumber].slotNumber;

Expand Down Expand Up @@ -441,7 +414,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
public
override(IRollup)
{
validateEpochProofRightClaim(_quote);
validateEpochProofRightClaimAtTime(Timestamp.wrap(block.timestamp), _quote);

Slot currentSlot = getCurrentSlot();
Epoch epochToProve = getEpochToProve();
Expand Down Expand Up @@ -545,6 +518,15 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
}
}

function quoteToDigest(EpochProofQuoteLib.EpochProofQuote memory quote)
public
view
override(IRollup)
returns (bytes32)
{
return _hashTypedDataV4(EpochProofQuoteLib.hash(quote));
}

/**
* @notice Returns the computed public inputs for the given epoch proof.
*
Expand Down Expand Up @@ -684,17 +666,21 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
return publicInputs;
}

function validateEpochProofRightClaim(EpochProofQuoteLib.SignedEpochProofQuote calldata _quote)
public
view
override(IRollup)
{
function validateEpochProofRightClaimAtTime(
Timestamp _ts,
EpochProofQuoteLib.SignedEpochProofQuote calldata _quote
) public view override(IRollup) {
SignatureLib.verify(_quote.signature, _quote.quote.prover, quoteToDigest(_quote.quote));

Slot currentSlot = getCurrentSlot();
address currentProposer = getCurrentProposer();
Slot currentSlot = getSlotAt(_ts);
address currentProposer = getProposerAt(_ts);
Epoch epochToProve = getEpochToProve();

require(
_quote.quote.validUntilSlot >= currentSlot,
Errors.Rollup__QuoteExpired(currentSlot, _quote.quote.validUntilSlot)
);

require(
_quote.quote.basisPointFee <= 10_000,
Errors.Rollup__InvalidBasisPointFee(_quote.quote.basisPointFee)
Expand Down Expand Up @@ -734,11 +720,6 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
_quote.quote.bondAmount <= availableFundsInEscrow,
Errors.Rollup__InsufficientFundsInEscrow(_quote.quote.bondAmount, availableFundsInEscrow)
);

require(
_quote.quote.validUntilSlot >= currentSlot,
Errors.Rollup__QuoteExpired(currentSlot, _quote.quote.validUntilSlot)
);
}

/**
Expand Down Expand Up @@ -794,6 +775,10 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
return bytes32(0);
}

function canPrune() public view override(IRollup) returns (bool) {
return _canPruneAtTime(Timestamp.wrap(block.timestamp));
}

function _prune() internal {
// TODO #8656
delete proofClaim;
Expand All @@ -809,11 +794,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
emit PrunedPending(tips.provenBlockNumber, pending);
}

function canPrune() public view returns (bool) {
return _canPruneAt(Timestamp.wrap(block.timestamp));
}

function _canPruneAt(Timestamp _ts) internal view returns (bool) {
function _canPruneAtTime(Timestamp _ts) internal view returns (bool) {
if (
tips.pendingBlockNumber == tips.provenBlockNumber
|| tips.pendingBlockNumber <= assumeProvenThroughBlockNumber
Expand Down Expand Up @@ -861,7 +842,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
DataStructures.ExecutionFlags memory _flags
) internal view {
uint256 pendingBlockNumber =
_canPruneAt(_currentTime) ? tips.provenBlockNumber : tips.pendingBlockNumber;
_canPruneAtTime(_currentTime) ? tips.provenBlockNumber : tips.pendingBlockNumber;
_validateHeaderForSubmissionBase(
_header, _currentTime, _txEffectsHash, pendingBlockNumber, _flags
);
Expand Down Expand Up @@ -986,4 +967,23 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
require(_header.globalVariables.gasFees.feePerL2Gas == 0, Errors.Rollup__NonZeroL2Fee());
}
}

function fakeBlockNumberAsProven(uint256 blockNumber) private {
if (blockNumber > tips.provenBlockNumber && blockNumber <= tips.pendingBlockNumber) {
tips.provenBlockNumber = blockNumber;

// If this results on a new epoch, create a fake claim for it
// Otherwise nextEpochToProve will report an old epoch
Epoch epoch = getEpochForBlock(blockNumber);
if (Epoch.unwrap(epoch) == 0 || Epoch.unwrap(epoch) > Epoch.unwrap(proofClaim.epochToProve)) {
proofClaim = DataStructures.EpochProofClaim({
epochToProve: epoch,
basisPointFee: 0,
bondAmount: 0,
bondProvider: address(0),
proposerClaimant: msg.sender
});
}
}
}
}
10 changes: 5 additions & 5 deletions l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,6 @@ interface IRollup {

function prune() external;

function canPrune() external view returns (bool);

function claimEpochProofRight(EpochProofQuoteLib.SignedEpochProofQuote calldata _quote) external;

function propose(
Expand Down Expand Up @@ -102,14 +100,16 @@ interface IRollup {

function archive() external view returns (bytes32);
function archiveAt(uint256 _blockNumber) external view returns (bytes32);
function canPrune() external view returns (bool);
function getProvenBlockNumber() external view returns (uint256);
function getPendingBlockNumber() external view returns (uint256);
function getEpochToProve() external view returns (Epoch);
function getClaimableEpoch() external view returns (Epoch);
function getEpochForBlock(uint256 blockNumber) external view returns (Epoch);
function validateEpochProofRightClaim(EpochProofQuoteLib.SignedEpochProofQuote calldata _quote)
external
view;
function validateEpochProofRightClaimAtTime(
Timestamp _ts,
EpochProofQuoteLib.SignedEpochProofQuote calldata _quote
) external view;
function getEpochProofPublicInputs(
uint256 _epochSize,
bytes32[7] calldata _args,
Expand Down
48 changes: 34 additions & 14 deletions l1-contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,23 @@ contract RollupTest is DecoderBase {
vm.warp(Timestamp.unwrap(rollup.getTimestampForSlot(Slot.wrap(_slot))));
}

function testClaimInTheFuture(uint256 _futureSlot) public setUpFor("mixed_block_1") {
uint256 futureSlot = bound(_futureSlot, 1, 1e20);
_testBlock("mixed_block_1", false, 1);

rollup.validateEpochProofRightClaimAtTime(Timestamp.wrap(block.timestamp), signedQuote);

Timestamp t = rollup.getTimestampForSlot(quote.validUntilSlot + Slot.wrap(futureSlot));
vm.expectRevert(
abi.encodeWithSelector(
Errors.Rollup__QuoteExpired.selector,
Slot.wrap(futureSlot) + quote.validUntilSlot,
signedQuote.quote.validUntilSlot
)
);
rollup.validateEpochProofRightClaimAtTime(t, signedQuote);
}

function testClaimableEpoch(uint256 epochForMixedBlock) public setUpFor("mixed_block_1") {
epochForMixedBlock = bound(epochForMixedBlock, 1, 10);
vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__NoEpochToProve.selector));
Expand Down Expand Up @@ -266,6 +283,8 @@ contract RollupTest is DecoderBase {

function testClaimTwice() public setUpFor("mixed_block_1") {
_testBlock("mixed_block_1", false, 1);
quote.validUntilSlot = Epoch.wrap(1e9).toSlots();
signedQuote = _quoteToSignedQuote(quote);

rollup.claimEpochProofRight(signedQuote);

Expand All @@ -291,7 +310,8 @@ contract RollupTest is DecoderBase {

function testClaimOutsideClaimPhase() public setUpFor("mixed_block_1") {
_testBlock("mixed_block_1", false, 1);

quote.validUntilSlot = Epoch.wrap(1e9).toSlots();
signedQuote = _quoteToSignedQuote(quote);
warpToL2Slot(Constants.AZTEC_EPOCH_DURATION + rollup.CLAIM_DURATION_IN_L2_SLOTS());

vm.expectRevert(
Expand Down Expand Up @@ -840,19 +860,6 @@ contract RollupTest is DecoderBase {
_submitEpochProof(rollup, 1, preArchive, data.archive, preBlockHash, wrongBlockHash, bytes32(0));
}

function _quoteToSignedQuote(EpochProofQuoteLib.EpochProofQuote memory _quote)
internal
view
returns (EpochProofQuoteLib.SignedEpochProofQuote memory)
{
bytes32 digest = rollup.quoteToDigest(_quote);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);
return EpochProofQuoteLib.SignedEpochProofQuote({
quote: _quote,
signature: SignatureLib.Signature({isEmpty: false, v: v, r: r, s: s})
});
}

function _testBlock(string memory name, bool _submitProof) public {
_testBlock(name, _submitProof, 0);
}
Expand Down Expand Up @@ -998,4 +1005,17 @@ contract RollupTest is DecoderBase {

_rollup.submitEpochRootProof(_epochSize, args, fees, aggregationObject, proof);
}

function _quoteToSignedQuote(EpochProofQuoteLib.EpochProofQuote memory _quote)
internal
view
returns (EpochProofQuoteLib.SignedEpochProofQuote memory)
{
bytes32 digest = rollup.quoteToDigest(_quote);
(uint8 v, bytes32 r, bytes32 s) = vm.sign(privateKey, digest);
return EpochProofQuoteLib.SignedEpochProofQuote({
quote: _quote,
signature: SignatureLib.Signature({isEmpty: false, v: v, r: r, s: s})
});
}
}
12 changes: 8 additions & 4 deletions yarn-project/sequencer-client/src/publisher/l1-publisher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,8 +239,11 @@ export class L1Publisher {
// FIXME: This should not throw if unable to propose but return a falsey value, so
// we can differentiate between errors when hitting the L1 rollup contract (eg RPC error)
// which may require a retry, vs actually not being the turn for proposing.
const ts = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION));
const [slot, blockNumber] = await this.rollupContract.read.canProposeAtTime([ts, `0x${archive.toString('hex')}`]);
const timeOfNextL1Slot = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION));
const [slot, blockNumber] = await this.rollupContract.read.canProposeAtTime([
timeOfNextL1Slot,
`0x${archive.toString('hex')}`,
]);
return [slot, blockNumber];
}

Expand Down Expand Up @@ -302,9 +305,10 @@ export class L1Publisher {
}

public async validateProofQuote(quote: EpochProofQuote): Promise<EpochProofQuote | undefined> {
const args = [quote.toViemArgs()] as const;
const timeOfNextL1Slot = BigInt((await this.publicClient.getBlock()).timestamp + BigInt(ETHEREUM_SLOT_DURATION));
const args = [timeOfNextL1Slot, quote.toViemArgs()] as const;
try {
await this.rollupContract.read.validateEpochProofRightClaim(args, { account: this.account });
await this.rollupContract.read.validateEpochProofRightClaimAtTime(args, { account: this.account });
} catch (err) {
const errorName = tryGetCustomErrorName(err);
this.log.warn(`Proof quote validation failed: ${errorName}`);
Expand Down
Loading