Skip to content

Commit 24795e4

Browse files
committed
feat: enhance CSM report preparation and update deposit tests
1 parent 6a40719 commit 24795e4

File tree

2 files changed

+56
-72
lines changed

2 files changed

+56
-72
lines changed

tests/regression/test_csm.py

Lines changed: 54 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
EASYTRACK_CS_SET_VETTED_GATE_TREE_FACTORY,
1212
)
1313
from utils.dsm import UnvetArgs, to_bytes, set_single_guardian
14-
from utils.evm_script import encode_error
1514
from utils.staking_module import calc_module_reward_shares
1615
from utils.test.csm_helpers import csm_add_node_operator, csm_upload_keys, get_ics_members, csm_add_ics_node_operator
1716
from utils.test.deposits_helpers import fill_deposit_buffer
@@ -82,9 +81,28 @@ def strikes():
8281

8382

8483
@pytest.fixture
85-
def node_operator(csm, permissionless_gate, accounting, accounts) -> int:
86-
address = accounts[7].address
87-
return csm_add_node_operator(csm, permissionless_gate, accounting, address)
84+
def depositable_node_operator(csm, accounting, permissionless_gate, stranger):
85+
increase_staking_module_share(module_id=CSM_MODULE_ID, share_multiplier=2)
86+
csm.cleanDepositQueue(2 * csm.getNonce(), {"from": stranger.address})
87+
for queue_priority in range(1, 6):
88+
deposit_batch = csm.depositQueueItem(queue_priority, csm.depositQueuePointers(queue_priority)["head"])
89+
if deposit_batch:
90+
node_operator_id = (deposit_batch >> 192) & ((1 << 64) - 1)
91+
keys_count = (deposit_batch >> 128) & ((1 << 64) - 1)
92+
break
93+
else:
94+
address = accounts[7].address
95+
keys_count = 5
96+
node_operator_id = csm_add_node_operator(csm, permissionless_gate, accounting, address, keys_count=keys_count)
97+
return node_operator_id, keys_count
98+
99+
100+
@pytest.fixture
101+
def node_operator(depositable_node_operator, csm, accounting) -> int:
102+
node_operator, keys_count = depositable_node_operator
103+
fill_deposit_buffer(keys_count)
104+
contracts.lido.deposit(keys_count, CSM_MODULE_ID, "0x", {"from": contracts.deposit_security_module})
105+
return node_operator
88106

89107

90108
@pytest.fixture
@@ -103,28 +121,6 @@ def remove_stake_limit():
103121
contracts.lido.removeStakingLimit({"from": accounts.at(contracts.agent, force=True)})
104122

105123

106-
@pytest.fixture
107-
def deposits_to_csm(csm, pause_modules, node_operator, remove_stake_limit):
108-
(_, _, depositable) = csm.getStakingModuleSummary()
109-
fill_deposit_buffer(depositable)
110-
increase_staking_module_share(module_id=CSM_MODULE_ID, share_multiplier=2)
111-
112-
if contracts.withdrawal_queue.isBunkerModeActive():
113-
# Disable bunker mode to allow deposits
114-
web3.provider.make_request(
115-
"hardhat_setStorageAt",
116-
[
117-
contracts.withdrawal_queue.address,
118-
web3.keccak(text="lido.WithdrawalQueue.bunkerModeSinceTimestamp").hex(),
119-
web3.to_hex(2**256 - 1) # type(uint256).max
120-
],
121-
)
122-
assert not contracts.withdrawal_queue.isBunkerModeActive()
123-
124-
for i in range(0, depositable, MAX_DEPOSITS):
125-
contracts.lido.deposit(MAX_DEPOSITS, CSM_MODULE_ID, "0x", {"from": contracts.deposit_security_module})
126-
127-
128124
@pytest.fixture
129125
def ref_slot():
130126
wait_to_next_available_report_time(contracts.csm_hash_consensus)
@@ -135,14 +131,15 @@ def ref_slot():
135131
def distribute_reward_tree(node_operator, ref_slot):
136132
consensus_version = contracts.cs_fee_oracle.getConsensusVersion()
137133
oracle_version = contracts.cs_fee_oracle.getContractVersion()
138-
claimable_shares = contracts.cs_fee_distributor.totalClaimableShares()
134+
distributed_before = contracts.lido.sharesOf(contracts.cs_fee_distributor)
135+
claimed_before = contracts.cs_fee_distributor.distributedShares(node_operator)
139136

140137
rewards = ETH(0.05)
141138
oracle_report(cl_diff=rewards)
142-
distributed_shares = contracts.lido.sharesOf(contracts.cs_fee_distributor) - claimable_shares
139+
distributed_shares = contracts.lido.sharesOf(contracts.cs_fee_distributor) - distributed_before
143140
assert distributed_shares > 0
144141

145-
report, report_hash, tree = prepare_csm_report({node_operator: distributed_shares}, ref_slot)
142+
report, report_hash, tree = prepare_csm_report({node_operator: claimed_before + distributed_shares}, ref_slot, distributed_shares)
146143

147144
submitter = reach_consensus(
148145
ref_slot,
@@ -152,7 +149,7 @@ def distribute_reward_tree(node_operator, ref_slot):
152149
)
153150

154151
contracts.cs_fee_oracle.submitReportData(report, oracle_version, {"from": submitter})
155-
return tree
152+
return distributed_shares, tree.tree
156153

157154

158155
def get_sys_fee_to_eject():
@@ -185,20 +182,16 @@ def test_add_node_operator_permissionless(csm, permissionless_gate, accounting,
185182

186183

187184
@pytest.mark.usefixtures("pause_modules")
188-
def test_deposit(node_operator, csm, remove_stake_limit):
189-
(_, _, depositable_validators_count) = csm.getStakingModuleSummary()
190-
deposits_count = depositable_validators_count
191-
fill_deposit_buffer(deposits_count)
192-
increase_staking_module_share(module_id=CSM_MODULE_ID, share_multiplier=2)
193-
194-
for i in range(0, deposits_count, MAX_DEPOSITS):
195-
contracts.lido.deposit(MAX_DEPOSITS, CSM_MODULE_ID, "0x", {"from": contracts.deposit_security_module})
185+
def test_deposit(depositable_node_operator, csm, remove_stake_limit):
186+
(node_operator, keys_count) = depositable_node_operator
187+
fill_deposit_buffer(keys_count)
188+
total_deposited_before = csm.getNodeOperator(node_operator)["totalDepositedKeys"]
189+
contracts.lido.deposit(keys_count, CSM_MODULE_ID, "0x", {"from": contracts.deposit_security_module})
196190

197191
no = csm.getNodeOperator(node_operator)
198-
assert no["totalDepositedKeys"] == no["totalAddedKeys"]
192+
assert no["totalDepositedKeys"] == total_deposited_before + keys_count
199193

200194

201-
@pytest.mark.usefixtures("deposits_to_csm")
202195
def test_mint_rewards_happy_path(csm, fee_distributor):
203196
csm_shares_before = contracts.lido.sharesOf(csm)
204197
fee_distributor_shares_before = contracts.lido.sharesOf(fee_distributor)
@@ -222,7 +215,6 @@ def test_csm_target_limits(csm, node_operator):
222215
assert no["targetLimit"] == target_limit
223216

224217

225-
@pytest.mark.usefixtures("deposits_to_csm")
226218
def test_csm_report_exited(csm, node_operator, extra_data_service):
227219
total_exited = csm.getStakingModuleSummary()["totalExitedValidators"]
228220
exited_keys = 5
@@ -240,7 +232,6 @@ def test_csm_report_exited(csm, node_operator, extra_data_service):
240232
assert no["totalExitedKeys"] == exited_keys
241233

242234

243-
@pytest.mark.usefixtures("deposits_to_csm")
244235
def test_csm_get_staking_module_summary(csm, accounting, node_operator, extra_data_service, remove_stake_limit):
245236
(exited_before, deposited_before, depositable_before) = contracts.staking_router.getStakingModuleSummary(
246237
CSM_MODULE_ID
@@ -276,11 +267,10 @@ def test_csm_get_staking_module_summary(csm, accounting, node_operator, extra_da
276267
assert depositable_after == depositable_before + new_depositable
277268

278269

279-
@pytest.mark.usefixtures("deposits_to_csm")
280270
def test_csm_get_node_operator_summary(csm, node_operator, extra_data_service):
281271
total_exited = csm.getStakingModuleSummary()["totalExitedValidators"]
282272
no = csm.getNodeOperator(node_operator)
283-
exited_keys = 1
273+
exited_keys = no["totalExitedKeys"] + 1
284274
extra_data = extra_data_service.collect(
285275
{(CSM_MODULE_ID, node_operator): exited_keys}, exited_keys, exited_keys
286276
)
@@ -294,19 +284,21 @@ def test_csm_get_node_operator_summary(csm, node_operator, extra_data_service):
294284
)
295285

296286
summary = contracts.staking_router.getNodeOperatorSummary(CSM_MODULE_ID, node_operator)
297-
assert summary["targetLimitMode"] == 0
298-
assert summary["targetValidatorsCount"] == 0
287+
assert summary["targetLimitMode"] == no["targetLimitMode"]
288+
assert summary["targetValidatorsCount"] == no["targetLimit"]
299289
# DEPRECATED #
300290
assert summary["stuckValidatorsCount"] == 0
301291
assert summary["refundedValidatorsCount"] == 0
302292
assert summary["stuckPenaltyEndTimestamp"] == 0
303293
##############
304294
assert summary["totalExitedValidators"] == exited_keys
305295
assert summary["totalDepositedValidators"] == no["totalDepositedKeys"]
306-
assert summary["depositableValidatorsCount"] == 0
296+
assert summary["depositableValidatorsCount"] == no["depositableValidatorsCount"]
307297

308298

309-
def test_csm_decrease_vetted_keys(csm, node_operator, stranger):
299+
def test_csm_decrease_vetted_keys(csm, depositable_node_operator, stranger):
300+
(node_operator, keys_count) = depositable_node_operator
301+
total_added_keys = csm.getNodeOperator(node_operator)["totalAddedKeys"]
310302
block_number = web3.eth.get_block_number()
311303
block = web3.eth.get_block(block_number)
312304
staking_module_nonce = contracts.staking_router.getStakingModuleNonce(CSM_MODULE_ID)
@@ -316,18 +308,17 @@ def test_csm_decrease_vetted_keys(csm, node_operator, stranger):
316308
staking_module_id=CSM_MODULE_ID,
317309
nonce=staking_module_nonce,
318310
node_operator_ids=to_bytes(node_operator, 16),
319-
vetted_signing_keys_counts=to_bytes(1, 32),
311+
vetted_signing_keys_counts=to_bytes(total_added_keys - keys_count, 32),
320312
)
321313

322314
set_single_guardian(contracts.deposit_security_module, contracts.agent, stranger)
323315

324316
contracts.deposit_security_module.unvetSigningKeys(*unvet_args.to_tuple(), (0, 0), {"from": stranger.address})
325317

326318
no = csm.getNodeOperator(node_operator)
327-
assert no["totalVettedKeys"] == 1
319+
assert no["totalVettedKeys"] == total_added_keys - keys_count
328320

329321

330-
@pytest.mark.usefixtures("deposits_to_csm")
331322
def test_csm_penalize_node_operator(csm, accounting, node_operator, helpers):
332323
bond_shares_before = accounting.getBondShares(node_operator)
333324
withdrawal_info = (node_operator, 0, ETH(30))
@@ -337,7 +328,6 @@ def test_csm_penalize_node_operator(csm, accounting, node_operator, helpers):
337328
assert accounting.getBondShares(node_operator) == bond_shares_before - burnt_shares
338329

339330

340-
@pytest.mark.usefixtures("deposits_to_csm")
341331
def test_csm_eth_bond(csm, accounting, node_operator):
342332
manager_address = csm.getNodeOperator(node_operator)["managerAddress"]
343333
set_balance_in_wei(manager_address, ETH(2))
@@ -348,7 +338,6 @@ def test_csm_eth_bond(csm, accounting, node_operator):
348338
assert accounting.getBondShares(node_operator) == bond_shares_before + shares
349339

350340

351-
@pytest.mark.usefixtures("deposits_to_csm")
352341
def test_csm_steth_bond(csm, accounting, node_operator):
353342
manager_address = csm.getNodeOperator(node_operator)["managerAddress"]
354343
set_balance_in_wei(manager_address, ETH(2))
@@ -362,7 +351,6 @@ def test_csm_steth_bond(csm, accounting, node_operator):
362351
assert accounting.getBondShares(node_operator) == bond_shares_before + shares
363352

364353

365-
@pytest.mark.usefixtures("deposits_to_csm")
366354
def test_csm_wsteth_bond(csm, accounting, node_operator):
367355
manager_address = csm.getNodeOperator(node_operator)["managerAddress"]
368356
set_balance_in_wei(manager_address, ETH(2))
@@ -381,25 +369,23 @@ def test_csm_wsteth_bond(csm, accounting, node_operator):
381369
assert accounting.getBondShares(node_operator) == bond_shares_before + shares
382370

383371

384-
@pytest.mark.usefixtures("deposits_to_csm")
385372
def test_csm_claim_rewards_steth(csm, accounting, node_operator, ref_slot):
386373
reward_address = csm.getNodeOperator(node_operator)["rewardAddress"]
387374
shares_before = contracts.lido.sharesOf(reward_address)
388-
accounting_shares_before = contracts.lido.sharesOf(contracts.cs_accounting)
389375

390-
tree = distribute_reward_tree(node_operator, ref_slot).tree
391-
shares = tree.values[0]["value"][1]
392-
proof = list(tree.get_proof(tree.find(tree.leaf((node_operator, shares)))))
376+
distributed_shares, tree = distribute_reward_tree(node_operator, ref_slot)
377+
cumulative_shares = tree.values[0]["value"][1]
378+
proof = list(tree.get_proof(tree.find(tree.leaf((node_operator, cumulative_shares)))))
379+
claimable_bond_shares_before = accounting.getClaimableBondShares(node_operator)
380+
381+
accounting.claimRewardsStETH(node_operator, ETH(999), cumulative_shares, proof, {"from": reward_address})
393382

394-
accounting.claimRewardsStETH(node_operator, ETH(1), shares, proof, {"from": reward_address})
395383
shares_after = contracts.lido.sharesOf(reward_address)
396-
accounting_shares_after = contracts.lido.sharesOf(contracts.cs_accounting)
397-
assert shares_after == shares_before + (accounting_shares_before + shares - accounting_shares_after)
384+
assert shares_after == shares_before + distributed_shares + claimable_bond_shares_before
398385

399386

400-
@pytest.mark.usefixtures("deposits_to_csm")
401387
def test_csm_claim_rewards_wsteth(csm, accounting, node_operator, ref_slot):
402-
tree = distribute_reward_tree(node_operator, ref_slot).tree
388+
_, tree = distribute_reward_tree(node_operator, ref_slot)
403389
shares = tree.values[0]["value"][1]
404390
proof = list(tree.get_proof(tree.find(tree.leaf((node_operator, shares)))))
405391
reward_address = csm.getNodeOperator(node_operator)["rewardAddress"]
@@ -409,9 +395,8 @@ def test_csm_claim_rewards_wsteth(csm, accounting, node_operator, ref_slot):
409395
assert contracts.wsteth.balanceOf(reward_address) > wsteth_before
410396

411397

412-
@pytest.mark.usefixtures("deposits_to_csm")
413398
def test_csm_claim_rewards_eth(csm, accounting, node_operator, ref_slot):
414-
tree = distribute_reward_tree(node_operator, ref_slot).tree
399+
_, tree = distribute_reward_tree(node_operator, ref_slot)
415400
shares = tree.values[0]["value"][1]
416401
proof = list(tree.get_proof(tree.find(tree.leaf((node_operator, shares)))))
417402
reward_address = csm.getNodeOperator(node_operator)["rewardAddress"]
@@ -423,10 +408,12 @@ def test_csm_claim_rewards_eth(csm, accounting, node_operator, ref_slot):
423408

424409

425410
def test_csm_remove_key(csm, parameters_registry, accounting, node_operator):
411+
csm_upload_keys(csm, accounting, node_operator, 1)
412+
426413
no = csm.getNodeOperator(node_operator)
427414
keys_before = no["totalAddedKeys"]
428415
manager_address = csm.getNodeOperator(node_operator)["managerAddress"]
429-
tx = csm.removeKeys(node_operator, 0, 1, {"from": manager_address})
416+
tx = csm.removeKeys(node_operator, keys_before - 1, 1, {"from": manager_address})
430417

431418
assert "KeyRemovalChargeApplied" in tx.events
432419
assert "BondCharged" in tx.events
@@ -439,7 +426,6 @@ def test_csm_remove_key(csm, parameters_registry, accounting, node_operator):
439426
assert no["totalAddedKeys"] == keys_before - 1
440427

441428

442-
@pytest.mark.usefixtures("deposits_to_csm")
443429
def test_eject_bad_performer(csm, ejector, strikes, node_operator, stranger):
444430
index_to_eject = 0
445431
pubkey_to_eject = csm.getSigningKeys(node_operator, index_to_eject, 1)
@@ -480,7 +466,6 @@ def test_eject_bad_performer(csm, ejector, strikes, node_operator, stranger):
480466
assert tx.events["TriggeredExitFeeRecorded"]["withdrawalRequestRecordedFee"] == eject_payment_value
481467

482468

483-
@pytest.mark.usefixtures("deposits_to_csm")
484469
def test_voluntary_eject(csm, ejector, node_operator):
485470
eject_payment_value = get_sys_fee_to_eject()
486471
operator_address = csm.getNodeOperator(node_operator)["rewardAddress"]
@@ -517,7 +502,6 @@ def test_on_validator_exit_triggered(csm, node_operator):
517502
assert tx.events["TriggeredExitFeeRecorded"]["withdrawalRequestRecordedFee"] == eject_payment_value
518503

519504

520-
@pytest.mark.usefixtures("deposits_to_csm")
521505
def test_easy_track_csm_settle_el_stealing_penalty(csm, accounting, node_operator, stranger):
522506
manager_address = csm.getNodeOperator(node_operator)["managerAddress"]
523507
set_balance_in_wei(manager_address, ETH(2))

utils/test/oracle_report_helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ def prepare_exit_bus_report(validators_to_exit, ref_slot):
108108
return report, report_hash
109109

110110

111-
def prepare_csm_report(node_operators_rewards: dict, ref_slot):
111+
def prepare_csm_report(node_operators_rewards: dict, ref_slot, distributed_shares):
112112
consensus_version = contracts.cs_fee_oracle.getConsensusVersion()
113113
shares = node_operators_rewards.copy()
114114
if len(shares) < 2:
@@ -126,7 +126,7 @@ def prepare_csm_report(node_operators_rewards: dict, ref_slot):
126126
tree.root,
127127
str(tree_cid),
128128
str(log_cid),
129-
sum(shares.values()),
129+
distributed_shares,
130130
0, # rebate
131131
HexBytes(ZERO_HASH), # strikesTreeRoot
132132
"", # strikesTreeCid

0 commit comments

Comments
 (0)