Skip to content

Commit 5346120

Browse files
authored
refactor: staking queue config now includes max value (#16621)
With the changes for BLS keys to be included, the cost of the queue and deposits was heavily impacted. however the upper limit of queue members to consume was not altered. It is now a config value along with the rest of the staking queue config. The config is important as it pose a potential DOS. If there are sufficient queue members it will be impossible to actually flush the queue, so making it updatable is a sane way to deal with changing costs of op-codes etc. Also it can be used to put limits more easily on set growth. Fixes an issue in the migration loading, and adds a test to showcase gas costs related to additions.
2 parents 1c3b9f3 + 9beb289 commit 5346120

File tree

18 files changed

+1891
-57
lines changed

18 files changed

+1891
-57
lines changed

l1-contracts/.gitignore

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,5 +36,3 @@ gas_benchmark.diff
3636
/bench-out
3737

3838
/coverage
39-
40-
/script/registration_data.json

l1-contracts/foundry.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ gas_reports = [
2929
"RollupWithPreheating",
3030
"EmpireSlashingProposer",
3131
"TallySlashingProposer",
32+
"MultiAdder"
3233
]
3334

3435
remappings = [

l1-contracts/script/Migration.s.sol

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import {TestERC20} from "@aztec/mock/TestERC20.sol";
1212

1313
struct RegistrationData {
1414
address attester;
15+
G1Point proofOfPossession;
1516
G1Point publicKeyInG1;
1617
G2Point publicKeyInG2;
17-
G1Point proofOfPossession;
1818
}
1919

2020
contract MigrationScript is Test {
@@ -46,7 +46,7 @@ contract MigrationScript is Test {
4646
function emulate() public {
4747
// If emulating we will deploy a rollup and other components ahead of time, otherwise
4848
// you should provide them by updating the values above.
49-
RollupBuilder builder = new RollupBuilder(ME).setUpdateOwnerships(false).deploy();
49+
RollupBuilder builder = new RollupBuilder(ME).setUpdateOwnerships(false).setCheckProofOfPossession(true).deploy();
5050

5151
INSTANCE = IInstance(address(builder.getConfig().rollup));
5252
STAKING_ASSET = builder.getConfig().testERC20;
@@ -57,6 +57,20 @@ contract MigrationScript is Test {
5757
migrate();
5858
}
5959

60+
function print() public {
61+
RegistrationData memory r = $registrations[0];
62+
emit log_named_address("Attester", r.attester);
63+
emit log_named_address("Withdrawer", WITHDRAWER);
64+
emit log_named_bytes32("PublicKeyInG1.x", bytes32(r.publicKeyInG1.x));
65+
emit log_named_bytes32("PublicKeyInG1.y", bytes32(r.publicKeyInG1.y));
66+
emit log_named_bytes32("PublicKeyInG2.x0", bytes32(r.publicKeyInG2.x0));
67+
emit log_named_bytes32("PublicKeyInG2.x1", bytes32(r.publicKeyInG2.x1));
68+
emit log_named_bytes32("PublicKeyInG2.y0", bytes32(r.publicKeyInG2.y0));
69+
emit log_named_bytes32("PublicKeyInG2.y1", bytes32(r.publicKeyInG2.y1));
70+
emit log_named_bytes32("ProofOfPossession.x", bytes32(r.proofOfPossession.x));
71+
emit log_named_bytes32("ProofOfPossession.y", bytes32(r.proofOfPossession.y));
72+
}
73+
6074
function migrate() public {
6175
uint256 activationThreshold = INSTANCE.getActivationThreshold();
6276

@@ -69,6 +83,8 @@ contract MigrationScript is Test {
6983
uint256 end = $registrations.length;
7084
uint256 batchSize = 10;
7185

86+
uint256 sizeBefore = INSTANCE.getActiveAttesterCount() + INSTANCE.getEntryQueueLength();
87+
7288
while (start < end) {
7389
uint256 batchEnd = start + batchSize;
7490
if (batchEnd > end) {
@@ -91,8 +107,11 @@ contract MigrationScript is Test {
91107
vm.startBroadcast(ME);
92108
ADDER.addValidators(args);
93109
vm.stopBroadcast();
94-
95110
start = batchEnd;
96111
}
112+
113+
uint256 sizeAfter = INSTANCE.getActiveAttesterCount() + INSTANCE.getEntryQueueLength();
114+
115+
assertGe(sizeAfter, sizeBefore + end);
97116
}
98117
}

l1-contracts/script/registration_data.json

Lines changed: 1702 additions & 0 deletions
Large diffs are not rendered by default.

l1-contracts/src/core/libraries/StakingQueue.sol

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ struct StakingQueue {
3131
}
3232

3333
library StakingQueueLib {
34-
// This is a HARD CAP. We will never attempt to flush more than this number of validators,
35-
// because it starts to butt up against the block gas limit.
36-
uint256 public constant MAX_QUEUE_FLUSH_SIZE = 150;
37-
3834
function init(StakingQueue storage self) internal {
3935
self.first = 1;
4036
self.last = 1;

l1-contracts/src/core/libraries/compressed-data/StakingQueueConfig.sol

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,28 +22,30 @@ type CompressedStakingQueueConfig is uint256;
2222
* possible to add validators. This can close the queue even if there are members in the validator set if a very high
2323
* `normalFlushSizeQuotient` is used.
2424
*
25-
* NOTE: We will NEVER flush more than `MAX_QUEUE_FLUSH_SIZE` validators: it is applied as a Max at the end of every
25+
* NOTE: We will NEVER flush more than `maxQueueFlushSize` validators: it is applied as a Max at the end of every
2626
* calculation.
27-
* This is to prevent a situation where flushing the queue would exceed the block gas limit.
27+
* This can be used to prevent a situation where flushing the queue would exceed the block gas limit.
2828
*/
2929
struct StakingQueueConfig {
3030
uint256 bootstrapValidatorSetSize;
3131
uint256 bootstrapFlushSize;
3232
uint256 normalFlushSizeMin;
3333
uint256 normalFlushSizeQuotient;
34+
uint256 maxQueueFlushSize;
3435
}
3536

3637
library StakingQueueConfigLib {
3738
using SafeCast for uint256;
3839

39-
uint256 private constant MASK_64BIT = 0xFFFFFFFFFFFFFFFF;
40+
uint256 private constant MASK_32BIT = 0xFFFFFFFF;
4041

4142
function compress(StakingQueueConfig memory _config) internal pure returns (CompressedStakingQueueConfig) {
4243
uint256 value = 0;
43-
value |= uint256(_config.normalFlushSizeQuotient.toUint64());
44-
value |= uint256(_config.normalFlushSizeMin.toUint64()) << 64;
45-
value |= uint256(_config.bootstrapFlushSize.toUint64()) << 128;
46-
value |= uint256(_config.bootstrapValidatorSetSize.toUint64()) << 192;
44+
value |= uint256(_config.maxQueueFlushSize.toUint32());
45+
value |= uint256(_config.normalFlushSizeQuotient.toUint32()) << 32;
46+
value |= uint256(_config.normalFlushSizeMin.toUint32()) << 64;
47+
value |= uint256(_config.bootstrapFlushSize.toUint32()) << 96;
48+
value |= uint256(_config.bootstrapValidatorSetSize.toUint32()) << 128;
4749

4850
return CompressedStakingQueueConfig.wrap(value);
4951
}
@@ -52,10 +54,11 @@ library StakingQueueConfigLib {
5254
uint256 value = CompressedStakingQueueConfig.unwrap(_compressedConfig);
5355

5456
return StakingQueueConfig({
55-
bootstrapValidatorSetSize: (value >> 192) & MASK_64BIT,
56-
bootstrapFlushSize: (value >> 128) & MASK_64BIT,
57-
normalFlushSizeMin: (value >> 64) & MASK_64BIT,
58-
normalFlushSizeQuotient: (value) & MASK_64BIT
57+
bootstrapValidatorSetSize: (value >> 128) & MASK_32BIT,
58+
bootstrapFlushSize: (value >> 96) & MASK_32BIT,
59+
normalFlushSizeMin: (value >> 64) & MASK_32BIT,
60+
normalFlushSizeQuotient: (value >> 32) & MASK_32BIT,
61+
maxQueueFlushSize: value & MASK_32BIT
5962
});
6063
}
6164
}

l1-contracts/src/core/libraries/rollup/StakingLib.sol

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ library StakingLib {
303303
* The function will revert if:
304304
* - The current epoch is before nextFlushableEpoch (max 1 flush per epoch). This limit:
305305
* 1. Controls validator set growth by only allowing a fixed number of additions per epoch (we can flush at
306-
* most MAX_QUEUE_FLUSH_SIZE validators at once - hence the limit)
306+
* most maxQueueFlushSize validators at once - hence the limit)
307307
* 2. Aligns with committee selection which happens once per epoch anyway
308308
* 3. Groups validator additions into epoch-sized chunks for efficient binary searches (fewer snapshots in
309309
* GSE)
@@ -509,7 +509,7 @@ library StakingLib {
509509
* 3. Normal phase: After the initial bootstrap and growth phases, returns a number proportional to the current
510510
* set size for conservative steady-state growth, unless constrained by configuration (`normalFlushSizeMin`).
511511
*
512-
* All phases are subject to a hard cap of `MAX_QUEUE_FLUSH_SIZE`.
512+
* All phases are subject to a hard cap of `maxQueueFlushSize`.
513513
*
514514
* The motivation for floodgates is that the whole system starts producing blocks with what is considered
515515
* a sufficiently decentralized set of validators.
@@ -536,14 +536,14 @@ library StakingLib {
536536

537537
// If growth:
538538
if (activeAttesterCount < config.bootstrapValidatorSetSize) {
539-
return Math.min(config.bootstrapFlushSize, StakingQueueLib.MAX_QUEUE_FLUSH_SIZE);
539+
return Math.min(config.bootstrapFlushSize, config.maxQueueFlushSize);
540540
}
541541
}
542542

543543
// If normal:
544544
return Math.min(
545545
Math.max(activeAttesterCount / config.normalFlushSizeQuotient, config.normalFlushSizeMin),
546-
StakingQueueLib.MAX_QUEUE_FLUSH_SIZE
546+
config.maxQueueFlushSize
547547
);
548548
}
549549

l1-contracts/src/mock/MultiAdder.sol

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct CheatDepositArgs {
1616

1717
interface IMultiAdder {
1818
function addValidators(CheatDepositArgs[] memory _args) external;
19+
function addValidators(CheatDepositArgs[] memory _args, bool _skipFlush) external;
1920
}
2021

2122
contract MultiAdder is IMultiAdder {
@@ -32,7 +33,11 @@ contract MultiAdder is IMultiAdder {
3233
stakingAsset.approve(address(STAKING), type(uint256).max);
3334
}
3435

35-
function addValidators(CheatDepositArgs[] memory _args) external override(IMultiAdder) {
36+
function addValidators(CheatDepositArgs[] memory _args) public override(IMultiAdder) {
37+
addValidators(_args, false);
38+
}
39+
40+
function addValidators(CheatDepositArgs[] memory _args, bool _skipFlush) public override(IMultiAdder) {
3641
require(msg.sender == OWNER, NotOwner());
3742
for (uint256 i = 0; i < _args.length; i++) {
3843
STAKING.deposit(
@@ -45,7 +50,7 @@ contract MultiAdder is IMultiAdder {
4550
);
4651
}
4752

48-
if (STAKING.getCurrentEpoch() >= STAKING.getNextFlushableEpoch()) {
53+
if (!_skipFlush && STAKING.getCurrentEpoch() >= STAKING.getNextFlushableEpoch()) {
4954
STAKING.flushEntryQueue();
5055
}
5156
}

l1-contracts/test/compression/StakingQueueConfig.t.sol

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,18 @@ contract StakingQueueConfigTest is Test {
1414
using StakingQueueConfigLib for CompressedStakingQueueConfig;
1515

1616
function test_compressAndDecompress(
17-
uint64 _bootstrapValidatorSetSize,
18-
uint64 _bootstrapFlushSize,
19-
uint64 _normalFlushSizeMin,
20-
uint64 _normalFlushSizeQuotient
17+
uint32 _bootstrapValidatorSetSize,
18+
uint32 _bootstrapFlushSize,
19+
uint32 _normalFlushSizeMin,
20+
uint32 _normalFlushSizeQuotient,
21+
uint32 _maxQueueFlushSize
2122
) public pure {
2223
StakingQueueConfig memory a = StakingQueueConfig({
2324
bootstrapValidatorSetSize: _bootstrapValidatorSetSize,
2425
bootstrapFlushSize: _bootstrapFlushSize,
2526
normalFlushSizeMin: _normalFlushSizeMin,
26-
normalFlushSizeQuotient: _normalFlushSizeQuotient
27+
normalFlushSizeQuotient: _normalFlushSizeQuotient,
28+
maxQueueFlushSize: _maxQueueFlushSize
2729
});
2830

2931
CompressedStakingQueueConfig b = a.compress();
@@ -33,5 +35,6 @@ contract StakingQueueConfigTest is Test {
3335
assertEq(c.bootstrapFlushSize, a.bootstrapFlushSize, "Bootstrap flush size");
3436
assertEq(c.normalFlushSizeMin, a.normalFlushSizeMin, "Normal flush size min");
3537
assertEq(c.normalFlushSizeQuotient, a.normalFlushSizeQuotient, "Normal flush size quotient");
38+
assertEq(c.maxQueueFlushSize, a.maxQueueFlushSize, "Max queue flush size");
3639
}
3740
}

l1-contracts/test/harnesses/TestConstants.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ library TestConstants {
3131
uint256 internal constant AZTEC_ENTRY_QUEUE_FLUSH_SIZE_QUOTIENT = 2;
3232
uint256 internal constant AZTEC_ENTRY_QUEUE_BOOTSTRAP_VALIDATOR_SET_SIZE = 0;
3333
uint256 internal constant AZTEC_ENTRY_QUEUE_BOOTSTRAP_FLUSH_SIZE = 0;
34+
uint256 internal constant AZTEC_ENTRY_QUEUE_MAX_FLUSH_SIZE = 480;
3435
uint256 internal constant AZTEC_EXIT_DELAY_SECONDS = 2 * 24 * 60 * 60; // 2 days
3536
EthValue internal constant AZTEC_PROVING_COST_PER_MANA = EthValue.wrap(100);
3637

@@ -81,7 +82,8 @@ library TestConstants {
8182
bootstrapValidatorSetSize: AZTEC_ENTRY_QUEUE_BOOTSTRAP_VALIDATOR_SET_SIZE,
8283
bootstrapFlushSize: AZTEC_ENTRY_QUEUE_BOOTSTRAP_FLUSH_SIZE,
8384
normalFlushSizeMin: AZTEC_ENTRY_QUEUE_FLUSH_SIZE_MIN,
84-
normalFlushSizeQuotient: AZTEC_ENTRY_QUEUE_FLUSH_SIZE_QUOTIENT
85+
normalFlushSizeQuotient: AZTEC_ENTRY_QUEUE_FLUSH_SIZE_QUOTIENT,
86+
maxQueueFlushSize: AZTEC_ENTRY_QUEUE_MAX_FLUSH_SIZE
8587
});
8688
}
8789

0 commit comments

Comments
 (0)