Skip to content

Commit 41d0383

Browse files
committed
fix
1 parent 4d8299b commit 41d0383

File tree

2 files changed

+263
-10
lines changed

2 files changed

+263
-10
lines changed

pkg/rewards/operatorAllocationSnapshots.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const operatorAllocationSnapshotsQuery = `
1111
SELECT *,
1212
ROW_NUMBER() OVER (PARTITION BY operator, avs, strategy, operator_set_id, cast(block_time AS DATE) ORDER BY block_time DESC, log_index DESC) AS rn
1313
FROM operator_allocations oa
14-
INNER JOIN blocks b ON oa.block_number = b.number
14+
INNER JOIN blocks b ON oa.effective_block = b.number
1515
WHERE b.block_time < TIMESTAMP '{{.cutoffDate}}'
1616
),
1717
-- Get the latest record for each day
@@ -42,8 +42,8 @@ const operatorAllocationSnapshotsQuery = `
4242
-- Deallocation (decrease or no change): Round DOWN to current day
4343
CASE
4444
WHEN LAG(magnitude) OVER (PARTITION BY operator, avs, strategy, operator_set_id ORDER BY block_time, block_number, log_index) IS NULL THEN
45-
-- First allocation: round down to current day (conservative default)
46-
date_trunc('day', block_time)
45+
-- First allocation: round up to next day
46+
date_trunc('day', block_time) + INTERVAL '1' day
4747
WHEN magnitude > LAG(magnitude) OVER (PARTITION BY operator, avs, strategy, operator_set_id ORDER BY block_time, block_number, log_index) THEN
4848
-- Increase: round up to next day
4949
date_trunc('day', block_time) + INTERVAL '1' day

pkg/rewards/operatorAllocationSnapshots_test.go

Lines changed: 260 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -124,19 +124,272 @@ func Test_OperatorAllocationSnapshots(t *testing.T) {
124124
assert.True(t, len(snapshots) > 0, "Expected snapshots to be generated")
125125

126126
// Verify rounding behavior:
127-
// - First allocation (1000) at block 100 (2024-11-14 10:00) should have snapshot starting 2024-11-15
128-
// - Increase (1500) at block 101 (2024-11-14 15:00) should have snapshot starting 2024-11-15 (next day after 2024-11-14)
129-
// - Decrease (500) at block 102 (2024-11-15 12:00) should have snapshot starting 2024-11-15 (same day)
127+
// - Both block 100 (magnitude 1000) and block 101 (magnitude 1500) occur on 2024-11-14
128+
// - SQL takes the latest record per day (block 101 with magnitude 1500)
129+
// - This allocation is an increase, so it rounds up to 2024-11-15
130+
// - Decrease (500) at block 102 (2024-11-15 12:00) rounds down to 2024-11-15 (same day)
130131

131-
// Find the snapshot with magnitude 1000
132-
var mag1000Count int64
132+
// Find the snapshot with magnitude 1500 (latest allocation on 2024-11-14)
133+
var mag1500Count int64
134+
res = grm.Raw(`
135+
SELECT COUNT(*) FROM operator_allocation_snapshots
136+
WHERE operator = ? AND avs = ? AND strategy = ? AND operator_set_id = ?
137+
AND magnitude = '1500' AND snapshot = ?
138+
`, "0xoperator1", "0xavs1", "0xstrategy1", 1, "2024-11-15").Scan(&mag1500Count)
139+
assert.Nil(t, res.Error)
140+
assert.True(t, mag1500Count > 0, "Expected magnitude 1500 to have snapshot on 2024-11-15 (latest record on 2024-11-14, rounded up)")
141+
})
142+
143+
t.Run("Basic allocation round up test", func(t *testing.T) {
144+
// Create test block
145+
blockTime := time.Date(2024, 12, 1, 14, 30, 0, 0, time.UTC)
146+
blockNum := uint64(200)
147+
res := grm.Exec(`
148+
INSERT INTO blocks (number, hash, block_time, block_date, state_root, created_at, updated_at)
149+
VALUES (?, ?, ?, ?, '', NOW(), NOW())
150+
`, blockNum, fmt.Sprintf("hash_%d", blockNum), blockTime, blockTime.Format("2006-01-02"))
151+
assert.Nil(t, res.Error)
152+
153+
// First allocation should round up to next day (2024-12-02)
154+
res = grm.Exec(`
155+
INSERT INTO operator_allocations (operator, avs, strategy, operator_set_id, magnitude, effective_block, block_number, transaction_hash, log_index, created_at, updated_at)
156+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
157+
`, "0xoperator2", "0xavs2", "0xstrategy2", 2, "2000", blockNum, blockNum, "tx_200", 1)
158+
assert.Nil(t, res.Error)
159+
160+
sog := stakerOperators.NewStakerOperatorGenerator(grm, l, cfg)
161+
calculator, err := NewRewardsCalculator(cfg, grm, nil, sog, sink, l)
162+
assert.Nil(t, err)
163+
164+
err = calculator.GenerateAndInsertOperatorAllocationSnapshots("2024-12-05")
165+
assert.Nil(t, err)
166+
167+
// Verify first allocation rounds up to 2024-12-02
168+
var count int64
169+
res = grm.Raw(`
170+
SELECT COUNT(*) FROM operator_allocation_snapshots
171+
WHERE operator = ? AND avs = ? AND strategy = ? AND operator_set_id = ?
172+
AND magnitude = '2000' AND snapshot = ?
173+
`, "0xoperator2", "0xavs2", "0xstrategy2", 2, "2024-12-02").Scan(&count)
174+
assert.Nil(t, res.Error)
175+
assert.True(t, count > 0, "Expected first allocation to round up to 2024-12-02")
176+
})
177+
178+
t.Run("Multiple allocations same day uses latest", func(t *testing.T) {
179+
// Create blocks - all on same day
180+
baseDate := time.Date(2024, 12, 10, 0, 0, 0, 0, time.UTC)
181+
blocks := []struct {
182+
number uint64
183+
hour int
184+
magnitude string
185+
}{
186+
{300, 8, "3000"}, // 08:00
187+
{301, 12, "3500"}, // 12:00
188+
{302, 18, "4000"}, // 18:00 - latest, should be used
189+
}
190+
191+
for _, b := range blocks {
192+
blockTime := baseDate.Add(time.Duration(b.hour) * time.Hour)
193+
res := grm.Exec(`
194+
INSERT INTO blocks (number, hash, block_time, block_date, state_root, created_at, updated_at)
195+
VALUES (?, ?, ?, ?, '', NOW(), NOW())
196+
`, b.number, fmt.Sprintf("hash_%d", b.number), blockTime, blockTime.Format("2006-01-02"))
197+
assert.Nil(t, res.Error)
198+
199+
res = grm.Exec(`
200+
INSERT INTO operator_allocations (operator, avs, strategy, operator_set_id, magnitude, effective_block, block_number, transaction_hash, log_index, created_at, updated_at)
201+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
202+
`, "0xoperator3", "0xavs3", "0xstrategy3", 3, b.magnitude, b.number, b.number, fmt.Sprintf("tx_%d", b.number), 1)
203+
assert.Nil(t, res.Error)
204+
}
205+
206+
sog := stakerOperators.NewStakerOperatorGenerator(grm, l, cfg)
207+
calculator, err := NewRewardsCalculator(cfg, grm, nil, sog, sink, l)
208+
assert.Nil(t, err)
209+
210+
err = calculator.GenerateAndInsertOperatorAllocationSnapshots("2024-12-15")
211+
assert.Nil(t, err)
212+
213+
// Verify only the latest allocation (4000) is used, rounded up to 2024-12-11
214+
var count int64
215+
res := grm.Raw(`
216+
SELECT COUNT(*) FROM operator_allocation_snapshots
217+
WHERE operator = ? AND avs = ? AND strategy = ? AND operator_set_id = ?
218+
AND magnitude = '4000' AND snapshot = ?
219+
`, "0xoperator3", "0xavs3", "0xstrategy3", 3, "2024-12-11").Scan(&count)
220+
assert.Nil(t, res.Error)
221+
assert.True(t, count > 0, "Expected latest allocation (4000) to be used, rounded up to 2024-12-11")
222+
223+
// Verify earlier allocations are not present
224+
res = grm.Raw(`
225+
SELECT COUNT(*) FROM operator_allocation_snapshots
226+
WHERE operator = ? AND avs = ? AND strategy = ? AND operator_set_id = ?
227+
AND magnitude IN ('3000', '3500')
228+
`, "0xoperator3", "0xavs3", "0xstrategy3", 3).Scan(&count)
229+
assert.Nil(t, res.Error)
230+
assert.Equal(t, int64(0), count, "Expected earlier allocations to not be present")
231+
})
232+
233+
t.Run("Allocate and deallocate same day uses latest", func(t *testing.T) {
234+
// Create blocks on same day
235+
baseDate := time.Date(2024, 12, 20, 0, 0, 0, 0, time.UTC)
236+
237+
// Allocate at 10:00
238+
block1Time := baseDate.Add(10 * time.Hour)
239+
block1Num := uint64(400)
240+
res := grm.Exec(`
241+
INSERT INTO blocks (number, hash, block_time, block_date, state_root, created_at, updated_at)
242+
VALUES (?, ?, ?, ?, '', NOW(), NOW())
243+
`, block1Num, fmt.Sprintf("hash_%d", block1Num), block1Time, block1Time.Format("2006-01-02"))
244+
assert.Nil(t, res.Error)
245+
246+
res = grm.Exec(`
247+
INSERT INTO operator_allocations (operator, avs, strategy, operator_set_id, magnitude, effective_block, block_number, transaction_hash, log_index, created_at, updated_at)
248+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
249+
`, "0xoperator4", "0xavs4", "0xstrategy4", 4, "5000", block1Num, block1Num, "tx_400", 1)
250+
assert.Nil(t, res.Error)
251+
252+
// Deallocate at 16:00 (same day)
253+
block2Time := baseDate.Add(16 * time.Hour)
254+
block2Num := uint64(401)
255+
res = grm.Exec(`
256+
INSERT INTO blocks (number, hash, block_time, block_date, state_root, created_at, updated_at)
257+
VALUES (?, ?, ?, ?, '', NOW(), NOW())
258+
`, block2Num, fmt.Sprintf("hash_%d", block2Num), block2Time, block2Time.Format("2006-01-02"))
259+
assert.Nil(t, res.Error)
260+
261+
res = grm.Exec(`
262+
INSERT INTO operator_allocations (operator, avs, strategy, operator_set_id, magnitude, effective_block, block_number, transaction_hash, log_index, created_at, updated_at)
263+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
264+
`, "0xoperator4", "0xavs4", "0xstrategy4", 4, "1000", block2Num, block2Num, "tx_401", 1)
265+
assert.Nil(t, res.Error)
266+
267+
sog := stakerOperators.NewStakerOperatorGenerator(grm, l, cfg)
268+
calculator, err := NewRewardsCalculator(cfg, grm, nil, sog, sink, l)
269+
assert.Nil(t, err)
270+
271+
err = calculator.GenerateAndInsertOperatorAllocationSnapshots("2024-12-25")
272+
assert.Nil(t, err)
273+
274+
// Verify the latest record (deallocation to 1000) is used
275+
// Since it's a decrease, it should round down to 2024-12-20
276+
var count int64
133277
res = grm.Raw(`
134278
SELECT COUNT(*) FROM operator_allocation_snapshots
135279
WHERE operator = ? AND avs = ? AND strategy = ? AND operator_set_id = ?
136280
AND magnitude = '1000' AND snapshot = ?
137-
`, "0xoperator1", "0xavs1", "0xstrategy1", 1, "2024-11-15").Scan(&mag1000Count)
281+
`, "0xoperator4", "0xavs4", "0xstrategy4", 4, "2024-12-20").Scan(&count)
282+
assert.Nil(t, res.Error)
283+
assert.True(t, count > 0, "Expected latest deallocation (1000) to be used, rounded down to 2024-12-20")
284+
})
285+
286+
t.Run("Allocate day 1, deallocate to 0 day 2 at 12pm - 0 days counted", func(t *testing.T) {
287+
// Allocate on day 1
288+
day1 := time.Date(2025, 1, 10, 10, 0, 0, 0, time.UTC)
289+
block1Num := uint64(500)
290+
res := grm.Exec(`
291+
INSERT INTO blocks (number, hash, block_time, block_date, state_root, created_at, updated_at)
292+
VALUES (?, ?, ?, ?, '', NOW(), NOW())
293+
`, block1Num, fmt.Sprintf("hash_%d", block1Num), day1, day1.Format("2006-01-02"))
294+
assert.Nil(t, res.Error)
295+
296+
res = grm.Exec(`
297+
INSERT INTO operator_allocations (operator, avs, strategy, operator_set_id, magnitude, effective_block, block_number, transaction_hash, log_index, created_at, updated_at)
298+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
299+
`, "0xoperator5", "0xavs5", "0xstrategy5", 5, "6000", block1Num, block1Num, "tx_500", 1)
300+
assert.Nil(t, res.Error)
301+
302+
// Deallocate to 0 on day 2 at 12pm (noon)
303+
day2Noon := time.Date(2025, 1, 11, 12, 0, 0, 0, time.UTC)
304+
block2Num := uint64(501)
305+
res = grm.Exec(`
306+
INSERT INTO blocks (number, hash, block_time, block_date, state_root, created_at, updated_at)
307+
VALUES (?, ?, ?, ?, '', NOW(), NOW())
308+
`, block2Num, fmt.Sprintf("hash_%d", block2Num), day2Noon, day2Noon.Format("2006-01-02"))
309+
assert.Nil(t, res.Error)
310+
311+
res = grm.Exec(`
312+
INSERT INTO operator_allocations (operator, avs, strategy, operator_set_id, magnitude, effective_block, block_number, transaction_hash, log_index, created_at, updated_at)
313+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
314+
`, "0xoperator5", "0xavs5", "0xstrategy5", 5, "0", block2Num, block2Num, "tx_501", 1)
315+
assert.Nil(t, res.Error)
316+
317+
sog := stakerOperators.NewStakerOperatorGenerator(grm, l, cfg)
318+
calculator, err := NewRewardsCalculator(cfg, grm, nil, sog, sink, l)
319+
assert.Nil(t, err)
320+
321+
err = calculator.GenerateAndInsertOperatorAllocationSnapshots("2025-01-15")
322+
assert.Nil(t, err)
323+
324+
// Allocation on day 1 (2025-01-10) rounds up to 2025-01-11
325+
// Deallocation on day 2 (2025-01-11) rounds down to 2025-01-11
326+
// So the range is [2025-01-11, 2025-01-11) which generates no days
327+
var count int64
328+
res = grm.Raw(`
329+
SELECT COUNT(*) FROM operator_allocation_snapshots
330+
WHERE operator = ? AND avs = ? AND strategy = ? AND operator_set_id = ?
331+
`, "0xoperator5", "0xavs5", "0xstrategy5", 5).Scan(&count)
332+
assert.Nil(t, res.Error)
333+
assert.Equal(t, int64(0), count, "Expected 0 days to be counted (allocation rounds up to day 2, deallocation rounds down to day 2)")
334+
})
335+
336+
t.Run("Allocate day 1, deallocate to 0 day 3 at 12pm - 1 day counted", func(t *testing.T) {
337+
// Allocate on day 1
338+
day1 := time.Date(2025, 1, 20, 10, 0, 0, 0, time.UTC)
339+
block1Num := uint64(600)
340+
res := grm.Exec(`
341+
INSERT INTO blocks (number, hash, block_time, block_date, state_root, created_at, updated_at)
342+
VALUES (?, ?, ?, ?, '', NOW(), NOW())
343+
`, block1Num, fmt.Sprintf("hash_%d", block1Num), day1, day1.Format("2006-01-02"))
344+
assert.Nil(t, res.Error)
345+
346+
res = grm.Exec(`
347+
INSERT INTO operator_allocations (operator, avs, strategy, operator_set_id, magnitude, effective_block, block_number, transaction_hash, log_index, created_at, updated_at)
348+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
349+
`, "0xoperator6", "0xavs6", "0xstrategy6", 6, "7000", block1Num, block1Num, "tx_600", 1)
350+
assert.Nil(t, res.Error)
351+
352+
// Deallocate to 0 on day 3 at 12pm (noon)
353+
day3Noon := time.Date(2025, 1, 22, 12, 0, 0, 0, time.UTC)
354+
block2Num := uint64(601)
355+
res = grm.Exec(`
356+
INSERT INTO blocks (number, hash, block_time, block_date, state_root, created_at, updated_at)
357+
VALUES (?, ?, ?, ?, '', NOW(), NOW())
358+
`, block2Num, fmt.Sprintf("hash_%d", block2Num), day3Noon, day3Noon.Format("2006-01-02"))
359+
assert.Nil(t, res.Error)
360+
361+
res = grm.Exec(`
362+
INSERT INTO operator_allocations (operator, avs, strategy, operator_set_id, magnitude, effective_block, block_number, transaction_hash, log_index, created_at, updated_at)
363+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW())
364+
`, "0xoperator6", "0xavs6", "0xstrategy6", 6, "0", block2Num, block2Num, "tx_601", 1)
365+
assert.Nil(t, res.Error)
366+
367+
sog := stakerOperators.NewStakerOperatorGenerator(grm, l, cfg)
368+
calculator, err := NewRewardsCalculator(cfg, grm, nil, sog, sink, l)
369+
assert.Nil(t, err)
370+
371+
err = calculator.GenerateAndInsertOperatorAllocationSnapshots("2025-01-25")
372+
assert.Nil(t, err)
373+
374+
// Allocation on day 1 (2025-01-20) rounds up to 2025-01-21
375+
// Deallocation on day 3 (2025-01-22) rounds down to 2025-01-22
376+
// So the range is [2025-01-21, 2025-01-22) which generates 1 day (2025-01-21)
377+
var count int64
378+
res = grm.Raw(`
379+
SELECT COUNT(*) FROM operator_allocation_snapshots
380+
WHERE operator = ? AND avs = ? AND strategy = ? AND operator_set_id = ?
381+
`, "0xoperator6", "0xavs6", "0xstrategy6", 6).Scan(&count)
382+
assert.Nil(t, res.Error)
383+
assert.Equal(t, int64(1), count, "Expected 1 day to be counted (2025-01-21)")
384+
385+
// Verify the snapshot is on 2025-01-21 with magnitude 7000
386+
res = grm.Raw(`
387+
SELECT COUNT(*) FROM operator_allocation_snapshots
388+
WHERE operator = ? AND avs = ? AND strategy = ? AND operator_set_id = ?
389+
AND magnitude = '7000' AND snapshot = ?
390+
`, "0xoperator6", "0xavs6", "0xstrategy6", 6, "2025-01-21").Scan(&count)
138391
assert.Nil(t, res.Error)
139-
assert.True(t, mag1000Count > 0, "Expected magnitude 1000 to have snapshot on 2024-11-15 (rounded up)")
392+
assert.Equal(t, int64(1), count, "Expected snapshot on 2025-01-21 with magnitude 7000")
140393
})
141394

142395
t.Cleanup(func() {

0 commit comments

Comments
 (0)