Skip to content

Commit eab5ed6

Browse files
committed
feat: implement transact gas limits
1 parent e3c32a8 commit eab5ed6

File tree

3 files changed

+91
-19
lines changed

3 files changed

+91
-19
lines changed

.gas-snapshot

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@ PassageTest:test_onlyTokenAdmin() (gas: 16881)
2424
PassageTest:test_receive() (gas: 21339)
2525
PassageTest:test_setUp() (gas: 16901)
2626
PassageTest:test_withdraw() (gas: 59055)
27-
ZenithTest:test_addSequencer() (gas: 88222)
28-
ZenithTest:test_badSignature() (gas: 37320)
29-
ZenithTest:test_incorrectHostBlock() (gas: 35165)
30-
ZenithTest:test_notSequencer() (gas: 34155)
31-
ZenithTest:test_notSequencerAdmin() (gas: 10137)
32-
ZenithTest:test_onePerBlock() (gas: 68351)
33-
ZenithTest:test_removeSequencer() (gas: 39744)
34-
ZenithTest:test_setUp() (gas: 8410)
35-
ZenithTest:test_submitBlock() (gas: 63456)
36-
ZenithTest:test_transact() (gas: 32536)
37-
ZenithTest:test_transact_defaultChain() (gas: 31022)
27+
ZenithTest:test_addSequencer() (gas: 110340)
28+
ZenithTest:test_badSignature() (gas: 37389)
29+
ZenithTest:test_incorrectHostBlock() (gas: 35233)
30+
ZenithTest:test_notSequencer() (gas: 34157)
31+
ZenithTest:test_notSequencerAdmin() (gas: 10149)
32+
ZenithTest:test_onePerBlock() (gas: 90513)
33+
ZenithTest:test_removeSequencer() (gas: 39747)
34+
ZenithTest:test_setUp() (gas: 8478)
35+
ZenithTest:test_submitBlock() (gas: 85551)
36+
ZenithTest:test_transact() (gas: 135881)
37+
ZenithTest:test_transact_defaultChain() (gas: 126171)
38+
ZenithTest:test_transact_globalGasLimit() (gas: 152687)
39+
ZenithTest:test_transact_perTransactGasLimit() (gas: 106413)

src/Zenith.sol

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ pragma solidity ^0.8.24;
44
import {Passage} from "./Passage.sol";
55

66
contract Zenith {
7+
/// @notice Each `transact` call cannot use more than 1/5 of the global `transact` gasLimit for the block.
8+
uint256 public constant PER_TRANSACT_GAS_LIMIT = 5;
9+
710
/// @notice The chainId of rollup that Ether will be sent to by default when entering the rollup via fallback() or receive().
811
uint256 public immutable defaultRollupChainId;
912

@@ -30,9 +33,18 @@ contract Zenith {
3033
}
3134

3235
/// @notice The host block number that a block was last submitted at for a given rollup chainId.
33-
/// rollupChainId => host blockNumber that block was last submitted at
36+
/// rollupChainId => host blockNumber the last block was submitted at.
3437
mapping(uint256 => uint256) public lastSubmittedAtBlock;
3538

39+
/// @notice The gasLimit of the last submitted block for a given rollup chainId.
40+
/// @dev NOTE that this can change mid-block, causing some `transact` to have a different limit than others.
41+
/// rollupChainId => gasLimit of the previous block.
42+
mapping(uint256 => uint256) public currentGasLimit;
43+
44+
/// @notice The total gasLimit used by `transact` so far in this block.
45+
/// rollupChainId => block number => `transasct` gasLimit used so far.
46+
mapping(uint256 => mapping(uint256 => uint256)) public transactGasUsed;
47+
3648
/// @notice Registry of permissioned sequencers.
3749
/// address => TRUE if it's a permissioned sequencer
3850
mapping(address => bool) public isSequencer;
@@ -48,6 +60,12 @@ contract Zenith {
4860
/// @notice Thrown when attempting to submit more than one rollup block per host block
4961
error OneRollupBlockPerHostBlock();
5062

63+
/// @notice Thrown when attempting to use more then the current global `transact` gasLimit for the block.
64+
error GlobalTransactGasLimitReached(uint256 globalTransactLimit);
65+
66+
/// @notice Thrown when attempting to use too much gas per single `transact` call.
67+
error PerTransactGasLimitReached(uint256 perTransactLimit);
68+
5169
/// @notice Thrown when attempting to modify sequencer roles if not sequencerAdmin.
5270
error OnlySequencerAdmin();
5371

@@ -133,13 +151,19 @@ contract Zenith {
133151
// assert this is the first rollup block submitted for this host block
134152
if (lastSubmittedAtBlock[header.rollupChainId] == block.number) revert OneRollupBlockPerHostBlock();
135153
lastSubmittedAtBlock[header.rollupChainId] = block.number;
154+
currentGasLimit[header.rollupChainId] = header.gasLimit;
136155

137156
// emit event
138157
emit BlockSubmitted(
139158
sequencer, header.rollupChainId, header.gasLimit, header.rewardAddress, header.blockDataHash
140159
);
141160
}
142161

162+
/// @dev See `transact` for docs.
163+
function transact(address to, bytes calldata data, uint256 value, uint256 gas, uint256 maxFeePerGas) external {
164+
transact(defaultRollupChainId, to, data, value, gas, maxFeePerGas);
165+
}
166+
143167
/// @notice Send a special transaction to be sent to the rollup with sender == L1 msg.sender.
144168
/// @dev Transact is processed after normal rollup block execution.
145169
/// @param rollupChainId - The rollup chain to send the transaction to.
@@ -157,15 +181,20 @@ contract Zenith {
157181
uint256 gas,
158182
uint256 maxFeePerGas
159183
) public {
184+
// ensure per-transact gas limit is respected
185+
uint256 globalLimit = currentGasLimit[rollupChainId];
186+
uint256 perTransactLimit = globalLimit / PER_TRANSACT_GAS_LIMIT;
187+
if (gas > perTransactLimit) revert PerTransactGasLimitReached(perTransactLimit);
188+
189+
// ensure global transact gas limit is respected
190+
uint256 gasUsed = transactGasUsed[rollupChainId][block.number];
191+
if (gasUsed + gas > globalLimit) revert GlobalTransactGasLimitReached(globalLimit);
192+
transactGasUsed[rollupChainId][block.number] = gasUsed + gas;
193+
160194
// emit Transact event
161195
emit Transact(rollupChainId, msg.sender, to, data, value, gas, maxFeePerGas);
162196
}
163197

164-
/// @dev See `transact` for docs.
165-
function transact(address to, bytes calldata data, uint256 value, uint256 gas, uint256 maxFeePerGas) external {
166-
transact(defaultRollupChainId, to, data, value, gas, maxFeePerGas);
167-
}
168-
169198
/// @notice Construct hash of block details that the sequencer signs.
170199
/// @param header - the header information for the rollup block.
171200
/// @return commit - the hash of the encoded block details.

test/Zenith.t.sol

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ contract ZenithTest is Test {
1919
address to = address(0x01);
2020
bytes data = abi.encode(0xbeef);
2121
uint256 value = 100;
22-
uint256 gas = 10_000_000;
22+
uint256 gas = 6_000_000;
2323
uint256 maxFeePerGas = 50;
2424

2525
event BlockSubmitted(
@@ -43,7 +43,7 @@ contract ZenithTest is Test {
4343
);
4444

4545
function setUp() public {
46-
target = new Zenith(block.number + 1, address(this));
46+
target = new Zenith(block.chainid + 1, address(this));
4747
target.addSequencer(vm.addr(sequencerKey));
4848

4949
// set default block values
@@ -178,14 +178,55 @@ contract ZenithTest is Test {
178178
}
179179

180180
function test_transact() public {
181+
// submit a block to set the prevGasLimit to 30M
182+
header.rollupChainId = chainId;
183+
commit = target.blockCommitment(header);
184+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerKey, commit);
185+
target.submitBlock(header, v, r, s, blockData);
186+
181187
vm.expectEmit();
182188
emit Transact(chainId, address(this), to, data, value, gas, maxFeePerGas);
183189
target.transact(chainId, to, data, value, gas, maxFeePerGas);
184190
}
185191

186192
function test_transact_defaultChain() public {
193+
// submit a block to set the prevGasLimit to 30M
194+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerKey, commit);
195+
target.submitBlock(header, v, r, s, blockData);
196+
187197
vm.expectEmit();
188198
emit Transact(target.defaultRollupChainId(), address(this), to, data, value, gas, maxFeePerGas);
189199
target.transact(to, data, value, gas, maxFeePerGas);
190200
}
201+
202+
function test_transact_perTransactGasLimit() public {
203+
// submit a block to set the prevGasLimit to 30M
204+
header.rollupChainId = chainId;
205+
commit = target.blockCommitment(header);
206+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerKey, commit);
207+
target.submitBlock(header, v, r, s, blockData);
208+
209+
// attempt transact with 6M + 1 gas.
210+
vm.expectRevert(
211+
abi.encodeWithSelector(
212+
Zenith.PerTransactGasLimitReached.selector, (header.gasLimit / target.PER_TRANSACT_GAS_LIMIT())
213+
)
214+
);
215+
target.transact(chainId, to, data, value, gas + 1, maxFeePerGas);
216+
}
217+
218+
function test_transact_globalGasLimit() public {
219+
// submit a block to set the prevGasLimit to 30M
220+
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerKey, commit);
221+
target.submitBlock(header, v, r, s, blockData);
222+
223+
// submit 5x transacts with 6M gas, consuming the total 30M global limit
224+
for (uint256 i; i < 5; i++) {
225+
target.transact(to, data, value, gas, maxFeePerGas);
226+
}
227+
228+
// attempt to submit another transact with 1 gas - should revert.
229+
vm.expectRevert(abi.encodeWithSelector(Zenith.GlobalTransactGasLimitReached.selector, header.gasLimit));
230+
target.transact(to, data, value, 1, maxFeePerGas);
231+
}
191232
}

0 commit comments

Comments
 (0)