Skip to content

Commit

Permalink
feat: update rollup storage to hold pending/proven tips (#8583)
Browse files Browse the repository at this point in the history
Update the rollup state to hold pending and proven tips, which
themselves are structs with block/slot numbers, instead of
proven/pending counts.

---------

Co-authored-by: Lasse Herskind <[email protected]>
  • Loading branch information
just-mitch and LHerskind authored Sep 17, 2024
1 parent 25d75ff commit 38e3051
Show file tree
Hide file tree
Showing 25 changed files with 167 additions and 151 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/devnet-deploys.yml
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,7 @@ jobs:
# working-directory: ./yarn-project/aztec/terraform/pxe
# run: |
# set -eo pipefail
# docker run aztecprotocol/aztec:${{ env.DEPLOY_TAG }} set-proven-until \
# docker run aztecprotocol/aztec:${{ env.DEPLOY_TAG }} set-proven-through \
# --rpc-url https://api.aztec.network/${{ env.DEPLOY_TAG }}/aztec-pxe/${{ env.API_KEY }} \
# --l1-rpc-url https://${{ env.DEPLOY_TAG }}-mainnet-fork.aztec.network:8545/admin-${{ env.FORK_ADMIN_API_KEY }} \
# --l1-chain-id ${{ env.L1_CHAIN_ID }} \
Expand Down
4 changes: 2 additions & 2 deletions docker-compose.provernet.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ services:
SEQ_PUBLISHER_PRIVATE_KEY: "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a"
VALIDATOR_PRIVATE_KEY: "0x47e179ec197488593b187f80a00eb0da91f1b9d0b13f8733639f19c30a34926a"
PROVER_REAL_PROOFS: "${PROVER_REAL_PROOFS:-false}"
ASSUME_PROVEN_UNTIL_BLOCK_NUMBER: "${ASSUME_PROVEN_UNTIL_BLOCK_NUMBER:-4}"
ASSUME_PROVEN_THROUGH_BLOCK_NUMBER: "${ASSUME_PROVEN_THROUGH_BLOCK_NUMBER:-4}"
P2P_ENABLED: false
IS_DEV_NET: true
volumes:
Expand Down Expand Up @@ -104,7 +104,7 @@ services:
PROVER_AGENT_ENABLED: false
PROVER_PUBLISHER_PRIVATE_KEY: "0xdbda1821b80551c9d65939329250298aa3472ba22feea921c0cf5d620ea67b97"
PROVER_REAL_PROOFS: "${PROVER_REAL_PROOFS:-false}"
ASSUME_PROVEN_UNTIL_BLOCK_NUMBER: "${ASSUME_PROVEN_UNTIL_BLOCK_NUMBER:-4}"
ASSUME_PROVEN_THROUGH_BLOCK_NUMBER: "${ASSUME_PROVEN_THROUGH_BLOCK_NUMBER:-4}"
IS_DEV_NET: true
volumes:
- ./log/aztec-prover/:/usr/src/yarn-project/aztec/log:rw
Expand Down
114 changes: 61 additions & 53 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ import {Leonidas} from "./sequencer_selection/Leonidas.sol";
contract Rollup is Leonidas, IRollup, ITestRollup {
using SafeCast for uint256;

struct ChainTips {
uint256 pendingBlockNumber;
uint256 provenBlockNumber;
}

struct BlockLog {
bytes32 archive;
bytes32 blockHash;
Expand All @@ -52,11 +57,9 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
IOutbox public immutable OUTBOX;
uint256 public immutable VERSION;
IFeeJuicePortal public immutable FEE_JUICE_PORTAL;

IVerifier public verifier;

uint256 public pendingBlockCount;
uint256 public provenBlockCount;
ChainTips public tips;

// @todo Validate assumption:
// Currently we assume that the archive root following a block is specific to the block
Expand All @@ -67,9 +70,9 @@ contract Rollup is Leonidas, IRollup, ITestRollup {

bytes32 public vkTreeRoot;

// @note Assume that all blocks up to this value are automatically proven. Speeds up bootstrapping.
// @note Assume that all blocks up to this value (inclusive) are automatically proven. Speeds up bootstrapping.
// Testing only. This should be removed eventually.
uint256 private assumeProvenUntilBlockNumber;
uint256 private assumeProvenThroughBlockNumber;

constructor(
IRegistry _registry,
Expand All @@ -93,9 +96,6 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
blockHash: bytes32(0),
slotNumber: 0
});
pendingBlockCount = 1;
provenBlockCount = 1;

for (uint256 i = 0; i < _validators.length; i++) {
_addValidator(_validators[i]);
}
Expand All @@ -110,11 +110,11 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
* @dev While in devnet, this will be guarded behind an `onlyOwner`
*/
function prune() external override(IRollup) onlyOwner {
if (pendingBlockCount == provenBlockCount) {
if (tips.pendingBlockNumber == tips.provenBlockNumber) {
revert Errors.Rollup__NothingToPrune();
}

BlockLog storage firstPendingNotInProven = blocks[provenBlockCount];
BlockLog storage firstPendingNotInProven = blocks[tips.provenBlockNumber + 1];
uint256 prunableAtSlot =
uint256(firstPendingNotInProven.slotNumber) + TIMELINESS_PROVING_IN_SLOTS;
uint256 currentSlot = getCurrentSlot();
Expand All @@ -123,29 +123,30 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
revert Errors.Rollup__NotReadyToPrune(currentSlot, prunableAtSlot);
}

// @note We are not deleting the blocks, but we are "winding back" the pendingBlockCount
// to the last block that was proven.
// The reason we can do this, is that any new block proposed will overwrite a previous block
// so no values should "survive". It it is however slightly odd for people reading
// the chain separately from the contract without using pendingBlockCount as a boundary.
pendingBlockCount = provenBlockCount;
uint256 pending = tips.pendingBlockNumber;

emit PrunedPending(provenBlockCount, pendingBlockCount);
// @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);
}

/**
* Sets the assumeProvenUntilBlockNumber. Only the contract deployer can set it.
* Sets the assumeProvenThroughBlockNumber. Only the contract deployer can set it.
* @param blockNumber - New value.
*/
function setAssumeProvenUntilBlockNumber(uint256 blockNumber)
function setAssumeProvenThroughBlockNumber(uint256 blockNumber)
external
override(ITestRollup)
onlyOwner
{
if (blockNumber > provenBlockCount && blockNumber <= pendingBlockCount) {
provenBlockCount = blockNumber;
if (blockNumber > tips.provenBlockNumber && blockNumber <= tips.pendingBlockNumber) {
tips.provenBlockNumber = blockNumber;
}
assumeProvenUntilBlockNumber = blockNumber;
assumeProvenThroughBlockNumber = blockNumber;
}

/**
Expand All @@ -170,15 +171,6 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
vkTreeRoot = _vkTreeRoot;
}

function computeTxsEffectsHash(bytes calldata _body)
external
pure
override(IRollup)
returns (bytes32)
{
return TxsDecoder.decode(_body);
}

/**
* @notice Publishes the body and propose the block
* @dev `eth_log_handlers` rely on this function
Expand Down Expand Up @@ -213,14 +205,16 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
_flags: DataStructures.ExecutionFlags({ignoreDA: false, ignoreSignatures: false})
});

blocks[pendingBlockCount++] = BlockLog({
uint256 blockNumber = ++tips.pendingBlockNumber;

blocks[blockNumber] = BlockLog({
archive: _archive,
blockHash: _blockHash,
slotNumber: header.globalVariables.slotNumber.toUint128()
});

// @note The block number here will always be >=1 as the genesis block is at 0
bytes32 inHash = INBOX.consume(header.globalVariables.blockNumber);
bytes32 inHash = INBOX.consume(blockNumber);
if (header.contentCommitment.inHash != inHash) {
revert Errors.Rollup__InvalidInHash(inHash, header.contentCommitment.inHash);
}
Expand All @@ -229,15 +223,13 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
// Min size = smallest path of the rollup tree + 1
(uint256 min,) = MerkleLib.computeMinMaxPathLength(header.contentCommitment.numTxs);
uint256 l2ToL1TreeMinHeight = min + 1;
OUTBOX.insert(
header.globalVariables.blockNumber, header.contentCommitment.outHash, l2ToL1TreeMinHeight
);
OUTBOX.insert(blockNumber, header.contentCommitment.outHash, l2ToL1TreeMinHeight);

emit L2BlockProposed(header.globalVariables.blockNumber);
emit L2BlockProposed(blockNumber);

// Automatically flag the block as proven if we have cheated and set assumeProvenUntilBlockNumber.
if (header.globalVariables.blockNumber < assumeProvenUntilBlockNumber) {
provenBlockCount += 1;
// Automatically flag the block as proven if we have cheated and set assumeProvenThroughBlockNumber.
if (blockNumber <= assumeProvenThroughBlockNumber) {
tips.provenBlockNumber = blockNumber;

if (header.globalVariables.coinbase != address(0) && header.totalFees > 0) {
// @note This will currently fail if there are insufficient funds in the bridge
Expand All @@ -246,7 +238,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
FEE_JUICE_PORTAL.distributeFees(header.globalVariables.coinbase, header.totalFees);
}

emit L2ProofVerified(header.globalVariables.blockNumber, "CHEAT");
emit L2ProofVerified(blockNumber, "CHEAT");
}
}

Expand Down Expand Up @@ -283,20 +275,19 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
) external override(IRollup) {
HeaderLib.Header memory header = HeaderLib.decode(_header);

if (header.globalVariables.blockNumber >= pendingBlockCount) {
if (header.globalVariables.blockNumber > tips.pendingBlockNumber) {
revert Errors.Rollup__TryingToProveNonExistingBlock();
}

// @note This implicitly also ensures that we have not already proven, since
// the value `provenBlockCount` is incremented at the end of this function
if (header.globalVariables.blockNumber != provenBlockCount) {
// the value `tips.provenBlockNumber` is incremented at the end of this function
if (header.globalVariables.blockNumber != tips.provenBlockNumber + 1) {
revert Errors.Rollup__NonSequentialProving();
}

bytes32 expectedLastArchive = blocks[header.globalVariables.blockNumber - 1].archive;
// We do it this way to provide better error messages than passing along the storage values
// TODO(#4148) Proper genesis state. If the state is empty, we allow anything for now.
if (expectedLastArchive != bytes32(0) && header.lastArchive.root != expectedLastArchive) {
if (header.lastArchive.root != expectedLastArchive) {
revert Errors.Rollup__InvalidArchive(expectedLastArchive, header.lastArchive.root);
}

Expand Down Expand Up @@ -375,7 +366,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
revert Errors.Rollup__InvalidProof();
}

provenBlockCount += 1;
tips.provenBlockNumber = header.globalVariables.blockNumber;

for (uint256 i = 0; i < 32; i++) {
address coinbase = address(uint160(uint256(publicInputs[25 + i * 2])));
Expand Down Expand Up @@ -419,7 +410,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
{
uint256 slot = getSlotAt(_ts);

uint256 lastSlot = uint256(blocks[pendingBlockCount - 1].slotNumber);
uint256 lastSlot = uint256(blocks[tips.pendingBlockNumber].slotNumber);
if (slot <= lastSlot) {
revert Errors.Rollup__SlotAlreadyInChain(lastSlot, slot);
}
Expand All @@ -435,7 +426,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
DataStructures.ExecutionFlags({ignoreDA: true, ignoreSignatures: true});
_validateLeonidas(slot, sigs, _archive, flags);

return (slot, pendingBlockCount);
return (slot, tips.pendingBlockNumber + 1);
}

/**
Expand All @@ -462,13 +453,30 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
_validateHeader(header, _signatures, _digest, _currentTime, _txsEffectsHash, _flags);
}

function computeTxsEffectsHash(bytes calldata _body)
external
pure
override(IRollup)
returns (bytes32)
{
return TxsDecoder.decode(_body);
}

/**
* @notice Get the current archive root
*
* @return bytes32 - The current archive root
*/
function archive() public view override(IRollup) returns (bytes32) {
return blocks[pendingBlockCount - 1].archive;
return blocks[tips.pendingBlockNumber].archive;
}

function getProvenBlockNumber() public view override(IRollup) returns (uint256) {
return tips.provenBlockNumber;
}

function getPendingBlockNumber() public view override(IRollup) returns (uint256) {
return tips.pendingBlockNumber;
}

/**
Expand Down Expand Up @@ -569,9 +577,9 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
revert Errors.Rollup__InvalidVersion(VERSION, _header.globalVariables.version);
}

if (_header.globalVariables.blockNumber != pendingBlockCount) {
if (_header.globalVariables.blockNumber != tips.pendingBlockNumber + 1) {
revert Errors.Rollup__InvalidBlockNumber(
pendingBlockCount, _header.globalVariables.blockNumber
tips.pendingBlockNumber + 1, _header.globalVariables.blockNumber
);
}

Expand All @@ -585,7 +593,7 @@ contract Rollup is Leonidas, IRollup, ITestRollup {
revert Errors.Rollup__SlotValueTooLarge(slot);
}

uint256 lastSlot = uint256(blocks[pendingBlockCount - 1].slotNumber);
uint256 lastSlot = uint256(blocks[tips.pendingBlockNumber].slotNumber);
if (slot <= lastSlot) {
revert Errors.Rollup__SlotAlreadyInChain(lastSlot, slot);
}
Expand Down
38 changes: 20 additions & 18 deletions l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,32 +11,16 @@ import {DataStructures} from "../libraries/DataStructures.sol";
interface ITestRollup {
function setVerifier(address _verifier) external;
function setVkTreeRoot(bytes32 _vkTreeRoot) external;
function setAssumeProvenUntilBlockNumber(uint256 blockNumber) external;
function setAssumeProvenThroughBlockNumber(uint256 blockNumber) external;
}

interface IRollup {
event L2BlockProposed(uint256 indexed blockNumber);
event L2ProofVerified(uint256 indexed blockNumber, bytes32 indexed proverId);
event PrunedPending(uint256 provenBlockCount, uint256 pendingBlockCount);

function canProposeAtTime(uint256 _ts, bytes32 _archive) external view returns (uint256, uint256);
function validateHeader(
bytes calldata _header,
SignatureLib.Signature[] memory _signatures,
bytes32 _digest,
uint256 _currentTime,
bytes32 _txsEffecstHash,
DataStructures.ExecutionFlags memory _flags
) external view;
event PrunedPending(uint256 provenBlockNumber, uint256 pendingBlockNumber);

function prune() external;

function INBOX() external view returns (IInbox);

function OUTBOX() external view returns (IOutbox);

function L1_BLOCK_AT_GENESIS() external view returns (uint256);

function propose(
bytes calldata _header,
bytes32 _archive,
Expand All @@ -54,6 +38,22 @@ interface IRollup {
bytes calldata _proof
) external;

function canProposeAtTime(uint256 _ts, bytes32 _archive) external view returns (uint256, uint256);
function validateHeader(
bytes calldata _header,
SignatureLib.Signature[] memory _signatures,
bytes32 _digest,
uint256 _currentTime,
bytes32 _txsEffecstHash,
DataStructures.ExecutionFlags memory _flags
) external view;

function INBOX() external view returns (IInbox);

function OUTBOX() external view returns (IOutbox);

function L1_BLOCK_AT_GENESIS() external view returns (uint256);

// TODO(#7346): Integrate batch rollups
// function submitRootProof(
// bytes32 _previousArchive,
Expand All @@ -68,5 +68,7 @@ interface IRollup {

function archive() external view returns (bytes32);
function archiveAt(uint256 _blockNumber) external view returns (bytes32);
function getProvenBlockNumber() external view returns (uint256);
function getPendingBlockNumber() external view returns (uint256);
function computeTxsEffectsHash(bytes calldata _body) external pure returns (bytes32);
}
4 changes: 2 additions & 2 deletions l1-contracts/src/core/messagebridge/Outbox.sol
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ contract Outbox is IOutbox {
uint256 _leafIndex,
bytes32[] calldata _path
) external override(IOutbox) {
if (_l2BlockNumber >= ROLLUP.provenBlockCount()) {
if (_l2BlockNumber > ROLLUP.getProvenBlockNumber()) {
revert Errors.Outbox__BlockNotProven(_l2BlockNumber);
}

Expand Down Expand Up @@ -157,7 +157,7 @@ contract Outbox is IOutbox {
override(IOutbox)
returns (bytes32 root, uint256 minHeight)
{
if (_l2BlockNumber >= ROLLUP.provenBlockCount()) {
if (_l2BlockNumber > ROLLUP.getProvenBlockNumber()) {
return (bytes32(0), 0);
}
RootData storage rootData = roots[_l2BlockNumber];
Expand Down
Loading

0 comments on commit 38e3051

Please sign in to comment.