@@ -4,7 +4,14 @@ pragma solidity >=0.8.27;
4
4
5
5
import {IFeeJuicePortal} from "@aztec/core/interfaces/IFeeJuicePortal.sol " ;
6
6
import {IProofCommitmentEscrow} from "@aztec/core/interfaces/IProofCommitmentEscrow.sol " ;
7
- import {IRollup, ITestRollup} from "@aztec/core/interfaces/IRollup.sol " ;
7
+ import {
8
+ IRollup,
9
+ ITestRollup,
10
+ FeeHeader,
11
+ ManaBaseFeeComponents,
12
+ BlockLog,
13
+ L1FeeData
14
+ } from "@aztec/core/interfaces/IRollup.sol " ;
8
15
import {IVerifier} from "@aztec/core/interfaces/IVerifier.sol " ;
9
16
import {IInbox} from "@aztec/core/interfaces/messagebridge/IInbox.sol " ;
10
17
import {IOutbox} from "@aztec/core/interfaces/messagebridge/IOutbox.sol " ;
@@ -15,6 +22,7 @@ import {SignatureLib} from "@aztec/core/libraries/crypto/SignatureLib.sol";
15
22
import {DataStructures} from "@aztec/core/libraries/DataStructures.sol " ;
16
23
import {EpochProofQuoteLib} from "@aztec/core/libraries/EpochProofQuoteLib.sol " ;
17
24
import {Errors} from "@aztec/core/libraries/Errors.sol " ;
25
+ import {FeeMath} from "@aztec/core/libraries/FeeMath.sol " ;
18
26
import {HeaderLib} from "@aztec/core/libraries/HeaderLib.sol " ;
19
27
import {ProposeArgs, ProposeLib} from "@aztec/core/libraries/ProposeLib.sol " ;
20
28
import {Timestamp, Slot, Epoch, SlotLib, EpochLib} from "@aztec/core/libraries/TimeMath.sol " ;
@@ -29,6 +37,19 @@ import {SafeERC20} from "@oz/token/ERC20/utils/SafeERC20.sol";
29
37
import {EIP712} from "@oz/utils/cryptography/EIP712.sol " ;
30
38
import {Math} from "@oz/utils/math/Math.sol " ;
31
39
import {SafeCast} from "@oz/utils/math/SafeCast.sol " ;
40
+ import {Vm} from "forge-std/Vm.sol " ;
41
+
42
+ struct ChainTips {
43
+ uint256 pendingBlockNumber;
44
+ uint256 provenBlockNumber;
45
+ }
46
+
47
+ struct Config {
48
+ uint256 aztecSlotDuration;
49
+ uint256 aztecEpochDuration;
50
+ uint256 targetCommitteeSize;
51
+ uint256 aztecEpochProofClaimWindowInL2Slots;
52
+ }
32
53
33
54
/**
34
55
* @title Rollup
@@ -42,29 +63,27 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
42
63
using EpochLib for Epoch;
43
64
using SafeERC20 for IERC20 ;
44
65
using ProposeLib for ProposeArgs;
66
+ using FeeMath for uint256 ;
45
67
46
- struct ChainTips {
47
- uint256 pendingBlockNumber;
48
- uint256 provenBlockNumber;
68
+ struct L1GasOracleValues {
69
+ L1FeeData pre;
70
+ L1FeeData post;
71
+ Slot slotOfChange;
49
72
}
50
73
51
- struct BlockLog {
52
- bytes32 archive;
53
- bytes32 blockHash;
54
- Slot slotNumber;
55
- }
74
+ uint256 internal constant BLOB_GAS_PER_BLOB = 2 ** 17 ;
75
+ uint256 internal constant GAS_PER_BLOB_POINT_EVALUATION = 50_000 ;
56
76
57
- struct Config {
58
- uint256 aztecSlotDuration;
59
- uint256 aztecEpochDuration;
60
- uint256 targetCommitteeSize;
61
- uint256 aztecEpochProofClaimWindowInL2Slots;
62
- }
77
+ Slot public constant LIFETIME = Slot.wrap (5 );
78
+ Slot public constant LAG = Slot.wrap (2 );
63
79
64
80
// See https://github.com/AztecProtocol/engineering-designs/blob/main/in-progress/8401-proof-timeliness/proof-timeliness.ipynb
65
81
// for justification of CLAIM_DURATION_IN_L2_SLOTS.
66
82
uint256 public constant PROOF_COMMITMENT_MIN_BOND_AMOUNT_IN_TST = 1000 ;
67
83
84
+ address public constant VM_ADDRESS = address (uint160 (uint256 (keccak256 ("hevm cheat code " ))));
85
+ bool public immutable IS_FOUNDRY_TEST;
86
+
68
87
uint256 public immutable CLAIM_DURATION_IN_L2_SLOTS;
69
88
uint256 public immutable L1_BLOCK_AT_GENESIS;
70
89
IInbox public immutable INBOX;
@@ -85,7 +104,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
85
104
// e.g., changing any values in the block or header should in the end make its way to the archive
86
105
//
87
106
// More direct approach would be storing keccak256(header) as well
88
- mapping (uint256 blockNumber = > BlockLog log ) public blocks;
107
+ mapping (uint256 blockNumber = > BlockLog log ) internal blocks;
89
108
90
109
bytes32 public vkTreeRoot;
91
110
bytes32 public protocolContractTreeRoot;
@@ -94,6 +113,8 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
94
113
// Testing only. This should be removed eventually.
95
114
uint256 private assumeProvenThroughBlockNumber;
96
115
116
+ L1GasOracleValues public l1GasOracleValues;
117
+
97
118
constructor (
98
119
IFeeJuicePortal _fpcJuicePortal ,
99
120
IRewardDistributor _rewardDistributor ,
@@ -125,12 +146,25 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
125
146
L1_BLOCK_AT_GENESIS = block .number ;
126
147
CLAIM_DURATION_IN_L2_SLOTS = _config.aztecEpochProofClaimWindowInL2Slots;
127
148
149
+ IS_FOUNDRY_TEST = VM_ADDRESS.code.length > 0 ;
150
+
128
151
// Genesis block
129
152
blocks[0 ] = BlockLog ({
153
+ feeHeader: FeeHeader ({
154
+ excessMana: 0 ,
155
+ feeAssetPriceNumerator: 0 ,
156
+ manaUsed: 0 ,
157
+ provingCostPerManaNumerator: 0
158
+ }),
130
159
archive: bytes32 (Constants.GENESIS_ARCHIVE_ROOT),
131
160
blockHash: bytes32 (0 ), // TODO(palla/prover): The first block does not have hash zero
132
161
slotNumber: Slot.wrap (0 )
133
162
});
163
+ l1GasOracleValues = L1GasOracleValues ({
164
+ pre: L1FeeData ({baseFee: 1 gwei, blobFee: 1 }),
165
+ post: L1FeeData ({baseFee: block .basefee , blobFee: _getBlobBaseFee ()}),
166
+ slotOfChange: LIFETIME
167
+ });
134
168
for (uint256 i = 0 ; i < _validators.length ; i++ ) {
135
169
_addValidator (_validators[i]);
136
170
}
@@ -398,8 +432,11 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
398
432
bytes32 _txsEffectsHash ,
399
433
DataStructures.ExecutionFlags memory _flags
400
434
) external view override (IRollup) {
435
+ uint256 manaBaseFee = getManaBaseFee (true );
401
436
HeaderLib.Header memory header = HeaderLib.decode (_header);
402
- _validateHeader (header, _signatures, _digest, _currentTime, _txsEffectsHash, _flags);
437
+ _validateHeader (
438
+ header, _signatures, _digest, _currentTime, manaBaseFee, _txsEffectsHash, _flags
439
+ );
403
440
}
404
441
405
442
/**
@@ -473,6 +510,8 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
473
510
if (canPrune ()) {
474
511
_prune ();
475
512
}
513
+ updateL1GasFeeOracle ();
514
+
476
515
// The `body` is passed outside the "args" as it does not directly need to be in the digest
477
516
// as long as the `txsEffectsHash` is included and matches what is in the header.
478
517
// Which we are checking in the `_validateHeader` call below.
@@ -483,22 +522,41 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
483
522
484
523
bytes32 digest = _args.digest ();
485
524
setupEpoch ();
525
+ uint256 manaBaseFee = getManaBaseFee (true );
486
526
_validateHeader ({
487
527
_header: header,
488
528
_signatures: _signatures,
489
529
_digest: digest,
490
530
_currentTime: Timestamp.wrap (block .timestamp ),
531
+ _manaBaseFee: manaBaseFee,
491
532
_txEffectsHash: txsEffectsHash,
492
533
_flags: DataStructures.ExecutionFlags ({ignoreDA: false , ignoreSignatures: false })
493
534
});
494
535
495
536
uint256 blockNumber = ++ tips.pendingBlockNumber;
496
537
497
- blocks[blockNumber] = BlockLog ({
498
- archive: _args.archive,
499
- blockHash: _args.blockHash,
500
- slotNumber: Slot.wrap (header.globalVariables.slotNumber)
501
- });
538
+ {
539
+ FeeHeader memory parentFeeHeader = blocks[blockNumber - 1 ].feeHeader;
540
+ uint256 excessMana = (parentFeeHeader.excessMana + parentFeeHeader.manaUsed).clampedAdd (
541
+ - int256 (FeeMath.MANA_TARGET)
542
+ );
543
+
544
+ blocks[blockNumber] = BlockLog ({
545
+ archive: _args.archive,
546
+ blockHash: _args.blockHash,
547
+ slotNumber: Slot.wrap (header.globalVariables.slotNumber),
548
+ feeHeader: FeeHeader ({
549
+ excessMana: excessMana,
550
+ feeAssetPriceNumerator: parentFeeHeader.feeAssetPriceNumerator.clampedAdd (
551
+ _args.oracleInput.feeAssetPriceModifier
552
+ ),
553
+ manaUsed: header.totalManaUsed,
554
+ provingCostPerManaNumerator: parentFeeHeader.provingCostPerManaNumerator.clampedAdd (
555
+ _args.oracleInput.provingCostModifier
556
+ )
557
+ })
558
+ });
559
+ }
502
560
503
561
// @note The block number here will always be >=1 as the genesis block is at 0
504
562
bytes32 inHash = INBOX.consume (blockNumber);
@@ -536,6 +594,113 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
536
594
}
537
595
}
538
596
597
+ /**
598
+ * @notice Updates the l1 gas fee oracle
599
+ * @dev This function is called by the `propose` function
600
+ */
601
+ function updateL1GasFeeOracle () public override (IRollup) {
602
+ Slot slot = getCurrentSlot ();
603
+ // The slot where we find a new queued value acceptable
604
+ Slot acceptableSlot = l1GasOracleValues.slotOfChange + (LIFETIME - LAG);
605
+
606
+ if (slot < acceptableSlot) {
607
+ return ;
608
+ }
609
+
610
+ l1GasOracleValues.pre = l1GasOracleValues.post;
611
+ l1GasOracleValues.post = L1FeeData ({baseFee: block .basefee , blobFee: _getBlobBaseFee ()});
612
+ l1GasOracleValues.slotOfChange = slot + LAG;
613
+ }
614
+
615
+ /**
616
+ * @notice Gets the fee asset price as fee_asset / eth with 1e9 precision
617
+ *
618
+ * @return The fee asset price
619
+ */
620
+ function getFeeAssetPrice () public view override (IRollup) returns (uint256 ) {
621
+ return FeeMath.feeAssetPriceModifier (
622
+ blocks[tips.pendingBlockNumber].feeHeader.feeAssetPriceNumerator
623
+ );
624
+ }
625
+
626
+ /**
627
+ * @notice Gets the current l1 fees
628
+ *
629
+ * @return The current l1 fees
630
+ */
631
+ function getCurrentL1Fees () public view override (IRollup) returns (L1FeeData memory ) {
632
+ Slot slot = getCurrentSlot ();
633
+ if (slot < l1GasOracleValues.slotOfChange) {
634
+ return l1GasOracleValues.pre;
635
+ }
636
+ return l1GasOracleValues.post;
637
+ }
638
+
639
+ /**
640
+ * @notice Gets the mana base fee
641
+ *
642
+ * @param _inFeeAsset - Whether to return the fee in the fee asset or ETH
643
+ *
644
+ * @return The mana base fee
645
+ */
646
+ function getManaBaseFee (bool _inFeeAsset ) public view override (IRollup) returns (uint256 ) {
647
+ ManaBaseFeeComponents memory components = manaBaseFeeComponents (_inFeeAsset);
648
+ return
649
+ components.dataCost + components.gasCost + components.provingCost + components.congestionCost;
650
+ }
651
+
652
+ /**
653
+ * @notice Gets the mana base fee components
654
+ * For more context, consult:
655
+ * https://github.com/AztecProtocol/engineering-designs/blob/main/in-progress/8757-fees/design.md
656
+ *
657
+ * @dev TODO #10004 - As part of the refactor, will likely get rid of this function or make it private
658
+ * keeping it public for now makes it simpler to test.
659
+ *
660
+ * @param _inFeeAsset - Whether to return the fee in the fee asset or ETH
661
+ *
662
+ * @return The mana base fee components
663
+ */
664
+ function manaBaseFeeComponents (bool _inFeeAsset )
665
+ public
666
+ view
667
+ override (ITestRollup)
668
+ returns (ManaBaseFeeComponents memory )
669
+ {
670
+ FeeHeader storage parentFeeHeader = blocks[tips.pendingBlockNumber].feeHeader;
671
+ uint256 excessMana = (parentFeeHeader.excessMana + parentFeeHeader.manaUsed).clampedAdd (
672
+ - int256 (FeeMath.MANA_TARGET)
673
+ );
674
+
675
+ L1FeeData memory fees = getCurrentL1Fees ();
676
+ uint256 dataCost =
677
+ Math.mulDiv (3 * BLOB_GAS_PER_BLOB, fees.blobFee, FeeMath.MANA_TARGET, Math.Rounding.Ceil);
678
+ uint256 gasUsed = FeeMath.L1_GAS_PER_BLOCK_PROPOSED + 3 * GAS_PER_BLOB_POINT_EVALUATION
679
+ + FeeMath.L1_GAS_PER_EPOCH_VERIFIED / EPOCH_DURATION;
680
+ uint256 gasCost = Math.mulDiv (gasUsed, fees.baseFee, FeeMath.MANA_TARGET, Math.Rounding.Ceil);
681
+ uint256 provingCost = FeeMath.provingCostPerMana (
682
+ blocks[tips.pendingBlockNumber].feeHeader.provingCostPerManaNumerator
683
+ );
684
+
685
+ uint256 congestionMultiplier = FeeMath.congestionMultiplier (excessMana);
686
+ uint256 total = dataCost + gasCost + provingCost;
687
+ uint256 congestionCost = Math.mulDiv (
688
+ total, congestionMultiplier, FeeMath.MINIMUM_CONGESTION_MULTIPLIER, Math.Rounding.Floor
689
+ ) - total;
690
+
691
+ uint256 feeAssetPrice = _inFeeAsset ? getFeeAssetPrice () : 1e9 ;
692
+
693
+ // @todo @lherskind. The following is a crime against humanity, but it makes it
694
+ // very neat to plot etc from python, #10004 will fix it across the board
695
+ return ManaBaseFeeComponents ({
696
+ dataCost: Math.mulDiv (dataCost, feeAssetPrice, 1e9 , Math.Rounding.Ceil),
697
+ gasCost: Math.mulDiv (gasCost, feeAssetPrice, 1e9 , Math.Rounding.Ceil),
698
+ provingCost: Math.mulDiv (provingCost, feeAssetPrice, 1e9 , Math.Rounding.Ceil),
699
+ congestionCost: Math.mulDiv (congestionCost, feeAssetPrice, 1e9 , Math.Rounding.Ceil),
700
+ congestionMultiplier: congestionMultiplier
701
+ });
702
+ }
703
+
539
704
function quoteToDigest (EpochProofQuoteLib.EpochProofQuote memory _quote )
540
705
public
541
706
view
@@ -757,6 +922,14 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
757
922
return tips.pendingBlockNumber;
758
923
}
759
924
925
+ function getBlock (uint256 _blockNumber ) public view override (IRollup) returns (BlockLog memory ) {
926
+ require (
927
+ _blockNumber <= tips.pendingBlockNumber,
928
+ Errors.Rollup__InvalidBlockNumber (tips.pendingBlockNumber, _blockNumber)
929
+ );
930
+ return blocks[_blockNumber];
931
+ }
932
+
760
933
function getEpochForBlock (uint256 _blockNumber ) public view override (IRollup) returns (Epoch) {
761
934
require (
762
935
_blockNumber <= tips.pendingBlockNumber,
@@ -856,13 +1029,14 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
856
1029
SignatureLib.Signature[] memory _signatures ,
857
1030
bytes32 _digest ,
858
1031
Timestamp _currentTime ,
1032
+ uint256 _manaBaseFee ,
859
1033
bytes32 _txEffectsHash ,
860
1034
DataStructures.ExecutionFlags memory _flags
861
1035
) internal view {
862
1036
uint256 pendingBlockNumber =
863
1037
canPruneAtTime (_currentTime) ? tips.provenBlockNumber : tips.pendingBlockNumber;
864
1038
_validateHeaderForSubmissionBase (
865
- _header, _currentTime, _txEffectsHash, pendingBlockNumber, _flags
1039
+ _header, _currentTime, _manaBaseFee, _txEffectsHash, pendingBlockNumber, _flags
866
1040
);
867
1041
_validateHeaderForSubmissionSequencerSelection (
868
1042
Slot.wrap (_header.globalVariables.slotNumber), _signatures, _digest, _currentTime, _flags
@@ -928,6 +1102,7 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
928
1102
function _validateHeaderForSubmissionBase (
929
1103
HeaderLib.Header memory _header ,
930
1104
Timestamp _currentTime ,
1105
+ uint256 _manaBaseFee ,
931
1106
bytes32 _txsEffectsHash ,
932
1107
uint256 _pendingBlockNumber ,
933
1108
DataStructures.ExecutionFlags memory _flags
@@ -983,6 +1158,12 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
983
1158
if (address (this ) != FEE_JUICE_PORTAL.canonicalRollup ()) {
984
1159
require (_header.globalVariables.gasFees.feePerDaGas == 0 , Errors.Rollup__NonZeroDaFee ());
985
1160
require (_header.globalVariables.gasFees.feePerL2Gas == 0 , Errors.Rollup__NonZeroL2Fee ());
1161
+ } else {
1162
+ require (_header.globalVariables.gasFees.feePerDaGas == 0 , Errors.Rollup__NonZeroDaFee ());
1163
+ require (
1164
+ _header.globalVariables.gasFees.feePerL2Gas == _manaBaseFee,
1165
+ Errors.Rollup__InvalidManaBaseFee (_manaBaseFee, _header.globalVariables.gasFees.feePerL2Gas)
1166
+ );
986
1167
}
987
1168
}
988
1169
@@ -1004,4 +1185,19 @@ contract Rollup is EIP712("Aztec Rollup", "1"), Leonidas, IRollup, ITestRollup {
1004
1185
}
1005
1186
}
1006
1187
}
1188
+
1189
+ /**
1190
+ * @notice Get the blob base fee
1191
+ *
1192
+ * @dev If we are in a foundry test, we use the cheatcode to get the blob base fee.
1193
+ * Otherwise, we use the `block.blobbasefee`
1194
+ *
1195
+ * @return uint256 - The blob base fee
1196
+ */
1197
+ function _getBlobBaseFee () private view returns (uint256 ) {
1198
+ if (IS_FOUNDRY_TEST) {
1199
+ return Vm (VM_ADDRESS).getBlobBaseFee ();
1200
+ }
1201
+ return block .blobbasefee ;
1202
+ }
1007
1203
}
0 commit comments