Skip to content
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
111 changes: 72 additions & 39 deletions l1-contracts/src/core/Rollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ import {
FeeHeader,
ManaBaseFeeComponents,
BlockLog,
L1FeeData
L1FeeData,
SubmitEpochRootProofArgs
} from "@aztec/core/interfaces/IRollup.sol";
import {IVerifier} from "@aztec/core/interfaces/IVerifier.sol";
import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol";
Expand Down Expand Up @@ -51,6 +52,13 @@ struct Config {
uint256 aztecEpochProofClaimWindowInL2Slots;
}

struct SubmitEpochRootProofInterimValues {
uint256 previousBlockNumber;
uint256 endBlockNumber;
Epoch epochToProve;
Epoch startEpoch;
}

/**
* @title Rollup
* @author Aztec Labs
Expand All @@ -64,6 +72,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
using SafeERC20 for IERC20;
using ProposeLib for ProposeArgs;
using FeeMath for uint256;
using FeeMath for ManaBaseFeeComponents;

struct L1GasOracleValues {
L1FeeData pre;
Expand All @@ -81,6 +90,10 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
// for justification of CLAIM_DURATION_IN_L2_SLOTS.
uint256 public constant PROOF_COMMITMENT_MIN_BOND_AMOUNT_IN_TST = 1000;

// A Cuauhxicalli [kʷaːʍʃiˈkalːi] ("eagle gourd bowl") is a ceremonial Aztec vessel or altar used to hold offerings,
// such as sacrificial hearts, during rituals performed within temples.
address public constant CUAUHXICALLI = address(bytes20("CUAUHXICALLI"));
Copy link
Member

Choose a reason for hiding this comment

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

my minds tellin me noooooo, but my body, my body'ss tellin me yeessss


address public constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
bool public immutable IS_FOUNDRY_TEST;

Expand Down Expand Up @@ -154,7 +167,8 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
excessMana: 0,
feeAssetPriceNumerator: 0,
manaUsed: 0,
provingCostPerManaNumerator: 0
provingCostPerManaNumerator: 0,
congestionCost: 0
}),
archive: bytes32(Constants.GENESIS_ARCHIVE_ROOT),
blockHash: bytes32(0), // TODO(palla/prover): The first block does not have hash zero
Expand Down Expand Up @@ -263,55 +277,71 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
* @dev We provide the `_archive` and `_blockHash` even if it could be read from storage itself because it allow for
* better error messages. Without passing it, we would just have a proof verification failure.
*
* @param _epochSize - The size of the epoch (to be promoted to a constant)
* @param _args - Array of public inputs to the proof (previousArchive, endArchive, previousBlockHash, endBlockHash, endTimestamp, outHash, proverId)
* @param _fees - Array of recipient-value pairs with fees to be distributed for the epoch
* @param _aggregationObject - The aggregation object for the proof
* @param _proof - The proof to verify
* @param _args - The arguments to submit the epoch root proof:
* _epochSize - The size of the epoch (to be promoted to a constant)
* _args - Array of public inputs to the proof (previousArchive, endArchive, previousBlockHash, endBlockHash, endTimestamp, outHash, proverId)
* _fees - Array of recipient-value pairs with fees to be distributed for the epoch
* _aggregationObject - The aggregation object for the proof
* _proof - The proof to verify
*/
function submitEpochRootProof(
uint256 _epochSize,
bytes32[7] calldata _args,
bytes32[] calldata _fees,
bytes calldata _aggregationObject,
bytes calldata _proof
) external override(IRollup) {
function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) external override(IRollup) {
if (canPrune()) {
_prune();
}

uint256 previousBlockNumber = tips.provenBlockNumber;
uint256 endBlockNumber = previousBlockNumber + _epochSize;
SubmitEpochRootProofInterimValues memory interimValues;

interimValues.previousBlockNumber = tips.provenBlockNumber;
interimValues.endBlockNumber = interimValues.previousBlockNumber + _args.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);
interimValues.epochToProve = getEpochForBlock(interimValues.endBlockNumber);
interimValues.startEpoch = getEpochForBlock(interimValues.previousBlockNumber + 1);

// Ensure that the proof is not across epochs
require(
interimValues.startEpoch == interimValues.epochToProve,
Errors.Rollup__InvalidEpoch(interimValues.startEpoch, interimValues.epochToProve)
);
Copy link
Member

Choose a reason for hiding this comment

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

interimValues.epochToProve must equal proofClaim.epochToProve, i may be missing where this assertion is

Copy link
Member

Choose a reason for hiding this comment

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

Ok the assertion is at the bottom, they dont get their bond back if they do not prove the epoch in the claim

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We don't have that assertion, it is an if for freeing bond. Will move it up to make it easier to follow 🫡


bytes32[] memory publicInputs =
getEpochProofPublicInputs(_epochSize, _args, _fees, _aggregationObject);
getEpochProofPublicInputs(_args.epochSize, _args.args, _args.fees, _args.aggregationObject);

require(epochProofVerifier.verify(_args.proof, publicInputs), Errors.Rollup__InvalidProof());

require(epochProofVerifier.verify(_proof, publicInputs), Errors.Rollup__InvalidProof());
if (proofClaim.epochToProve == interimValues.epochToProve) {
PROOF_COMMITMENT_ESCROW.unstakeBond(proofClaim.bondProvider, proofClaim.bondAmount);
}

tips.provenBlockNumber = endBlockNumber;
tips.provenBlockNumber = interimValues.endBlockNumber;

// @note Only if the rollup is the canonical will it be able to meaningfully claim fees
// Otherwise, the fees are unbacked #7938.
bool isFeeCanonical = address(this) == FEE_JUICE_PORTAL.canonicalRollup();
bool isRewardDistributorCanonical = address(this) == REWARD_DISTRIBUTOR.canonicalRollup();

uint256 totalProverReward = 0;
uint256 totalBurn = 0;

if (isFeeCanonical || isRewardDistributorCanonical) {
for (uint256 i = 0; i < _epochSize; i++) {
for (uint256 i = 0; i < _args.epochSize; i++) {
address coinbase = address(uint160(uint256(publicInputs[9 + i * 2])));
uint256 reward = 0;
uint256 toProver = 0;
uint256 burn = 0;

if (isFeeCanonical) {
uint256 fees = uint256(publicInputs[10 + i * 2]);
if (fees > 0) {
reward += fees;
// This is insanely expensive, and will be fixed as part of the general storage cost reduction.
// See #9826.
FeeHeader storage feeHeader =
blocks[interimValues.previousBlockNumber + 1 + i].feeHeader;
burn += feeHeader.congestionCost * feeHeader.manaUsed;

reward += (fees - burn);
FEE_JUICE_PORTAL.distributeFees(address(this), fees);
}
}
Expand All @@ -335,6 +365,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
}

totalProverReward += toProver;
totalBurn += burn;
}

if (totalProverReward > 0) {
Expand All @@ -343,13 +374,13 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
proofClaim.bondProvider == address(0) ? msg.sender : proofClaim.bondProvider;
ASSET.safeTransfer(proofRewardRecipient, totalProverReward);
}
}

if (proofClaim.epochToProve == epochToProve) {
PROOF_COMMITMENT_ESCROW.unstakeBond(proofClaim.bondProvider, proofClaim.bondAmount);
if (totalBurn > 0) {
ASSET.safeTransfer(CUAUHXICALLI, totalBurn);
}
}

emit L2ProofVerified(endBlockNumber, _args[6]);
emit L2ProofVerified(interimValues.endBlockNumber, _args.args[6]);
}

function status(uint256 _myHeaderBlockNumber)
Expand Down Expand Up @@ -520,13 +551,13 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
// Decode and validate header
HeaderLib.Header memory header = HeaderLib.decode(_args.header);

bytes32 digest = _args.digest();
setupEpoch();
uint256 manaBaseFee = getManaBaseFee(true);
ManaBaseFeeComponents memory components = getManaBaseFeeComponents(true);
uint256 manaBaseFee = FeeMath.summedBaseFee(components);
_validateHeader({
_header: header,
_signatures: _signatures,
_digest: digest,
_digest: _args.digest(),
_currentTime: Timestamp.wrap(block.timestamp),
_manaBaseFee: manaBaseFee,
_txEffectsHash: txsEffectsHash,
Expand All @@ -553,17 +584,20 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
manaUsed: header.totalManaUsed,
provingCostPerManaNumerator: parentFeeHeader.provingCostPerManaNumerator.clampedAdd(
_args.oracleInput.provingCostModifier
)
),
congestionCost: components.congestionCost
})
});
}

// @note The block number here will always be >=1 as the genesis block is at 0
bytes32 inHash = INBOX.consume(blockNumber);
require(
header.contentCommitment.inHash == inHash,
Errors.Rollup__InvalidInHash(inHash, header.contentCommitment.inHash)
);
{
bytes32 inHash = INBOX.consume(blockNumber);
require(
header.contentCommitment.inHash == inHash,
Errors.Rollup__InvalidInHash(inHash, header.contentCommitment.inHash)
);
}

// TODO(#7218): Revert to fixed height tree for outbox, currently just providing min as interim
// Min size = smallest path of the rollup tree + 1
Expand Down Expand Up @@ -644,9 +678,8 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
* @return The mana base fee
*/
function getManaBaseFee(bool _inFeeAsset) public view override(IRollup) returns (uint256) {
ManaBaseFeeComponents memory components = manaBaseFeeComponents(_inFeeAsset);
return
components.dataCost + components.gasCost + components.provingCost + components.congestionCost;
ManaBaseFeeComponents memory components = getManaBaseFeeComponents(_inFeeAsset);
return components.summedBaseFee();
}

/**
Expand All @@ -661,7 +694,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
*
* @return The mana base fee components
*/
function manaBaseFeeComponents(bool _inFeeAsset)
function getManaBaseFeeComponents(bool _inFeeAsset)
public
view
override(ITestRollup)
Expand Down
28 changes: 12 additions & 16 deletions l1-contracts/src/core/interfaces/IRollup.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,24 @@ import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol";
import {SignatureLib} from "@aztec/core/libraries/crypto/SignatureLib.sol";
import {DataStructures} from "@aztec/core/libraries/DataStructures.sol";
import {EpochProofQuoteLib} from "@aztec/core/libraries/EpochProofQuoteLib.sol";
import {ManaBaseFeeComponents} from "@aztec/core/libraries/FeeMath.sol";
import {ProposeArgs} from "@aztec/core/libraries/ProposeLib.sol";
import {Timestamp, Slot, Epoch} from "@aztec/core/libraries/TimeMath.sol";

struct SubmitEpochRootProofArgs {
uint256 epochSize;
bytes32[7] args;
bytes32[] fees;
bytes aggregationObject;
bytes proof;
}

struct FeeHeader {
uint256 excessMana;
uint256 feeAssetPriceNumerator;
uint256 manaUsed;
uint256 provingCostPerManaNumerator;
uint256 congestionCost;
}

struct BlockLog {
Expand All @@ -29,20 +39,12 @@ struct L1FeeData {
uint256 blobFee;
}

struct ManaBaseFeeComponents {
uint256 congestionCost;
uint256 congestionMultiplier;
uint256 dataCost;
uint256 gasCost;
uint256 provingCost;
}

interface ITestRollup {
function setEpochVerifier(address _verifier) external;
function setVkTreeRoot(bytes32 _vkTreeRoot) external;
function setProtocolContractTreeRoot(bytes32 _protocolContractTreeRoot) external;
function setAssumeProvenThroughBlockNumber(uint256 _blockNumber) external;
function manaBaseFeeComponents(bool _inFeeAsset)
function getManaBaseFeeComponents(bool _inFeeAsset)
external
view
returns (ManaBaseFeeComponents memory);
Expand Down Expand Up @@ -78,13 +80,7 @@ interface IRollup {
EpochProofQuoteLib.SignedEpochProofQuote calldata _quote
) external;

function submitEpochRootProof(
uint256 _epochSize,
bytes32[7] calldata _args,
bytes32[] calldata _fees,
bytes calldata _aggregationObject,
bytes calldata _proof
) external;
function submitEpochRootProof(SubmitEpochRootProofArgs calldata _args) external;

function canProposeAtTime(Timestamp _ts, bytes32 _archive) external view returns (Slot, uint256);

Expand Down
13 changes: 13 additions & 0 deletions l1-contracts/src/core/libraries/FeeMath.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ struct OracleInput {
int256 feeAssetPriceModifier;
}

struct ManaBaseFeeComponents {
uint256 congestionCost;
uint256 congestionMultiplier;
uint256 dataCost;
uint256 gasCost;
uint256 provingCost;
}

library FeeMath {
using Math for uint256;
using SafeCast for int256;
Expand Down Expand Up @@ -81,6 +89,11 @@ library FeeMath {
return fakeExponential(MINIMUM_CONGESTION_MULTIPLIER, _numerator, CONGESTION_UPDATE_FRACTION);
}

function summedBaseFee(ManaBaseFeeComponents memory _components) internal pure returns (uint256) {
return _components.dataCost + _components.gasCost + _components.provingCost
+ _components.congestionCost;
}

/**
* @notice An approximation of the exponential function: factor * e ** (numerator / denominator)
*
Expand Down
Loading