Skip to content

Commit

Permalink
feat(shed): check command for FIP-0081 pledge calculation
Browse files Browse the repository at this point in the history
lotus-shed audits fip0081-pledge
  • Loading branch information
rvagg committed Nov 21, 2024
1 parent 467c6ff commit a39441c
Show file tree
Hide file tree
Showing 2 changed files with 312 additions and 4 deletions.
311 changes: 310 additions & 1 deletion cmd/lotus-shed/balances.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,14 @@ import (
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"

miner14 "github.com/filecoin-project/go-state-types/builtin/v14/miner"
smoothing14 "github.com/filecoin-project/go-state-types/builtin/v14/util/smoothing"
miner15 "github.com/filecoin-project/go-state-types/builtin/v15/miner"
smoothing15 "github.com/filecoin-project/go-state-types/builtin/v15/util/smoothing"
gststore "github.com/filecoin-project/go-state-types/store"

"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/blockstore"
"github.com/filecoin-project/lotus/build/buildconstants"
"github.com/filecoin-project/lotus/chain/actors/adt"
"github.com/filecoin-project/lotus/chain/actors/builtin"
Expand Down Expand Up @@ -69,6 +76,7 @@ var auditsCmd = &cli.Command{
chainBalanceSanityCheckCmd,
chainBalanceStateCmd,
chainPledgeCmd,
chainFip0081PledgeCmd,
fillBalancesCmd,
duplicatedMessagesCmd,
},
Expand Down Expand Up @@ -941,3 +949,304 @@ var fillBalancesCmd = &cli.Command{
return nil
},
}

var chainFip0081PledgeCmd = &cli.Command{
Name: "fip0081-pledge",
Description: "Calculate sector pledge values comparing current to pre-FIP-0081",
ArgsUsage: "[epoch number]",
Action: func(cctx *cli.Context) error {

ctx := lcli.ReqContext(cctx)

api, acloser, err := lcli.GetFullNodeAPIV1(cctx)
if err != nil {
return err
}
defer acloser()

var ts *types.TipSet
if cctx.Args().Present() {
epoch, err := strconv.ParseInt(cctx.Args().First(), 10, 64)
if err != nil {
return xerrors.Errorf("parsing epoch arg: %w", err)
}
ts, err = api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(epoch), types.EmptyTSK)
if err != nil {
return err
}
} else {
ts, err = api.ChainHead(ctx)
if err != nil {
return err
}
}

cases := []struct {
sectorSize abi.SectorSize
verifiedSize uint64
duration abi.ChainEpoch
}{
{
sectorSize: 32 << 30,
verifiedSize: 32 << 30,
duration: builtin.EpochsInYear,
},
{
sectorSize: 32 << 30,
verifiedSize: (32 << 30) / 2,
duration: builtin.EpochsInYear,
},
{
sectorSize: 32 << 30,
verifiedSize: 0,
duration: builtin.EpochsInYear,
},
{
sectorSize: 32 << 30,
verifiedSize: 32 << 30,
duration: 3 * builtin.EpochsInYear,
},
{
sectorSize: 32 << 30,
verifiedSize: (32 << 30) / 2,
duration: 3 * builtin.EpochsInYear,
},
{
sectorSize: 32 << 30,
verifiedSize: 0,
duration: 3 * builtin.EpochsInYear,
},
{
sectorSize: 64 << 30,
verifiedSize: 64 << 30,
duration: builtin.EpochsInYear,
},
{
sectorSize: 64 << 30,
verifiedSize: (64 << 30) / 2,
duration: builtin.EpochsInYear,
},
{
sectorSize: 64 << 30,
verifiedSize: 0,
duration: builtin.EpochsInYear,
},
{
sectorSize: 64 << 30,
verifiedSize: 64 << 30,
duration: 3 * builtin.EpochsInYear,
},
{
sectorSize: 64 << 30,
verifiedSize: (64 << 30) / 2,
duration: 3 * builtin.EpochsInYear,
},
{
sectorSize: 64 << 30,
verifiedSize: 0,
duration: 3 * builtin.EpochsInYear,
},
}

fmt.Printf(" Type | Actual | Pre-FIP-0081 | Difference\n")
fmt.Printf("---------------------------------------------------------------------------------------------------------------------------\n")

for _, c := range cases {
pledge, err := api.StateMinerInitialPledgeForSector(ctx, c.duration, c.sectorSize, c.verifiedSize, ts.Key())
if err != nil {
return err
}
newPledge, err := postFip0081StateMinerInitialPledgeForSector(ctx, api, c.duration, c.sectorSize, c.verifiedSize, ts)
if err != nil {
return err
}
if !pledge.Equals(newPledge) {
return xerrors.Errorf("failed to sanity check StateMinerInitialPledgeForSector calculation!")
}
oldPledge, err := preFip0081StateMinerInitialPledgeForSector(ctx, api, c.duration, c.sectorSize, c.verifiedSize, ts)
if err != nil {
return err
}

fmt.Printf("%s% 4.f%% verified sector for %0.f years @ epoch %d | %-24s | %-24s | %s\n",
c.sectorSize.ShortString(),
float64(c.verifiedSize)/float64(c.sectorSize)*100,
float64(c.duration)/builtin.EpochsInYear,
ts.Height(),
types.FIL(pledge).String(),
types.FIL(oldPledge).String(),
types.BigSub(pledge, oldPledge),
)
}

return nil
},
}

// from itests/migration_test.go

// preFip0081StateMinerInitialPledgeForSector is the same calculation as StateMinerInitialPledgeForSector
// but uses miner14's version of the calculation without the FIP-0081 changes.
func preFip0081StateMinerInitialPledgeForSector(
ctx context.Context,
client api.FullNode,
sectorDuration abi.ChainEpoch,
sectorSize abi.SectorSize,
verifiedSize uint64,
ts *types.TipSet,
) (types.BigInt, error) {
bs := blockstore.NewAPIBlockstore(client)
ctxStore := gststore.WrapBlockStore(ctx, bs)

circSupply, err := client.StateVMCirculatingSupplyInternal(ctx, ts.Key())
if err != nil {
return types.NewInt(0), err
}

powerActor, err := client.StateGetActor(ctx, power.Address, ts.Key())
if err != nil {
return types.NewInt(0), err
}

powerState, err := power.Load(ctxStore, powerActor)
if err != nil {
return types.NewInt(0), err
}

rewardActor, err := client.StateGetActor(ctx, reward.Address, ts.Key())
if err != nil {
return types.NewInt(0), err
}

rewardState, err := reward.Load(ctxStore, rewardActor)
if err != nil {
return types.NewInt(0), err
}

networkQAPower, err := powerState.TotalPowerSmoothed()
if err != nil {
return types.NewInt(0), err
}

verifiedWeight := big.Mul(big.NewIntUnsigned(verifiedSize), big.NewInt(int64(sectorDuration)))
sectorWeight := builtin.QAPowerForWeight(sectorSize, sectorDuration, verifiedWeight)

thisEpochBaselinePower, err := rewardState.(interface {
ThisEpochBaselinePower() (abi.StoragePower, error)
}).ThisEpochBaselinePower()
if err != nil {
return types.NewInt(0), err
}
thisEpochRewardSmoothed, err := rewardState.(interface {
ThisEpochRewardSmoothed() (builtin.FilterEstimate, error)
}).ThisEpochRewardSmoothed()
if err != nil {
return types.NewInt(0), err
}

rewardEstimate := smoothing14.FilterEstimate{
PositionEstimate: thisEpochRewardSmoothed.PositionEstimate,
VelocityEstimate: thisEpochRewardSmoothed.VelocityEstimate,
}
networkQAPowerEstimate := smoothing14.FilterEstimate{
PositionEstimate: networkQAPower.PositionEstimate,
VelocityEstimate: networkQAPower.VelocityEstimate,
}

initialPledge := miner14.InitialPledgeForPower(
sectorWeight,
thisEpochBaselinePower,
rewardEstimate,
networkQAPowerEstimate,
circSupply.FilCirculating,
)

var initialPledgeNum = types.NewInt(110)
var initialPledgeDen = types.NewInt(100)

return types.BigDiv(types.BigMul(initialPledge, initialPledgeNum), initialPledgeDen), nil
}

// postFip0081StateMinerInitialPledgeForSector should be the same calculation as StateMinerInitialPledgeForSector,
// it's here for sanity checking.
func postFip0081StateMinerInitialPledgeForSector(
ctx context.Context,
client api.FullNode,
sectorDuration abi.ChainEpoch,
sectorSize abi.SectorSize,
verifiedSize uint64,
ts *types.TipSet,
) (types.BigInt, error) {
bs := blockstore.NewAPIBlockstore(client)
ctxStore := gststore.WrapBlockStore(ctx, bs)

circSupply, err := client.StateVMCirculatingSupplyInternal(ctx, ts.Key())
if err != nil {
return types.NewInt(0), err
}

powerActor, err := client.StateGetActor(ctx, power.Address, ts.Key())
if err != nil {
return types.NewInt(0), err
}

powerState, err := power.Load(ctxStore, powerActor)
if err != nil {
return types.NewInt(0), err
}

rewardActor, err := client.StateGetActor(ctx, reward.Address, ts.Key())
if err != nil {
return types.NewInt(0), err
}

rewardState, err := reward.Load(ctxStore, rewardActor)
if err != nil {
return types.NewInt(0), err
}

networkQAPower, err := powerState.TotalPowerSmoothed()
if err != nil {
return types.NewInt(0), err
}

verifiedWeight := big.Mul(big.NewIntUnsigned(verifiedSize), big.NewInt(int64(sectorDuration)))
sectorWeight := builtin.QAPowerForWeight(sectorSize, sectorDuration, verifiedWeight)

thisEpochBaselinePower, err := rewardState.(interface {
ThisEpochBaselinePower() (abi.StoragePower, error)
}).ThisEpochBaselinePower()
if err != nil {
return types.NewInt(0), err
}
thisEpochRewardSmoothed, err := rewardState.(interface {
ThisEpochRewardSmoothed() (builtin.FilterEstimate, error)
}).ThisEpochRewardSmoothed()
if err != nil {
return types.NewInt(0), err
}

rewardEstimate := smoothing15.FilterEstimate{
PositionEstimate: thisEpochRewardSmoothed.PositionEstimate,
VelocityEstimate: thisEpochRewardSmoothed.VelocityEstimate,
}
networkQAPowerEstimate := smoothing15.FilterEstimate{
PositionEstimate: networkQAPower.PositionEstimate,
VelocityEstimate: networkQAPower.VelocityEstimate,
}

initialPledge := miner15.InitialPledgeForPower(
sectorWeight,
thisEpochBaselinePower,
rewardEstimate,
networkQAPowerEstimate,
circSupply.FilCirculating,
int64(ts.Height())-powerState.RampStartEpoch(),
powerState.RampDurationEpochs(),
)

var initialPledgeNum = types.NewInt(110)
var initialPledgeDen = types.NewInt(100)

return types.BigDiv(types.BigMul(initialPledge, initialPledgeNum), initialPledgeDen), nil
}
5 changes: 2 additions & 3 deletions itests/migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1140,14 +1140,13 @@ func preFip0081StateMinerInitialPledgeForSector(ctx context.Context, t *testing.
VelocityEstimate: networkQAPower.VelocityEstimate,
}

initialPledge, err := miner14.InitialPledgeForPower(
initialPledge := miner14.InitialPledgeForPower(
sectorWeight,
thisEpochBaselinePower,
rewardEstimate,
networkQAPowerEstimate,
circSupply.FilCirculating,
), nil
req.NoError(err)
)

var initialPledgeNum = types.NewInt(110)
var initialPledgeDen = types.NewInt(100)
Expand Down

0 comments on commit a39441c

Please sign in to comment.