Skip to content

Commit 269ddd7

Browse files
authored
feat: replace sequence and confirmBy with hostBlockNumber (#48)
1 parent 6b83737 commit 269ddd7

File tree

3 files changed

+29
-106
lines changed

3 files changed

+29
-106
lines changed

.gas-snapshot

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
ZenithTest:test_badSequence() (gas: 60268)
2-
ZenithTest:test_badSignature() (gas: 66936)
3-
ZenithTest:test_blockExpired() (gas: 55499)
4-
ZenithTest:test_notSequencer() (gas: 58618)
5-
ZenithTest:test_onePerBlock() (gas: 104426)
6-
ZenithTest:test_submitBlock() (gas: 88526)
1+
ZenithTest:test_badSignature() (gas: 37377)
2+
ZenithTest:test_incorrectHostBlock() (gas: 35200)
3+
ZenithTest:test_notSequencer() (gas: 34145)
4+
ZenithTest:test_onePerBlock() (gas: 68365)
5+
ZenithTest:test_submitBlock() (gas: 61143)

src/Zenith.sol

Lines changed: 13 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,25 @@ contract Zenith is Passage {
77
/// @notice The address that is allowed to set/remove sequencers.
88
address public immutable sequencerAdmin;
99

10+
/// @notice The block number at which the Zenith contract was deployed.
11+
uint256 public deployBlockNumber;
12+
1013
/// @notice Block header information for the rollup block, signed by the sequencer.
1114
/// @param rollupChainId - the chainId of the rollup chain. Any chainId is accepted by the contract.
12-
/// @param sequence - the sequence number of the rollup block. Must be monotonically increasing. Enforced by the contract.
13-
/// @param confirmBy - the timestamp by which the block must be submitted. Enforced by the contract.
15+
/// @param hostBlockNumber - the host block number in which the rollup block must be submitted. Enforced by the contract.
1416
/// @param gasLimit - the gas limit for the rollup block. Ignored by the contract; enforced by the Node.
1517
/// @param rewardAddress - the address to receive the rollup block reward. Ignored by the contract; enforced by the Node.
1618
/// @param blockDataHash - keccak256(rlp-encoded transactions). the Node will discard the block if the hash doens't match.
1719
/// this allows the sequencer to sign over finalized set of transactions,
1820
/// without the Zenith contract needing to interact with raw transaction data (which may be provided via blobs or calldata).
1921
struct BlockHeader {
2022
uint256 rollupChainId;
21-
uint256 sequence;
22-
uint256 confirmBy;
23+
uint256 hostBlockNumber;
2324
uint256 gasLimit;
2425
address rewardAddress;
2526
bytes32 blockDataHash;
2627
}
2728

28-
/// @notice The sequence number of the next block that can be submitted for a given rollup chainId.
29-
/// @dev Because sequences must start at 1, accessors must add 1 to this number.
30-
/// rollupChainId => (nextSequence - 1)
31-
mapping(uint256 => uint256) sequences;
32-
3329
/// @notice The host block number that a block was last submitted at for a given rollup chainId.
3430
/// rollupChainId => host blockNumber that block was last submitted at
3531
mapping(uint256 => uint256) public lastSubmittedAtBlock;
@@ -38,13 +34,8 @@ contract Zenith is Passage {
3834
/// address => TRUE if it's a permissioned sequencer
3935
mapping(address => bool) public isSequencer;
4036

41-
/// @notice Thrown when a block submission is attempted with a sequence number that is not the next block for the rollup chainId.
42-
/// @dev Blocks must be submitted in strict monotonic increasing order.
43-
/// @param expected - the correct next sequence number for the given rollup chainId.
44-
error BadSequence(uint256 expected);
45-
46-
/// @notice Thrown when a block submission is attempted when the confirmBy time has passed.
47-
error BlockExpired();
37+
/// @notice Thrown when a block submission is attempted in the incorrect host block.
38+
error IncorrectHostBlock();
4839

4940
/// @notice Thrown when a block submission is attempted with a signature by a non-permissioned sequencer,
5041
/// OR when signature is produced over different block header than is provided.
@@ -60,17 +51,13 @@ contract Zenith is Passage {
6051
/// @notice Emitted when a new rollup block is successfully submitted.
6152
/// @param sequencer - the address of the sequencer that signed the block.
6253
/// @param rollupChainId - the chainId of the rollup chain.
63-
/// @param sequence - the sequence number of the rollup block.
64-
/// @param confirmBy - the timestamp by which the block must be submitted.
6554
/// @param gasLimit - the gas limit for the rollup block.
6655
/// @param rewardAddress - the address to receive the rollup block reward.
6756
/// @param blockDataHash - keccak256(rlp-encoded transactions). the Node will discard the block if the hash doens't match transactions provided.
6857
/// @dev including blockDataHash allows the sequencer to sign over finalized block data, without needing to calldatacopy the `blockData` param.
6958
event BlockSubmitted(
7059
address indexed sequencer,
7160
uint256 indexed rollupChainId,
72-
uint256 indexed sequence,
73-
uint256 confirmBy,
7461
uint256 gasLimit,
7562
address rewardAddress,
7663
bytes32 blockDataHash
@@ -83,22 +70,7 @@ contract Zenith is Passage {
8370
Passage(_defaultRollupChainId, _withdrawalAdmin)
8471
{
8572
sequencerAdmin = _sequencerAdmin;
86-
}
87-
88-
/// @notice Returns the next sequence number.
89-
/// @dev Because sequences must start at 1, we add 1 to the mapping value.
90-
/// @param _rollupChainId - the chainId of the rollup chain. Any chainId is accepted by the contract.
91-
/// @return The next sequence number.
92-
function nextSequence(uint256 _rollupChainId) public view returns (uint256) {
93-
return sequences[_rollupChainId] + 1;
94-
}
95-
96-
/// @notice Increments the sequence number and returns it.
97-
/// @dev Because sequences must start at 1, we add 1 to the mapping value.
98-
/// @param _rollupChainId - the chainId of the rollup chain. Any chainId is accepted by the contract.
99-
/// @return The next sequence number.
100-
function incrementSequence(uint256 _rollupChainId) internal returns (uint256) {
101-
return ++sequences[_rollupChainId];
73+
deployBlockNumber = block.number;
10274
}
10375

10476
/// @notice Add a sequencer to the permissioned sequencer list.
@@ -129,19 +101,14 @@ contract Zenith is Passage {
129101
/// @param v - the v component of the Sequencer's ECSDA signature over the block header.
130102
/// @param r - the r component of the Sequencer's ECSDA signature over the block header.
131103
/// @param s - the s component of the Sequencer's ECSDA signature over the block header.
132-
/// @custom:reverts BadSequence if the sequence number is not the next block for the given rollup chainId.
133-
/// @custom:reverts BlockExpired if the confirmBy time has passed.
104+
/// @custom:reverts IncorrectHostBlock if the hostBlockNumber does not match the current block.
134105
/// @custom:reverts BadSignature if the signer is not a permissioned sequencer,
135106
/// OR if the signature provided commits to a different header.
136107
/// @custom:reverts OneRollupBlockPerHostBlock if attempting to submit a second rollup block within one host block.
137108
/// @custom:emits BlockSubmitted if the block is successfully submitted.
138109
function submitBlock(BlockHeader memory header, uint8 v, bytes32 r, bytes32 s, bytes calldata) external {
139-
// assert that the sequence number is valid and increment it
140-
uint256 _nextSequence = incrementSequence(header.rollupChainId);
141-
if (_nextSequence != header.sequence) revert BadSequence(_nextSequence);
142-
143-
// assert that confirmBy time has not passed
144-
if (block.timestamp > header.confirmBy) revert BlockExpired();
110+
// assert that the host block number matches the current block
111+
if (block.number != header.hostBlockNumber) revert IncorrectHostBlock();
145112

146113
// derive sequencer from signature over block header
147114
bytes32 blockCommit = blockCommitment(header);
@@ -156,13 +123,7 @@ contract Zenith is Passage {
156123

157124
// emit event
158125
emit BlockSubmitted(
159-
sequencer,
160-
header.rollupChainId,
161-
header.sequence,
162-
header.confirmBy,
163-
header.gasLimit,
164-
header.rewardAddress,
165-
header.blockDataHash
126+
sequencer, header.rollupChainId, header.gasLimit, header.rewardAddress, header.blockDataHash
166127
);
167128
}
168129

@@ -174,9 +135,8 @@ contract Zenith is Passage {
174135
"init4.sequencer.v0",
175136
block.chainid,
176137
header.rollupChainId,
177-
header.sequence,
138+
header.hostBlockNumber,
178139
header.gasLimit,
179-
header.confirmBy,
180140
header.rewardAddress,
181141
header.blockDataHash
182142
);

test/Zenith.t.sol

Lines changed: 11 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ contract ZenithTest is Test {
1818
event BlockSubmitted(
1919
address indexed sequencer,
2020
uint256 indexed rollupChainId,
21-
uint256 indexed sequence,
22-
uint256 confirmBy,
2321
uint256 gasLimit,
2422
address rewardAddress,
2523
bytes32 blockDataHash
@@ -31,8 +29,7 @@ contract ZenithTest is Test {
3129

3230
// set default block values
3331
header.rollupChainId = block.chainid + 1;
34-
header.sequence = 1; // first block has index 1
35-
header.confirmBy = block.timestamp + 10 minutes;
32+
header.hostBlockNumber = block.number;
3633
header.gasLimit = 30_000_000;
3734
header.rewardAddress = address(this);
3835
header.blockDataHash = keccak256(blockData);
@@ -41,29 +38,16 @@ contract ZenithTest is Test {
4138
commit = target.blockCommitment(header);
4239
}
4340

44-
// cannot submit block with incorrect sequence number
45-
function test_badSequence() public {
46-
// change to incorrect sequence number
47-
header.sequence = 100;
41+
// cannot submit block with incorrect host block number
42+
function test_incorrectHostBlock() public {
43+
// change to incorrect host block number
44+
header.hostBlockNumber = 100;
4845
commit = target.blockCommitment(header);
4946

5047
// sign block commitmenet with sequencer key
5148
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerKey, commit);
5249

53-
vm.expectRevert(abi.encodeWithSelector(Zenith.BadSequence.selector, 1));
54-
target.submitBlock(header, v, r, s, blockData);
55-
}
56-
57-
// cannot submit block with expired confirmBy time
58-
function test_blockExpired() public {
59-
// change to expired confirmBy time
60-
header.confirmBy = block.timestamp - 1;
61-
commit = target.blockCommitment(header);
62-
63-
// sign block commitmenet with sequencer key
64-
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerKey, commit);
65-
66-
vm.expectRevert(abi.encodeWithSelector(Zenith.BlockExpired.selector));
50+
vm.expectRevert(Zenith.IncorrectHostBlock.selector);
6751
target.submitBlock(header, v, r, s, blockData);
6852
}
6953

@@ -75,18 +59,9 @@ contract ZenithTest is Test {
7559
// should emit BlockSubmitted event
7660
vm.expectEmit();
7761
emit BlockSubmitted(
78-
vm.addr(sequencerKey),
79-
header.rollupChainId,
80-
header.sequence,
81-
header.confirmBy,
82-
header.gasLimit,
83-
header.rewardAddress,
84-
header.blockDataHash
62+
vm.addr(sequencerKey), header.rollupChainId, header.gasLimit, header.rewardAddress, header.blockDataHash
8563
);
8664
target.submitBlock(header, v, r, s, blockData);
87-
88-
// should increment sequence number
89-
assertEq(target.nextSequence(header.rollupChainId), header.sequence + 1);
9065
}
9166

9267
// cannot submit block with invalid sequencer signer from non-permissioned key
@@ -104,7 +79,7 @@ contract ZenithTest is Test {
10479
(uint8 v, bytes32 r, bytes32 s) = vm.sign(sequencerKey, commit);
10580

10681
// change header data from what was signed by sequencer
107-
header.confirmBy = block.timestamp + 15 minutes;
82+
header.rewardAddress = address(0);
10883
bytes32 newCommit = target.blockCommitment(header);
10984
address derivedSigner = ecrecover(newCommit, v, r, s);
11085

@@ -120,24 +95,13 @@ contract ZenithTest is Test {
12095
// should emit BlockSubmitted event
12196
vm.expectEmit();
12297
emit BlockSubmitted(
123-
vm.addr(sequencerKey),
124-
header.rollupChainId,
125-
header.sequence,
126-
header.confirmBy,
127-
header.gasLimit,
128-
header.rewardAddress,
129-
header.blockDataHash
98+
vm.addr(sequencerKey), header.rollupChainId, header.gasLimit, header.rewardAddress, header.blockDataHash
13099
);
131100
target.submitBlock(header, v, r, s, blockData);
132101

133-
// incerement the header sequence
134-
header.sequence += 1;
135-
commit = target.blockCommitment(header);
136-
(v, r, s) = vm.sign(sequencerKey, commit);
137-
138102
// should revert with OneRollupBlockPerHostBlock
139-
// (NOTE: this test works because forge does not increment block.number when it mines a transaction)
140-
vm.expectRevert(abi.encodeWithSelector(Zenith.OneRollupBlockPerHostBlock.selector));
103+
// (NOTE: this test works without changing the Header because forge does not increment block.number when it mines a transaction)
104+
vm.expectRevert(Zenith.OneRollupBlockPerHostBlock.selector);
141105
target.submitBlock(header, v, r, s, blockData);
142106
}
143107
}

0 commit comments

Comments
 (0)