Skip to content

Commit

Permalink
Testing liquidation value (#65)
Browse files Browse the repository at this point in the history
* Use env vars in tests

* Handle progressCh as `nil`

* Update README

* Add a basic test (unfinished)

* Add assert approx rel and abs to test helpers

* Add tests for assert approx eq rel

* Improve test

* Add a daily test runner for the terminate package
  • Loading branch information
Schwartz10 authored Jun 28, 2024
1 parent 2c14d4f commit cc66b24
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 41 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/daily_test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Go Daily Termination Tests

on:
schedule:
- cron: '0 0 * * *'

jobs:
test:
name: Run Go Tests
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- name: Set up Go
uses: actions/setup-go@v3
with:
go-version: 1.19.1

- name: Test
run: go test -v ./terminate
env:
LOTUS_DIAL_ADDR: ${{ secrets.LOTUS_DIAL_ADDR }}
LOTUS_TOKEN: ${{ secrets.LOTUS_TOKEN }}
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,4 +83,9 @@ type PoolsSDK interface {
```

### Generating mocks
We use [mockery](https://vektra.github.io/mockery/#why-mockery) for generating mock files, which are output to the `./mock` package. For example, see `types/types.go` and run `go generate types/types.go`
We use [mockery](https://vektra.github.io/mockery/#why-mockery) for generating mock files, which are output to the `./mock` package. For example, see `types/types.go` and run `go generate types/types.go`

### Testing
To run certain tests, a couple environment variables must be exported:
`LOTUS_DIAL_ADDRESS`<br />
`LOTUS_TOKEN`
9 changes: 2 additions & 7 deletions econ/pmt_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/builtin"
"github.com/glifio/go-pools/abigen"
"github.com/glifio/go-pools/util"
)

func TestInterestOwed(t *testing.T) {
Expand All @@ -35,13 +36,7 @@ func TestInterestOwed(t *testing.T) {

owed := InterestOwed(context.Background(), testAccount, rate, currentEpoch)

if !assertApproxEqAbs(owed, expectedOwed, DUST) {
if !util.AssertApproxEqAbs(owed, expectedOwed, DUST) {
t.Errorf("expected %v, got %v", expectedOwed, owed)
}
}

func assertApproxEqAbs(a, b, DUST *big.Int) bool {
diff := new(big.Int).Sub(a, b)
diff.Abs(diff)
return diff.Cmp(DUST) <= 0
}
57 changes: 32 additions & 25 deletions terminate/preview.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,14 +171,16 @@ func PreviewTerminateSectors(
gasLimit = 90000000000 * 3 // 3 deadlines per batch
}

progressCh <- &PreviewTerminateSectorsProgress{
Epoch: h,
MinerInfo: minerInfo,
WorkerActor: workerActor,
PrevHeightForImmutable: prevHeight,
WorkerActorPrev: workerActorPrev,
BatchSize: batchSize,
GasLimit: gasLimit,
if progressCh != nil {
progressCh <- &PreviewTerminateSectorsProgress{
Epoch: h,
MinerInfo: minerInfo,
WorkerActor: workerActor,
PrevHeightForImmutable: prevHeight,
WorkerActorPrev: workerActorPrev,
BatchSize: batchSize,
GasLimit: gasLimit,
}
}

if autoBatchSize && batchSize < 10 {
Expand Down Expand Up @@ -320,13 +322,16 @@ func PreviewTerminateSectors(
errorCh <- err
return
}
progressCh <- &PreviewTerminateSectorsProgress{
DeadlinePartitionCount: len(deadlinePartitions),
DeadlinePartitionIndex: deadlinePartitionIdx,
Deadline: dlIdx,
DeadlineImmutable: dlImmutable,
Partition: partIdx,
SectorsCount: sc,

if progressCh != nil {
progressCh <- &PreviewTerminateSectorsProgress{
DeadlinePartitionCount: len(deadlinePartitions),
DeadlinePartitionIndex: deadlinePartitionIdx,
Deadline: dlIdx,
DeadlineImmutable: dlImmutable,
Partition: partIdx,
SectorsCount: sc,
}
}
if sc > 0 {

Expand All @@ -346,16 +351,18 @@ func PreviewTerminateSectors(
return
}

progressCh <- &PreviewTerminateSectorsProgress{
DeadlinePartitionCount: len(deadlinePartitions),
DeadlinePartitionIndex: deadlinePartitionIdx,
Deadline: dlIdx,
DeadlineImmutable: dlImmutable,
Partition: partIdx,
SectorsCount: sc,
SliceStart: i,
SliceEnd: lastIndex,
SliceCount: sliceCount,
if progressCh != nil {
progressCh <- &PreviewTerminateSectorsProgress{
DeadlinePartitionCount: len(deadlinePartitions),
DeadlinePartitionIndex: deadlinePartitionIdx,
Deadline: dlIdx,
DeadlineImmutable: dlImmutable,
Partition: partIdx,
SectorsCount: sc,
SliceStart: i,
SliceEnd: lastIndex,
SliceCount: sliceCount,
}
}

termination := miner.TerminationDeclaration{
Expand Down
97 changes: 97 additions & 0 deletions terminate/terminate_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package terminate

import (
"context"
"fmt"
"log"
"math/big"
"testing"

"github.com/filecoin-project/lotus/chain/types"
"github.com/glifio/go-pools/util"
)

// this test compares N random miner termination penalties computed in the most precise (time intensive) way against the quick, less precise sampling method used in the ADO
var N = 10

// the wad based percentage that is acceptible for imprecision
var DIFF = big.NewInt(3e16)

func TestTerminationPrecision(t *testing.T) {
lapi, closer := util.SetupSuite(t)
defer util.TeardownSuite(closer)

// get ChainHead and lookback 5 epochs
head, err := lapi.ChainHead(context.Background())
if err != nil {
t.Fatal(err)
}
// TODO: this doesn't align with `@head` passed to the PreviewTerminateSectors call
lookback := head.Height() - 1
ts, err := lapi.ChainGetTipSetByHeight(context.Background(), lookback, types.EmptyTSK)
if err != nil {
t.Fatal(err)
}

miners, err := lapi.StateListMiners(context.Background(), ts.Key())
if err != nil {
t.Fatal(err)
}

// // make a slice the length of N
// chosenMiners := make([]address.Address, N)

// for i := 0; i < N; i++ {
// // choose a random miner
// randomIndex := rand.Intn(len(miners))
// chosenMiners[i] = miners[randomIndex]
// }
// eventually run this test on all 10 miners in parallel
idx := 0
miner := miners[0]
hasBalance := false
// check if the miner has a balance
for !hasBalance {
sectorCount, err := lapi.StateMinerSectorCount(context.Background(), miner, ts.Key())
if err != nil {
t.Fatal(err)
}
// if the miner has no active sectors, move to the next miner
fmt.Println(miner, sectorCount.Active)
if sectorCount.Active == 0 {
idx++
miner = miners[idx]
} else {
hasBalance = true
}
}

imprecise, err := PreviewTerminateSectorsQuick(context.Background(), lapi, miner, ts)
if err != nil {
t.Fatal(err)
}

errorCh := make(chan error)
resultCh := make(chan *PreviewTerminateSectorsReturn)

go PreviewTerminateSectors(context.Background(), lapi, miner, "@head", 0, 0, 0, false, false, false, 0, errorCh, nil, resultCh)

loop:
for {
select {
case result := <-resultCh:
fmt.Println("PRECISE: ", result.SectorStats.TerminationPenalty)
fmt.Println("IMPRECISE: ", imprecise.SectorStats.TerminationPenalty)
if !util.AssertApproxEqRel(result.SectorStats.TerminationPenalty, imprecise.SectorStats.TerminationPenalty, DIFF) {
t.Fatalf("TERMINATION PENALTIES DO NOT MATCH: precise: %v, imprecise: %v", result.SectorStats.TerminationPenalty, imprecise.SectorStats.TerminationPenalty)
}
break loop

case err := <-errorCh:
log.Fatal(err)
}

}

// TODO: test the 10 random miners instead of just 1
}
39 changes: 31 additions & 8 deletions util/tests.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,32 +2,34 @@ package util

import (
"context"
"math/big"
"net/http"
"os"
"testing"

"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
)

var DIAL_ADDR = ""
var TOKEN = ""

func SetupSuite(t *testing.T) (*api.FullNodeStruct, jsonrpc.ClientCloser) {
if DIAL_ADDR == "" {
t.Fatal("DIAL_ADDR must be set")
lotusDialAddr := os.Getenv("LOTUS_DIAL_ADDR")
lotusToken := os.Getenv("LOTUS_TOKEN")

if lotusDialAddr == "" {
t.Fatal("LOTUS_DIAL_ADDR env var must be set")
}

var lcli api.FullNodeStruct = api.FullNodeStruct{}
head := http.Header{}

if TOKEN != "" {
head.Add("Authorization", "Bearer "+TOKEN)
if lotusToken != "" {
head.Add("Authorization", "Bearer "+lotusToken)
}

closer, err := jsonrpc.NewMergeClient(
context.Background(),
DIAL_ADDR,
lotusDialAddr,
"Filecoin",
api.GetInternalStructs(&lcli),
head,
Expand All @@ -51,3 +53,24 @@ func SetupSuite(t *testing.T) (*api.FullNodeStruct, jsonrpc.ClientCloser) {
func TeardownSuite(close jsonrpc.ClientCloser) {
defer close()
}

func AssertApproxEqAbs(a, b, DUST *big.Int) bool {
diff := new(big.Int).Sub(a, b)
diff.Abs(diff)
return diff.Cmp(DUST) <= 0
}

// DIFF is a WAD math based percentage, such that 1e18 is 100%
func AssertApproxEqRel(a, b, DIFF *big.Int) bool {
// compute the diff
diff := new(big.Int).Sub(a, b)
diff.Abs(diff)

// Calculate the difference in terms of percentage: (|a - b| / a) * 1e18
// To avoid losing precision, first multiply diff by 1e18, then divide by a
percentageDiff := new(big.Int).Mul(diff, big.NewInt(1e18))
percentageDiff.Div(percentageDiff, a)

// Check if the calculated percentage difference is within the specified range
return percentageDiff.Cmp(DIFF) <= 0
}
52 changes: 52 additions & 0 deletions util/tests_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package util

import (
"math/big"
"testing"
)

func TestApproxEqualRel(t *testing.T) {
testCases := []struct {
name string
a, b, rangeWAD *big.Int
want bool
}{
{
name: "Equal values",
a: big.NewInt(1e18), // 1 WAD
b: big.NewInt(1e18), // 1 WAD
rangeWAD: big.NewInt(1e17), // 10%
want: true,
},
{
name: "Within range",
a: big.NewInt(1e18), // 1 WAD
b: big.NewInt(1e18 + 5e16), // 1.05 WAD
rangeWAD: big.NewInt(1e17), // 10%
want: true,
},
{
name: "Outside range",
a: big.NewInt(1e18), // 1 WAD
b: big.NewInt(1e18 + 2e17), // 1.2 WAD
rangeWAD: big.NewInt(1e17), // 10%
want: false,
},
{
name: "A is zero",
a: big.NewInt(0), // 0 WAD
b: big.NewInt(1e18), // 1 WAD
rangeWAD: big.NewInt(1e17), // 10%
want: false, // Cannot compare if 'a' is zero
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
got := AssertApproxEqAbs(tc.a, tc.b, tc.rangeWAD)
if got != tc.want {
t.Errorf("approximatelyEqual(%v, %v, %v) = %v; want %v", tc.a, tc.b, tc.rangeWAD, got, tc.want)
}
})
}
}

0 comments on commit cc66b24

Please sign in to comment.