Skip to content

Commit c44160e

Browse files
committed
fix conversion, block-based fork check, update test
1 parent 8c87bb0 commit c44160e

File tree

5 files changed

+139
-47
lines changed

5 files changed

+139
-47
lines changed

internal/config/config.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -114,10 +114,10 @@ type RpcConfig struct {
114114
}
115115

116116
type RewardsConfig struct {
117-
ValidateRewardsRoot bool
118-
GenerateStakerOperatorsTable bool
119-
CalculateRewardsDaily bool
120-
WithdrawalQueueWindow float64 // Duration in days for withdrawal queue period (14.0 for mainnet, 0.0069 for testnet/preprod ~10 min)
117+
ValidateRewardsRoot bool
118+
GenerateStakerOperatorsTable bool
119+
CalculateRewardsDaily bool
120+
WithdrawalQueueWindow float64 // Duration in days for withdrawal queue period (14.0 for mainnet, 0.0069 for testnet/preprod ~10 min)
121121
}
122122

123123
type StatsdConfig struct {
@@ -237,10 +237,10 @@ var (
237237

238238
SnapshotOutputMetadataFile = "generate-metadata-file"
239239

240-
RewardsValidateRewardsRoot = "rewards.validate_rewards_root"
241-
RewardsGenerateStakerOperatorsTable = "rewards.generate_staker_operators_table"
242-
RewardsCalculateRewardsDaily = "rewards.calculate_rewards_daily"
243-
RewardsWithdrawalQueueWindow = "rewards.withdrawal_queue_window"
240+
RewardsValidateRewardsRoot = "rewards.validate_rewards_root"
241+
RewardsGenerateStakerOperatorsTable = "rewards.generate_staker_operators_table"
242+
RewardsCalculateRewardsDaily = "rewards.calculate_rewards_daily"
243+
RewardsWithdrawalQueueWindow = "rewards.withdrawal_queue_window"
244244

245245
EthereumRpcBaseUrl = "ethereum.rpc_url"
246246
EthereumRpcContractCallBatchSize = "ethereum.contract_call_batch_size"
@@ -325,10 +325,10 @@ func NewConfig() *Config {
325325
},
326326

327327
Rewards: RewardsConfig{
328-
ValidateRewardsRoot: viper.GetBool(normalizeFlagName(RewardsValidateRewardsRoot)),
328+
ValidateRewardsRoot: viper.GetBool(normalizeFlagName(RewardsValidateRewardsRoot)),
329329
GenerateStakerOperatorsTable: viper.GetBool(normalizeFlagName(RewardsGenerateStakerOperatorsTable)),
330-
CalculateRewardsDaily: viper.GetBool(normalizeFlagName(RewardsCalculateRewardsDaily)),
331-
WithdrawalQueueWindow: FloatWithDefault(viper.GetFloat64(normalizeFlagName(RewardsWithdrawalQueueWindow)), getDefaultWithdrawalQueueDuration(chain)),
330+
CalculateRewardsDaily: viper.GetBool(normalizeFlagName(RewardsCalculateRewardsDaily)),
331+
WithdrawalQueueWindow: FloatWithDefault(viper.GetFloat64(normalizeFlagName(RewardsWithdrawalQueueWindow)), getDefaultWithdrawalQueueDuration(chain)),
332332
},
333333

334334
DataDogConfig: DataDogConfig{

pkg/eigenState/precommitProcessors/slashingProcessor/slashing.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ func (sp *SlashingProcessor) createSlashingAdjustments(slashEvent *SlashingEvent
180180
OR (qsw.block_number = @slashBlockNumber AND qsw.log_index < @logIndex)
181181
)
182182
-- Still within withdrawal queue window (not yet completable)
183-
AND DATE(b_queued.block_time) + (@withdrawalQueueWindow * INTERVAL '1 day') > (
183+
AND b_queued.block_time + (@withdrawalQueueWindow * INTERVAL '1 day') > (
184184
SELECT block_time FROM blocks WHERE number = @blockNumber
185185
)
186186
-- Backwards compatibility: only process records with valid data
@@ -205,13 +205,13 @@ func (sp *SlashingProcessor) createSlashingAdjustments(slashEvent *SlashingEvent
205205

206206
var adjustments []AdjustmentRecord
207207
err := sp.grm.Raw(query, map[string]any{
208-
"slashBlockNumber": blockNumber,
209-
"wadSlashed": slashEvent.WadSlashed,
210-
"blockNumber": blockNumber,
211-
"transactionHash": slashEvent.TransactionHash,
212-
"logIndex": slashEvent.LogIndex,
213-
"operator": slashEvent.Operator,
214-
"strategy": slashEvent.Strategy,
208+
"slashBlockNumber": blockNumber,
209+
"wadSlashed": slashEvent.WadSlashed,
210+
"blockNumber": blockNumber,
211+
"transactionHash": slashEvent.TransactionHash,
212+
"logIndex": slashEvent.LogIndex,
213+
"operator": slashEvent.Operator,
214+
"strategy": slashEvent.Strategy,
215215
"withdrawalQueueWindow": sp.globalConfig.Rewards.WithdrawalQueueWindow,
216216
}).Scan(&adjustments).Error
217217

pkg/rewards/operatorAllocationSnapshots.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,24 @@ func (r *RewardsCalculator) GenerateAndInsertOperatorAllocationSnapshots(snapsho
109109
return err
110110
}
111111

112+
// Get the block number for the cutoff date to make block-based fork decision
113+
var cutoffBlockNumber uint64
114+
err = r.grm.Raw(`
115+
SELECT number
116+
FROM blocks
117+
WHERE block_time <= ?
118+
ORDER BY number DESC
119+
LIMIT 1
120+
`, snapshotDate).Scan(&cutoffBlockNumber).Error
121+
if err != nil {
122+
r.logger.Sugar().Errorw("Failed to get cutoff block number", "error", err, "snapshotDate", snapshotDate)
123+
return err
124+
}
125+
126+
// Use block-based fork check for backwards compatibility
112127
useSabineRounding := false
113128
if sabineFork, exists := forks[config.RewardsFork_Sabine]; exists {
114-
// Use new rounding logic if snapshot date is on or after Sabine fork
115-
useSabineRounding = snapshotDate >= sabineFork.Date
129+
useSabineRounding = cutoffBlockNumber >= sabineFork.BlockNumber
116130
}
117131

118132
query, err := rewardsUtils.RenderQueryTemplate(operatorAllocationSnapshotsQuery, map[string]interface{}{

pkg/rewards/stakerShareSnapshots.go

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const stakerShareSnapshotsQuery = `
6060
INNER JOIN blocks b_queued ON qsw.block_number = b_queued.number
6161
WHERE
6262
-- Still within withdrawal queue window (not yet completable)
63-
DATE(b_queued.block_time) + INTERVAL '{{.withdrawalQueueWindow}} days' > DATE '{{.snapshotDate}}'
63+
b_queued.block_time + INTERVAL '{{.withdrawalQueueWindow}} days' > TIMESTAMP '{{.snapshotDate}}'
6464
-- Backwards compatibility: only process records with valid data
6565
AND qsw.staker IS NOT NULL
6666
AND qsw.strategy IS NOT NULL
@@ -125,9 +125,24 @@ func (r *RewardsCalculator) GenerateAndInsertStakerShareSnapshots(snapshotDate s
125125
return err
126126
}
127127

128+
// Get the block number for the cutoff date to make block-based fork decision
129+
var cutoffBlockNumber uint64
130+
err = r.grm.Raw(`
131+
SELECT number
132+
FROM blocks
133+
WHERE block_time <= ?
134+
ORDER BY number DESC
135+
LIMIT 1
136+
`, snapshotDate).Scan(&cutoffBlockNumber).Error
137+
if err != nil {
138+
r.logger.Sugar().Errorw("Failed to get cutoff block number", "error", err, "snapshotDate", snapshotDate)
139+
return err
140+
}
141+
142+
// Use block-based fork check for backwards compatibility
128143
useSabineFork := false
129144
if sabineFork, exists := forks[config.RewardsFork_Sabine]; exists {
130-
useSabineFork = snapshotDate >= sabineFork.Date
145+
useSabineFork = cutoffBlockNumber >= sabineFork.BlockNumber
131146
}
132147

133148
query, err := rewardsUtils.RenderQueryTemplate(stakerShareSnapshotsQuery, map[string]interface{}{

pkg/rewards/stakerShareSnapshots_test.go

Lines changed: 87 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -301,27 +301,24 @@ func Test_StakerShareSnapshots_WithdrawalAndSlashing(t *testing.T) {
301301
assert.Nil(t, err)
302302

303303
// Verify we have unique entries for each time period
304-
assert.GreaterOrEqual(t, len(snapshots), 3, "Should have at least 3 unique snapshot entries")
305-
306-
// TODO: Uncomment these assertions once the full pipeline is working
307-
// These verify the exact share values at each timestamp in the T0-T3 scenario
308-
//
309304
// Expected calculations:
310305
// T0: 200 shares (initial state)
311306
// T1: 200 shares (withdrawal queued for 50, but still earning: 150 base + 50 queued = 200)
312307
// T2: 150 shares (25% slash: 112.5 base + 37.5 queued = 150)
313-
// T3: 137.5 shares (withdrawal completable: 112.5 base + 25 remaining queued = 137.5)
314-
// Note: The slashed portion of queued withdrawal (12.5) is subtracted at T3
315-
//
316-
// Note: The slashingProcessor must run and populate queued_withdrawal_slashing_adjustments
317-
// for these values to be correct. Currently it may not run automatically during test.
318-
//
319-
// if len(snapshots) >= 4 {
320-
// assert.Equal(t, "200000000000000000000", snapshots[0].Shares, "T0: Alice should have 200 shares")
321-
// assert.Equal(t, "200000000000000000000", snapshots[1].Shares, "T1: Alice should still have 200 shares (withdrawal queued)")
322-
// assert.Equal(t, "150000000000000000000", snapshots[2].Shares, "T2: Alice should have 150 shares (slashed 25%)")
323-
// assert.Equal(t, "137500000000000000000", snapshots[3].Shares, "T3: Alice should have 137.5 shares (withdrawal completable)")
324-
// }
308+
// T3: 112.5 shares (withdrawal completable: 112.5 base, queued no longer counts)
309+
310+
assert.Equal(t, 4, len(snapshots), "Should have exactly 4 snapshots")
311+
assert.Equal(t, "200000000000000000000", snapshots[0].Shares, "T0: Alice should have 200 shares")
312+
assert.Equal(t, t0.Format(time.DateOnly), snapshots[0].Snapshot.Format(time.DateOnly))
313+
314+
assert.Equal(t, "200000000000000000000", snapshots[1].Shares, "T1: Alice should have 200 shares (150 base + 50 queued)")
315+
assert.Equal(t, t1.Format(time.DateOnly), snapshots[1].Snapshot.Format(time.DateOnly))
316+
317+
assert.Equal(t, "150000000000000000000", snapshots[2].Shares, "T2: Alice should have 150 shares (112.5 base + 37.5 queued after 25% slash)")
318+
assert.Equal(t, t2.Format(time.DateOnly), snapshots[2].Snapshot.Format(time.DateOnly))
319+
320+
assert.Equal(t, "112500000000000000000", snapshots[3].Shares, "T3: Alice should have 112.5 shares (withdrawal completable)")
321+
assert.Equal(t, t3.Format(time.DateOnly), snapshots[3].Snapshot.Format(time.DateOnly))
325322

326323
t.Logf("Generated %d snapshots for Alice:", len(snapshots))
327324
for i, snap := range snapshots {
@@ -470,7 +467,18 @@ func Test_StakerShareSnapshots_QueuedThenSlashed(t *testing.T) {
470467
// T2: 50 shares (35 base + 15 queued after 50% slash)
471468
// T3: 35 shares (queued withdrawal no longer counts)
472469

473-
assert.GreaterOrEqual(t, len(snapshots), 3, "Should have at least 3 unique snapshots")
470+
assert.Equal(t, 4, len(snapshots), "Should have exactly 4 snapshots")
471+
assert.Equal(t, "100000000000000000000", snapshots[0].Shares, "T0: Should have 100 shares")
472+
assert.Equal(t, t0.Format(time.DateOnly), snapshots[0].Snapshot.Format(time.DateOnly))
473+
474+
assert.Equal(t, "100000000000000000000", snapshots[1].Shares, "T1: Should have 100 shares (70 base + 30 queued)")
475+
assert.Equal(t, t1.Format(time.DateOnly), snapshots[1].Snapshot.Format(time.DateOnly))
476+
477+
assert.Equal(t, "50000000000000000000", snapshots[2].Shares, "T2: Should have 50 shares (35 base + 15 queued after 50% slash)")
478+
assert.Equal(t, t2.Format(time.DateOnly), snapshots[2].Snapshot.Format(time.DateOnly))
479+
480+
assert.Equal(t, "35000000000000000000", snapshots[3].Shares, "T3: Should have 35 shares (queued withdrawal no longer counts)")
481+
assert.Equal(t, t3.Format(time.DateOnly), snapshots[3].Snapshot.Format(time.DateOnly))
474482
})
475483
}
476484

@@ -607,7 +615,21 @@ func Test_StakerShareSnapshots_MultipleSlashingEvents(t *testing.T) {
607615
// T3: 60 shares (36 base + 24 queued, cumulative: 0.8 * 0.75 = 0.6)
608616
// T4: 36 shares (queued withdrawal no longer counts)
609617

610-
assert.GreaterOrEqual(t, len(snapshots), 4, "Should have at least 4 unique snapshots")
618+
assert.Equal(t, 5, len(snapshots), "Should have exactly 5 snapshots")
619+
assert.Equal(t, "100000000000000000000", snapshots[0].Shares, "T0: Should have 100 shares")
620+
assert.Equal(t, t0.Format(time.DateOnly), snapshots[0].Snapshot.Format(time.DateOnly))
621+
622+
assert.Equal(t, "100000000000000000000", snapshots[1].Shares, "T1: Should have 100 shares (60 base + 40 queued)")
623+
assert.Equal(t, t1.Format(time.DateOnly), snapshots[1].Snapshot.Format(time.DateOnly))
624+
625+
assert.Equal(t, "80000000000000000000", snapshots[2].Shares, "T2: Should have 80 shares (48 base + 32 queued after 20% slash)")
626+
assert.Equal(t, t2.Format(time.DateOnly), snapshots[2].Snapshot.Format(time.DateOnly))
627+
628+
assert.Equal(t, "60000000000000000000", snapshots[3].Shares, "T3: Should have 60 shares (36 base + 24 queued, cumulative 0.8 * 0.75)")
629+
assert.Equal(t, t3.Format(time.DateOnly), snapshots[3].Snapshot.Format(time.DateOnly))
630+
631+
assert.Equal(t, "36000000000000000000", snapshots[4].Shares, "T4: Should have 36 shares (queued withdrawal no longer counts)")
632+
assert.Equal(t, t4.Format(time.DateOnly), snapshots[4].Snapshot.Format(time.DateOnly))
611633
})
612634
}
613635

@@ -731,6 +753,19 @@ func Test_StakerShareSnapshots_CompletedBeforeSlash(t *testing.T) {
731753
// T2: 75 shares (queued withdrawal completable, no longer counts)
732754
// T3: 52.5 shares (75 * 0.7, slash doesn't affect completed withdrawal)
733755

756+
assert.Equal(t, 4, len(snapshots), "Should have exactly 4 snapshots")
757+
assert.Equal(t, "100000000000000000000", snapshots[0].Shares, "T0: Should have 100 shares")
758+
assert.Equal(t, t0.Format(time.DateOnly), snapshots[0].Snapshot.Format(time.DateOnly))
759+
760+
assert.Equal(t, "100000000000000000000", snapshots[1].Shares, "T1: Should have 100 shares (75 base + 25 queued)")
761+
assert.Equal(t, t1.Format(time.DateOnly), snapshots[1].Snapshot.Format(time.DateOnly))
762+
763+
assert.Equal(t, "75000000000000000000", snapshots[2].Shares, "T2: Should have 75 shares (queued withdrawal completable)")
764+
assert.Equal(t, t2.Format(time.DateOnly), snapshots[2].Snapshot.Format(time.DateOnly))
765+
766+
assert.Equal(t, "52500000000000000000", snapshots[3].Shares, "T3: Should have 52.5 shares (75 * 0.7 after 30% slash)")
767+
assert.Equal(t, t3.Format(time.DateOnly), snapshots[3].Snapshot.Format(time.DateOnly))
768+
734769
// Verify no adjustment records were created for this withdrawal
735770
var adjustmentCount int64
736771
err = grm.Raw(`
@@ -883,12 +918,29 @@ func Test_StakerShareSnapshots_MultipleQueuedWithdrawals(t *testing.T) {
883918
// Expected:
884919
// T0: 200 shares
885920
// T1: 200 shares (150 base + 50 queued)
886-
// T2: 160 shares (120 base + 40 queued, 50 * 0.8 = 40)
887-
// T3: 150 shares (96 base + 32 queued first + 30 queued second, note: 120 * 0.8 = 96 base)
888-
// T4: 126 shares (96 base + 30 queued second, first withdrawal completable)
921+
// T2: 160 shares (120 base + 40 queued after 20% slash, 150 * 0.8 = 120, 50 * 0.8 = 40)
922+
// T3: 166 shares (96 base + 40 first queued + 30 second queued, 120 * 0.8 = 96 base)
923+
// T4: 126 shares (96 base + 30 second queued, first withdrawal completable)
889924
// T5: 96 shares (only base shares remain)
890925

891-
assert.GreaterOrEqual(t, len(snapshots), 5, "Should have at least 5 unique snapshots")
926+
assert.Equal(t, 6, len(snapshots), "Should have exactly 6 snapshots")
927+
assert.Equal(t, "200000000000000000000", snapshots[0].Shares, "T0: Should have 200 shares")
928+
assert.Equal(t, t0.Format(time.DateOnly), snapshots[0].Snapshot.Format(time.DateOnly))
929+
930+
assert.Equal(t, "200000000000000000000", snapshots[1].Shares, "T1: Should have 200 shares (150 base + 50 queued)")
931+
assert.Equal(t, t1.Format(time.DateOnly), snapshots[1].Snapshot.Format(time.DateOnly))
932+
933+
assert.Equal(t, "160000000000000000000", snapshots[2].Shares, "T2: Should have 160 shares (120 base + 40 queued after 20% slash)")
934+
assert.Equal(t, t2.Format(time.DateOnly), snapshots[2].Snapshot.Format(time.DateOnly))
935+
936+
assert.Equal(t, "166000000000000000000", snapshots[3].Shares, "T3: Should have 166 shares (96 base + 40 first queued + 30 second queued)")
937+
assert.Equal(t, t3.Format(time.DateOnly), snapshots[3].Snapshot.Format(time.DateOnly))
938+
939+
assert.Equal(t, "126000000000000000000", snapshots[4].Shares, "T4: Should have 126 shares (96 base + 30 second queued)")
940+
assert.Equal(t, t4.Format(time.DateOnly), snapshots[4].Snapshot.Format(time.DateOnly))
941+
942+
assert.Equal(t, "96000000000000000000", snapshots[5].Shares, "T5: Should have 96 shares (only base)")
943+
assert.Equal(t, t5.Format(time.DateOnly), snapshots[5].Snapshot.Format(time.DateOnly))
892944

893945
// Verify adjustments were created for the first withdrawal only
894946
var adjustmentCount int64
@@ -1139,7 +1191,18 @@ func Test_StakerShareSnapshots_FullSlash(t *testing.T) {
11391191
// T2: 0 shares (100% slashed: 0 base + 0 queued)
11401192
// T3: 0 shares (nothing left)
11411193

1142-
assert.GreaterOrEqual(t, len(snapshots), 3, "Should have at least 3 unique snapshots")
1194+
assert.Equal(t, 4, len(snapshots), "Should have exactly 4 snapshots")
1195+
assert.Equal(t, "100000000000000000000", snapshots[0].Shares, "T0: Should have 100 shares")
1196+
assert.Equal(t, t0.Format(time.DateOnly), snapshots[0].Snapshot.Format(time.DateOnly))
1197+
1198+
assert.Equal(t, "100000000000000000000", snapshots[1].Shares, "T1: Should have 100 shares (60 base + 40 queued)")
1199+
assert.Equal(t, t1.Format(time.DateOnly), snapshots[1].Snapshot.Format(time.DateOnly))
1200+
1201+
assert.Equal(t, "0", snapshots[2].Shares, "T2: Should have 0 shares (100% slashed)")
1202+
assert.Equal(t, t2.Format(time.DateOnly), snapshots[2].Snapshot.Format(time.DateOnly))
1203+
1204+
assert.Equal(t, "0", snapshots[3].Shares, "T3: Should have 0 shares (nothing left)")
1205+
assert.Equal(t, t3.Format(time.DateOnly), snapshots[3].Snapshot.Format(time.DateOnly))
11431206

11441207
// Verify slash multiplier is 0 for the queued withdrawal
11451208
var multiplier string

0 commit comments

Comments
 (0)