Skip to content

Commit

Permalink
feat: add canPruneAtTime (#9751)
Browse files Browse the repository at this point in the history
  • Loading branch information
LHerskind authored and Ubuntu committed Nov 7, 2024
1 parent 39fcf43 commit b4718d4
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 33 deletions.
49 changes: 29 additions & 20 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -227,9 +227,18 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
bytes calldata _aggregationObject,
bytes calldata _proof
) external override(IRollup) {
if (canPrune()) {
_prune();
}

uint256 previousBlockNumber = tips.provenBlockNumber;
uint256 endBlockNumber = previousBlockNumber + _epochSize;

// @note The getEpochForBlock is expected to revert if the block is beyond pending.
// If this changes you are gonna get so rekt you won't believe it.
// I mean proving blocks that have been pruned rekt.
Epoch epochToProve = getEpochForBlock(endBlockNumber);

bytes32[] memory publicInputs =
getEpochProofPublicInputs(_epochSize, _args, _fees, _aggregationObject);

Expand Down Expand Up @@ -287,7 +296,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
}
}

if (proofClaim.epochToProve == getEpochForBlock(endBlockNumber)) {
if (proofClaim.epochToProve == epochToProve) {
PROOF_COMMITMENT_ESCROW.unstakeBond(proofClaim.bondProvider, proofClaim.bondAmount);
}

Expand Down Expand Up @@ -336,7 +345,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {

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

Slot lastSlot = blocks[pendingBlockNumber].slotNumber;

Expand Down Expand Up @@ -772,25 +781,10 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
}

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

function _prune() internal {
// TODO #8656
delete proofClaim;

uint256 pending = tips.pendingBlockNumber;

// @note We are not deleting the blocks, but we are "winding back" the pendingTip to the last block that was proven.
// We can do because any new block proposed will overwrite a previous block in the block log,
// so no values should "survive".
// People must therefore read the chain using the pendingTip as a boundary.
tips.pendingBlockNumber = tips.provenBlockNumber;

emit PrunedPending(tips.provenBlockNumber, pending);
}

function _canPruneAtTime(Timestamp _ts) internal view returns (bool) {
function canPruneAtTime(Timestamp _ts) public view override(IRollup) returns (bool) {
if (
tips.pendingBlockNumber == tips.provenBlockNumber
|| tips.pendingBlockNumber <= assumeProvenThroughBlockNumber
Expand Down Expand Up @@ -819,6 +813,21 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
return true;
}

function _prune() internal {
// TODO #8656
delete proofClaim;

uint256 pending = tips.pendingBlockNumber;

// @note We are not deleting the blocks, but we are "winding back" the pendingTip to the last block that was proven.
// We can do because any new block proposed will overwrite a previous block in the block log,
// so no values should "survive".
// People must therefore read the chain using the pendingTip as a boundary.
tips.pendingBlockNumber = tips.provenBlockNumber;

emit PrunedPending(tips.provenBlockNumber, pending);
}

/**
* @notice Validates the header for submission
*
Expand All @@ -838,7 +847,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
DataStructures.ExecutionFlags memory _flags
) internal view {
uint256 pendingBlockNumber =
_canPruneAtTime(_currentTime) ? tips.provenBlockNumber : tips.pendingBlockNumber;
canPruneAtTime(_currentTime) ? tips.provenBlockNumber : tips.pendingBlockNumber;
_validateHeaderForSubmissionBase(
_header, _currentTime, _txEffectsHash, pendingBlockNumber, _flags
);
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 @@ -101,6 +101,7 @@ interface IRollup {
function archive() external view returns (bytes32);
function archiveAt(uint256 _blockNumber) external view returns (bytes32);
function canPrune() external view returns (bool);
function canPruneAtTime(Timestamp _ts) external view returns (bool);
function getProvenBlockNumber() external view returns (uint256);
function getPendingBlockNumber() external view returns (uint256);
function getEpochToProve() external view returns (Epoch);
Expand Down
4 changes: 1 addition & 3 deletions l1-contracts/test/Rollup.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -418,9 +418,7 @@ contract RollupTest is DecoderBase {
(bytes32 preArchive, bytes32 preBlockHash,) = rollup.blocks(0);
_submitEpochProof(rollup, 1, preArchive, archive, preBlockHash, blockHash, proverId);

vm.expectRevert(
abi.encodeWithSelector(Errors.Rollup__InvalidPreviousArchive.selector, archive, preArchive)
);
vm.expectRevert(abi.encodeWithSelector(Errors.Rollup__InvalidBlockNumber.selector, 1, 2));
_submitEpochProof(rollup, 1, preArchive, archive, preBlockHash, blockHash, proverId);
}

Expand Down
6 changes: 3 additions & 3 deletions yarn-project/archiver/src/archiver/archiver.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
LogType,
UnencryptedL2BlockL2Logs,
} from '@aztec/circuit-types';
import { GENESIS_ARCHIVE_ROOT } from '@aztec/circuits.js';
import { ETHEREUM_SLOT_DURATION, GENESIS_ARCHIVE_ROOT } from '@aztec/circuits.js';
import { EthAddress } from '@aztec/foundation/eth-address';
import { Fr } from '@aztec/foundation/fields';
import { sleep } from '@aztec/foundation/sleep';
Expand Down Expand Up @@ -60,7 +60,7 @@ describe('Archiver', () => {
now = +new Date();
publicClient = mock<PublicClient<HttpTransport, Chain>>({
getBlock: ((args: any) => ({
timestamp: args.blockNumber * 1000n + BigInt(now),
timestamp: args.blockNumber * BigInt(ETHEREUM_SLOT_DURATION) + BigInt(now),
})) as any,
});

Expand Down Expand Up @@ -98,7 +98,7 @@ describe('Archiver', () => {
let latestBlockNum = await archiver.getBlockNumber();
expect(latestBlockNum).toEqual(0);

blocks.forEach((b, i) => (b.header.globalVariables.timestamp = new Fr(now + 1000 * (i + 1))));
blocks.forEach((b, i) => (b.header.globalVariables.timestamp = new Fr(now + ETHEREUM_SLOT_DURATION * (i + 1))));
const rollupTxs = blocks.map(makeRollupTx);

publicClient.getBlockNumber.mockResolvedValueOnce(2500n).mockResolvedValueOnce(2600n).mockResolvedValueOnce(2700n);
Expand Down
17 changes: 10 additions & 7 deletions yarn-project/archiver/src/archiver/archiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
type ContractDataSource,
ContractInstanceDeployedEvent,
type ContractInstanceWithAddress,
ETHEREUM_SLOT_DURATION,
type ExecutablePrivateFunctionWithMembershipProof,
type FunctionSelector,
type Header,
Expand Down Expand Up @@ -246,6 +247,12 @@ export class Archiver implements ArchiveSource {
// ********** Events that are processed per L1 block **********
await this.handleL1ToL2Messages(blockUntilSynced, messagesSynchedTo, currentL1BlockNumber);

// Store latest l1 block number and timestamp seen. Used for epoch and slots calculations.
if (!this.l1BlockNumber || this.l1BlockNumber < currentL1BlockNumber) {
this.l1Timestamp = (await this.publicClient.getBlock({ blockNumber: currentL1BlockNumber })).timestamp;
this.l1BlockNumber = currentL1BlockNumber;
}

// ********** Events that are processed per L2 block **********
if (currentL1BlockNumber > blocksSynchedTo) {
// First we retrieve new L2 blocks
Expand All @@ -257,21 +264,17 @@ export class Archiver implements ArchiveSource {
// up to which point we're pruning, and then requesting L2 blocks up to that point only.
await this.handleEpochPrune(provenBlockNumber, currentL1BlockNumber);
}

// Store latest l1 block number and timestamp seen. Used for epoch and slots calculations.
if (!this.l1BlockNumber || this.l1BlockNumber < currentL1BlockNumber) {
this.l1Timestamp = await this.publicClient.getBlock({ blockNumber: currentL1BlockNumber }).then(b => b.timestamp);
this.l1BlockNumber = currentL1BlockNumber;
}
}

/** Checks if there'd be a reorg for the next block submission and start pruning now. */
private async handleEpochPrune(provenBlockNumber: bigint, currentL1BlockNumber: bigint) {
const localPendingBlockNumber = BigInt(await this.getBlockNumber());

const time = (this.l1Timestamp ?? 0n) + BigInt(ETHEREUM_SLOT_DURATION);

const canPrune =
localPendingBlockNumber > provenBlockNumber &&
(await this.rollup.read.canPrune({ blockNumber: currentL1BlockNumber }));
(await this.rollup.read.canPruneAtTime([time], { blockNumber: currentL1BlockNumber }));

if (canPrune) {
this.log.verbose(`L2 prune will occur on next submission. Rolling back to last proven block.`);
Expand Down

0 comments on commit b4718d4

Please sign in to comment.