Skip to content

Commit 372a151

Browse files
MikelleAlok Nerurkar
authored andcommitted
fix: changed decay logic (#673)
1 parent a9c75f0 commit 372a151

3 files changed

Lines changed: 218 additions & 19 deletions

File tree

oracle/pkg/updater/export_test.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package updater
2+
3+
import "math/big"
4+
5+
func (u *Updater) ComputeResidualAfterDecay(startTimestamp, endTimestamp, commitTimestamp uint64) *big.Int {
6+
return u.computeResidualAfterDecay(startTimestamp, endTimestamp, commitTimestamp)
7+
}

oracle/pkg/updater/updater.go

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"errors"
77
"fmt"
88
"log/slog"
9-
"math"
109
"math/big"
1110
"strings"
1211
"sync"
@@ -39,8 +38,11 @@ const (
3938
)
4039

4140
const (
42-
PRECISION = 1e16
43-
ONE_HUNDRED_PERCENT = 100 * PRECISION
41+
PRECISION = 1e16
42+
)
43+
44+
var (
45+
BigOneHundredPercent = big.NewInt(100 * PRECISION)
4446
)
4547

4648
type Winner struct {
@@ -635,32 +637,60 @@ func (u *Updater) getL1Txns(ctx context.Context, blockNum uint64) (map[string]Tx
635637
// The computation does not care what format the timestamps are in, as long as they are consistent
636638
// (e.g they could be unix or unixMili timestamps)
637639
func (u *Updater) computeResidualAfterDecay(startTimestamp, endTimestamp, commitTimestamp uint64) *big.Int {
638-
if startTimestamp >= endTimestamp || startTimestamp > commitTimestamp || endTimestamp <= commitTimestamp {
639-
u.logger.Debug("timestamp out of range", "startTimestamp", startTimestamp, "endTimestamp", endTimestamp, "commitTimestamp", commitTimestamp)
640+
if startTimestamp >= endTimestamp || endTimestamp <= commitTimestamp {
641+
u.logger.Debug(
642+
"timestamp out of range",
643+
"startTimestamp", startTimestamp,
644+
"endTimestamp", endTimestamp,
645+
"commitTimestamp", commitTimestamp,
646+
)
640647
return big.NewInt(0)
641648
}
642649

650+
// providers may commit before the start of the decay period
651+
// in this case, there is no decay
652+
if startTimestamp > commitTimestamp {
653+
u.logger.Debug(
654+
"commitTimestamp is before startTimestamp",
655+
"startTimestamp", startTimestamp,
656+
"commitTimestamp", commitTimestamp,
657+
)
658+
return BigOneHundredPercent
659+
}
660+
643661
// Calculate the total time in seconds
644-
totalTime := endTimestamp - startTimestamp
662+
totalTime := new(big.Int).SetUint64(endTimestamp - startTimestamp)
645663
// Calculate the time passed in seconds
646-
timePassed := commitTimestamp - startTimestamp
647-
// Calculate the decay percentage
648-
decayPercentage := float64(timePassed) / float64(totalTime)
649-
// Residual value
650-
residual := 1 - decayPercentage
651-
652-
residualPercentageRound := math.Round(residual * ONE_HUNDRED_PERCENT)
653-
if residualPercentageRound > ONE_HUNDRED_PERCENT {
654-
residualPercentageRound = ONE_HUNDRED_PERCENT
664+
timePassed := new(big.Int).SetUint64(commitTimestamp - startTimestamp)
665+
666+
// Calculate the residual percentage using integer arithmetic
667+
// residual = (totalTime - timePassed) * ONE_HUNDRED_PERCENT / totalTime
668+
669+
// Step 1: (totalTime - timePassed)
670+
timeRemaining := new(big.Int).Sub(totalTime, timePassed)
671+
672+
// Step 2: (totalTime - timePassed) * ONE_HUNDRED_PERCENT
673+
scaledRemaining := new(big.Int).Mul(timeRemaining, BigOneHundredPercent)
674+
675+
// Step 3: ((totalTime - timePassed) * ONE_HUNDRED_PERCENT) / totalTime
676+
// This gives us the residual percentage directly as an integer
677+
residualPercentage := new(big.Int).Div(scaledRemaining, totalTime)
678+
679+
// Ensure residual doesn't exceed ONE_HUNDRED_PERCENT (shouldn't happen with correct inputs, but for safety)
680+
if residualPercentage.Cmp(BigOneHundredPercent) > 0 {
681+
residualPercentage = BigOneHundredPercent
655682
}
656-
u.logger.Debug("decay information",
683+
684+
u.logger.Debug(
685+
"decay information",
657686
"startTimestamp", startTimestamp,
658687
"endTimestamp", endTimestamp,
659688
"commitTimestamp", commitTimestamp,
660689
"totalTime", totalTime,
661690
"timePassed", timePassed,
662-
"decayPercentage", decayPercentage,
663-
"residual", residual,
691+
"timeRemaining", timeRemaining,
692+
"residualPercentage", residualPercentage,
664693
)
665-
return big.NewInt(int64(residualPercentageRound))
694+
695+
return residualPercentage
666696
}

oracle/pkg/updater/updater_test.go

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1292,6 +1292,168 @@ func TestUpdaterIgnoreCommitments(t *testing.T) {
12921292
}
12931293
}
12941294

1295+
func TestComputeResidualAfterDecay(t *testing.T) {
1296+
t.Parallel()
1297+
1298+
discardLogger := slog.New(slog.NewTextHandler(io.Discard, nil))
1299+
1300+
u, err := updater.NewUpdater(
1301+
discardLogger,
1302+
nil,
1303+
nil,
1304+
nil,
1305+
nil,
1306+
nil,
1307+
)
1308+
if err != nil {
1309+
// The current NewUpdater only returns error on cache creation failure, unlikely here.
1310+
t.Fatalf("Failed to create minimal updater instance for test: %v", err)
1311+
}
1312+
1313+
tests := []struct {
1314+
name string
1315+
start uint64
1316+
end uint64
1317+
commit uint64
1318+
want *big.Int
1319+
}{
1320+
{
1321+
name: "Commit Before Start",
1322+
start: 1000,
1323+
end: 2000,
1324+
commit: 500,
1325+
want: updater.BigOneHundredPercent,
1326+
},
1327+
{
1328+
name: "Commit At Start",
1329+
start: 1000,
1330+
end: 2000,
1331+
commit: 1000,
1332+
want: updater.BigOneHundredPercent,
1333+
},
1334+
{
1335+
name: "Commit After End",
1336+
start: 1000,
1337+
end: 2000,
1338+
commit: 2500,
1339+
want: big.NewInt(0),
1340+
},
1341+
{
1342+
name: "Commit At End",
1343+
start: 1000,
1344+
end: 2000,
1345+
commit: 2000,
1346+
want: big.NewInt(0),
1347+
},
1348+
{
1349+
name: "Invalid Range: Start Equals End",
1350+
start: 1000,
1351+
end: 1000,
1352+
commit: 1000,
1353+
want: big.NewInt(0),
1354+
},
1355+
{
1356+
name: "Invalid Range: Start Greater Than End",
1357+
start: 2000,
1358+
end: 1000,
1359+
commit: 1500,
1360+
want: big.NewInt(0),
1361+
},
1362+
{
1363+
name: "Commit Exactly Midpoint",
1364+
start: 1000,
1365+
end: 2000, // duration 1000
1366+
commit: 1500, // 500 passed
1367+
want: big.NewInt(50 * updater.PRECISION), // 1 - 500/1000 = 0.5 -> 50%
1368+
},
1369+
{
1370+
name: "Commit At 25% Time Passed",
1371+
start: 1000,
1372+
end: 2000, // duration 1000
1373+
commit: 1250, // 250 passed
1374+
want: big.NewInt(75 * updater.PRECISION), // 1 - 250/1000 = 0.75 -> 75%
1375+
},
1376+
{
1377+
name: "Commit At 75% Time Passed",
1378+
start: 1000,
1379+
end: 2000, // duration 1000
1380+
commit: 1750, // 750 passed
1381+
want: big.NewInt(25 * updater.PRECISION), // 1 - 750/1000 = 0.25 -> 25%
1382+
},
1383+
{
1384+
name: "Commit Very Close To Start",
1385+
start: 100000,
1386+
end: 200000, // duration 100000
1387+
commit: 100001, // 1 passed
1388+
// residual = 1 - 1/100000 = 0.99999
1389+
// percentage = 0.99999 * 100 = 99.999
1390+
// scaled = 99.999 * PRECISION
1391+
// Expected float: (1.0 - (1.0 / 100000.0)) * ONE_HUNDRED_PERCENT
1392+
// Expected float: 0.99999 * 100 * 1e16 = 99.999 * 1e16 = 999990000000000000
1393+
want: big.NewInt(999990000000000000),
1394+
},
1395+
{
1396+
name: "Commit Very Close To End",
1397+
start: 100000,
1398+
end: 200000, // duration 100000
1399+
commit: 199000, // 99000 passed
1400+
// residual = 1 - 99000/100000 = 1 - 0.99 = 0.01
1401+
// scaled = 0.01 * 100 * PRECISION = 1 * PRECISION
1402+
want: big.NewInt(1 * int64(updater.PRECISION)),
1403+
},
1404+
{
1405+
name: "Zero Start Time",
1406+
start: 0,
1407+
end: 1000, // duration 1000
1408+
commit: 500, // 500 passed
1409+
want: big.NewInt(50 * updater.PRECISION), // 1 - 500/1000 = 0.5 -> 50%
1410+
},
1411+
{
1412+
name: "Zero Start and Commit Time",
1413+
start: 0,
1414+
end: 1000,
1415+
commit: 0,
1416+
want: updater.BigOneHundredPercent,
1417+
},
1418+
{
1419+
name: "Large Timestamps",
1420+
start: 1700000000000, // example ms timestamps
1421+
end: 1700000012000, // 12 second duration
1422+
commit: 1700000003000, // 3 seconds passed
1423+
// residual = 1 - 3000/12000 = 1 - 0.25 = 0.75
1424+
// scaled = 0.75 * 100 * PRECISION = 75 * PRECISION
1425+
want: big.NewInt(75 * updater.PRECISION),
1426+
},
1427+
{
1428+
name: "Minimal Valid Duration",
1429+
start: 1000,
1430+
end: 1001, // duration 1
1431+
commit: 1000,
1432+
want: updater.BigOneHundredPercent,
1433+
},
1434+
{
1435+
name: "Minimal Valid Duration, Commit slightly after start",
1436+
start: 1000,
1437+
end: 1002, // duration 2
1438+
commit: 1001, // passed 1
1439+
// residual = 1 - 1/2 = 0.5
1440+
want: big.NewInt(50 * updater.PRECISION),
1441+
},
1442+
}
1443+
1444+
for _, tc := range tests {
1445+
tc := tc
1446+
t.Run(tc.name, func(t *testing.T) {
1447+
t.Parallel()
1448+
got := u.ComputeResidualAfterDecay(tc.start, tc.end, tc.commit)
1449+
1450+
if got.Cmp(tc.want) != 0 {
1451+
t.Errorf("ComputeResidualAfterDecay(%d, %d, %d) = %v, want %v", tc.start, tc.end, tc.commit, got, tc.want)
1452+
}
1453+
})
1454+
}
1455+
}
1456+
12951457
type testSettlement struct {
12961458
commitmentIdx []byte
12971459
txHash string

0 commit comments

Comments
 (0)