diff --git a/terminate/onpledge.go b/terminate/onpledge.go new file mode 100644 index 0000000..f2efd0e --- /dev/null +++ b/terminate/onpledge.go @@ -0,0 +1,109 @@ +package terminate + +import ( + "context" + "math/big" + + "github.com/filecoin-project/go-state-types/abi" + filbig "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/go-state-types/builtin/v13/miner" + lotusapi "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/types" + miner8 "github.com/filecoin-project/specs-actors/v8/actors/builtin/miner" + "github.com/glifio/go-pools/util" +) + +func TermPenaltyOnPledge( + ctx context.Context, + api *lotusapi.FullNodeStruct, + ts *types.TipSet, + filToPledge *big.Int, + sectorSize uint64, + ratioVerified float64, +) (cost *big.Int, penalty *big.Int, err error) { + smoothedPow, err := util.TotalPowerSmoothed(ctx, api, ts) + if err != nil { + return nil, nil, err + } + + smoothedRew, err := util.ThisEpochRewardsSmoothed(ctx, api, ts) + if err != nil { + return nil, nil, err + } + + height := ts.Height() + activation := 4071894 + expiration := 5619449 + duration := abi.ChainEpoch(expiration - activation) + powerBaseEpoch := activation + replacedDayReward := big.NewInt(0) + + dealWeight := big.NewInt(0) + + scaledSectorSize := int64(float64(sectorSize) * ratioVerified) + verifiedDealWeight := new(big.Int).Mul( + big.NewInt(scaledSectorSize), + big.NewInt(int64(duration)), + ) + + pwr := miner.QAPowerForWeight( + abi.SectorSize(sectorSize), + duration, + filbig.NewFromGo(dealWeight), + filbig.NewFromGo(verifiedDealWeight), + ) + + expectedDayReward := miner.ExpectedRewardForPower( + util.ConvertSmoothing(smoothedRew), + util.ConvertSmoothing(smoothedPow), + pwr, + builtin.EpochsInDay, + ) + + expectedStoragePledge := miner.ExpectedRewardForPower( + util.ConvertSmoothing(smoothedRew), + util.ConvertSmoothing(smoothedPow), + pwr, + miner.InitialPledgeProjectionPeriod, + ) + + s := miner.SectorOnChainInfo{ + Activation: abi.ChainEpoch(activation), + Expiration: abi.ChainEpoch(expiration), + DealWeight: abi.DealWeight(filbig.NewFromGo(dealWeight)), + VerifiedDealWeight: abi.DealWeight(filbig.NewFromGo(verifiedDealWeight)), + ExpectedDayReward: expectedDayReward, + ExpectedStoragePledge: expectedStoragePledge, + PowerBaseEpoch: abi.ChainEpoch(powerBaseEpoch), + ReplacedDayReward: filbig.NewFromGo(replacedDayReward), + } + sectorPower := miner8.QAPowerForSector(abi.SectorSize(sectorSize), util.ConvertSectorType(&s)) + + termFee := miner8.PledgePenaltyForTermination( + s.ExpectedDayReward, + height-s.PowerBaseEpoch, + s.ExpectedStoragePledge, + smoothedPow, + sectorPower, + smoothedRew, + s.ReplacedDayReward, + s.PowerBaseEpoch-s.Activation, + ) + + // Pledge for full duration + pledge := miner.ExpectedRewardForPower( + util.ConvertSmoothing(smoothedRew), + util.ConvertSmoothing(smoothedPow), + pwr, + duration, + ) + + // Calculate number of sectors + sectors := new(big.Int).Quo(filToPledge, pledge.Int) + + cost = new(big.Int).Mul(pledge.Int, sectors) + penalty = new(big.Int).Mul(termFee.Int, sectors) + + return cost, penalty, nil +} diff --git a/terminate/onpledge_test.go b/terminate/onpledge_test.go new file mode 100644 index 0000000..75dde7b --- /dev/null +++ b/terminate/onpledge_test.go @@ -0,0 +1,41 @@ +package terminate + +import ( + "context" + "math/big" + "testing" + + "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/lotus/chain/types" + "github.com/glifio/go-pools/constants" + "github.com/glifio/go-pools/util" +) + +func TestTermPenaltyOnPledge(t *testing.T) { + ctx := context.Background() + + lapi, closer := util.SetupSuite(t) + defer util.TeardownSuite(closer) + + height := abi.ChainEpoch(4072417) + ts, err := lapi.ChainGetTipSetByHeight(context.Background(), height, types.EmptyTSK) + if err != nil { + t.Fatal(err) + } + + filToPledge := new(big.Int).Mul(big.NewInt(1000), constants.WAD) + var sectorSize uint64 = 34359738368 + var ratioVerified float64 = 1.0 + cost, penalty, err := TermPenaltyOnPledge(ctx, lapi, ts, filToPledge, sectorSize, ratioVerified) + if err != nil { + t.Fatal(err) + } + expectedCost, _ := new(big.Int).SetString("999985869063292958098", 10) + if cost.Cmp(expectedCost) != 0 { + t.Errorf("expected cost %v, got %v", expectedCost, cost) + } + expectedPenalty, _ := new(big.Int).SetString("41721371161825193117", 10) + if penalty.Cmp(expectedPenalty) != 0 { + t.Errorf("expected penalty %v, got %v", expectedPenalty, penalty) + } +} diff --git a/util/termination.go b/util/termination.go index b106ef1..db12c44 100644 --- a/util/termination.go +++ b/util/termination.go @@ -4,6 +4,7 @@ import ( "context" "github.com/filecoin-project/go-state-types/builtin/v13/miner" + smoothing13 "github.com/filecoin-project/go-state-types/builtin/v13/util/smoothing" lotusapi "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -34,6 +35,13 @@ func ConvertSectorType(sector *miner.SectorOnChainInfo) *miner8.SectorOnChainInf } } +func ConvertSmoothing(fe smoothing.FilterEstimate) smoothing13.FilterEstimate { + return smoothing13.FilterEstimate{ + PositionEstimate: fe.PositionEstimate, + VelocityEstimate: fe.VelocityEstimate, + } +} + func TotalPowerSmoothed(ctx context.Context, api *lotusapi.FullNodeStruct, tsk *types.TipSet) (smoothing.FilterEstimate, error) { pact, err := api.StateGetActor(ctx, power.Address, tsk.Key()) if err != nil {