diff --git a/l1-contracts/src/core/Rollup.sol b/l1-contracts/src/core/Rollup.sol index fb1c72b14e48..841943579620 100644 --- a/l1-contracts/src/core/Rollup.sol +++ b/l1-contracts/src/core/Rollup.sol @@ -11,6 +11,7 @@ import { EnumerableSet } from "@aztec/core/interfaces/IStaking.sol"; import {IValidatorSelection} from "@aztec/core/interfaces/IValidatorSelection.sol"; +import {FeeAssetValue} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; // We allow the unused imports here as they make it much simpler to import the Rollup later // solhint-disable no-unused-import @@ -39,7 +40,6 @@ import { EpochRewards, FeeAssetPerEthE9, EthValue, - FeeAssetValue, PriceLib } from "./RollupCore.sol"; // solhint-enable no-unused-import diff --git a/l1-contracts/src/core/RollupCore.sol b/l1-contracts/src/core/RollupCore.sol index a22d2094a289..66218c04d892 100644 --- a/l1-contracts/src/core/RollupCore.sol +++ b/l1-contracts/src/core/RollupCore.sol @@ -32,12 +32,7 @@ import { ValidateHeaderArgs, Header } from "@aztec/core/libraries/RollupLibs/ExtRollupLib.sol"; -import { - EthValue, - FeeAssetValue, - FeeAssetPerEthE9, - PriceLib -} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; +import {EthValue, FeeAssetPerEthE9, PriceLib} from "@aztec/core/libraries/RollupLibs/FeeMath.sol"; import {IntRollupLib} from "@aztec/core/libraries/RollupLibs/IntRollupLib.sol"; import {ProposeArgs, ProposeLib} from "@aztec/core/libraries/RollupLibs/ProposeLib.sol"; import {StakingLib} from "@aztec/core/libraries/staking/StakingLib.sol"; @@ -175,7 +170,13 @@ contract RollupCore is // Genesis block rollupStore.blocks[0] = BlockLog({ - feeHeader: FeeHeader({excessMana: 0, feeAssetPriceNumerator: 0, manaUsed: 0, congestionCost: 0}), + feeHeader: FeeHeader({ + excessMana: 0, + feeAssetPriceNumerator: 0, + manaUsed: 0, + congestionCost: 0, + provingCost: 0 + }), archive: _genesisArchiveRoot, blockHash: _genesisBlockHash, slotNumber: Slot.wrap(0) @@ -424,18 +425,7 @@ contract RollupCore is interim.totalBurn += interim.burn; // Compute the proving fee in the fee asset - { - // @todo likely better for us to store this if we can pack it better - interim.feeAssetPrice = IntRollupLib.getFeeAssetPerEth( - rollupStore.blocks[_args.start + i - 1].feeHeader.feeAssetPriceNumerator - ); - } - interim.proverFee = Math.min( - feeHeader.manaUsed - * FeeAssetValue.unwrap(rollupStore.provingCostPerMana.toFeeAsset(interim.feeAssetPrice)), - interim.fee - ); - + interim.proverFee = Math.min(feeHeader.manaUsed * feeHeader.provingCost, interim.fee); interim.fee -= interim.proverFee; er.rewards += interim.proverFee; @@ -498,6 +488,11 @@ contract RollupCore is // Decode and validate header Header memory header = ExtRollupLib.decodeHeader(_args.header); + // @todo As part of a refactor of the core for propose and submit, we should + // be able to set it up such that we don't need compute the fee components + // unless needed. + // Would be part of joining the header validation. + setupEpoch(); ManaBaseFeeComponents memory components = getManaBaseFeeComponentsAt(Timestamp.wrap(block.timestamp), true); @@ -515,7 +510,9 @@ contract RollupCore is uint256 blockNumber = ++rollupStore.tips.pendingBlockNumber; { - rollupStore.blocks[blockNumber] = _toBlockLog(_args, blockNumber, components.congestionCost); + // @note The components are measured in the fee asset. + rollupStore.blocks[blockNumber] = + _toBlockLog(_args, blockNumber, components.congestionCost, components.provingCost); } rollupStore.blobPublicInputsHashes[blockNumber] = blobPublicInputsHash; @@ -729,11 +726,12 @@ contract RollupCore is } // Helper to avoid stack too deep - function _toBlockLog(ProposeArgs calldata _args, uint256 _blockNumber, uint256 _congestionCost) - internal - view - returns (BlockLog memory) - { + function _toBlockLog( + ProposeArgs calldata _args, + uint256 _blockNumber, + uint256 _congestionCost, + uint256 _provingCost + ) internal view returns (BlockLog memory) { FeeHeader memory parentFeeHeader = rollupStore.blocks[_blockNumber - 1].feeHeader; return BlockLog({ archive: _args.archive, @@ -745,7 +743,8 @@ contract RollupCore is _args.oracleInput.feeAssetPriceModifier ), manaUsed: uint256(bytes32(_args.header[0x0268:0x0288])), - congestionCost: _congestionCost + congestionCost: _congestionCost, + provingCost: _provingCost }) }); } diff --git a/l1-contracts/src/core/libraries/RollupLibs/FeeMath.sol b/l1-contracts/src/core/libraries/RollupLibs/FeeMath.sol index 282270547302..a4e1b3e4b4c9 100644 --- a/l1-contracts/src/core/libraries/RollupLibs/FeeMath.sol +++ b/l1-contracts/src/core/libraries/RollupLibs/FeeMath.sol @@ -37,9 +37,10 @@ struct ManaBaseFeeComponents { struct FeeHeader { uint256 excessMana; - uint256 feeAssetPriceNumerator; uint256 manaUsed; + uint256 feeAssetPriceNumerator; uint256 congestionCost; + uint256 provingCost; } struct L1FeeData { diff --git a/l1-contracts/test/Rollup.t.sol b/l1-contracts/test/Rollup.t.sol index 3cb461359abc..9a5346fc8ea9 100644 --- a/l1-contracts/test/Rollup.t.sol +++ b/l1-contracts/test/Rollup.t.sol @@ -346,6 +346,28 @@ contract RollupTest is RollupBase { rollup.propose(args, signatures, data.body, data.blobInputs); } + function testProvingFeeUpdates() public setUpFor("mixed_block_1") { + rollup.setProvingCostPerMana(EthValue.wrap(1000)); + _proposeBlock("mixed_block_1", 1, 1e6); + + rollup.setProvingCostPerMana(EthValue.wrap(2000)); + _proposeBlock("mixed_block_2", 2, 1e6); + + // At this point in time, we have had different proving costs for the two blocks. When we prove them + // in the same epoch, we want to see that the correct fee is taken for each block. + _proveBlocks("mixed_block_", 1, 2, address(this)); + + // 1e6 mana at 1000 and 2000 cost per manage multiplied by 10 for the price conversion to fee asset. + uint256 provingFees = 1e6 * (1000 + 2000) * 10; + uint256 expectedProverRewards = rewardDistributor.BLOCK_REWARD() / 2 * 2 + provingFees; + + assertEq( + rollup.getCollectiveProverRewardsForEpoch(Epoch.wrap(0)), + expectedProverRewards, + "invalid prover rewards" + ); + } + struct TestBlockFeeStruct { EthValue provingCostPerManaInEth; FeeAssetValue provingCostPerManaInFeeAsset; diff --git a/l1-contracts/test/fees/MinimalFeeModel.sol b/l1-contracts/test/fees/MinimalFeeModel.sol index 344f933f9f24..ad1d6b107fbf 100644 --- a/l1-contracts/test/fees/MinimalFeeModel.sol +++ b/l1-contracts/test/fees/MinimalFeeModel.sol @@ -56,7 +56,7 @@ contract MinimalFeeModel { EthValue public provingCost = EthValue.wrap(100); constructor(uint256 _slotDuration, uint256 _epochDuration) { - feeHeaders[0] = FeeHeader(0, 0, 0, 0); + feeHeaders[0] = FeeHeader(0, 0, 0, 0, 0); l1BaseFees.pre = L1FeesModel({base_fee: 1 gwei, blob_fee: 1}); l1BaseFees.post = L1FeesModel({base_fee: block.basefee, blob_fee: _getBlobBaseFee()}); @@ -127,7 +127,8 @@ contract MinimalFeeModel { ), manaUsed: _manaUsed, excessMana: excessMana, - congestionCost: 0 + congestionCost: 0, + provingCost: 0 }); } diff --git a/yarn-project/aztec.js/src/api/ethereum/cheat_codes.ts b/yarn-project/aztec.js/src/api/ethereum/cheat_codes.ts index 388ab57f0959..c51d0389ed73 100644 --- a/yarn-project/aztec.js/src/api/ethereum/cheat_codes.ts +++ b/yarn-project/aztec.js/src/api/ethereum/cheat_codes.ts @@ -143,7 +143,7 @@ export class RollupCheatCodes { const tipsAfter = await this.getTips(); this.logger.info( - `Proven tip moved: ${tipsBefore.proven} -> ${tipsAfter.proven}. Pending tip moved: ${tipsBefore.pending} -> ${tipsAfter.pending}`, + `Proven tip moved: ${tipsBefore.proven} -> ${tipsAfter.proven}. Pending tip: ${tipsAfter.pending}.`, ); }