Skip to content

feat: ExpectedRewardForPower builtin utility function #13138

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
- chore: increase the F3 GMessage buffer size to 1024 ([filecoin-project/lotus#13126](https://github.com/filecoin-project/lotus/pull/13126))
- feat(f3): integrate cached MapReduce from go-hamt-ipld, which improves performance of F3 power table calculation by 6-10x ([filecoin-project/lotus#13134](https://github.com/filecoin-project/lotus/pull/13134))
- fix(spcli): send SettleDealPayments msg to f05 for `lotus-miner actor settle-deal` ([filecoin-project/lotus#13142](https://github.com/filecoin-project/lotus/pull/13142))
- feat: ExpectedRewardForPower builtin utility function and `lotus-shed miner expected-reward` CLI command ([filecoin-project/lotus#13138](https://github.com/filecoin-project/lotus/pull/13138))

# Node v1.33.0 / 2025-05-08
The Lotus v1.33.0 release introduces experimental v2 APIs with F3 awareness, featuring a new TipSet selection mechanism that significantly enhances how applications interact with the Filecoin blockchain. This release candidate also adds F3-aware Ethereum APIs via the /v2 endpoint. All of the /v2 APIs implement intelligent fallback mechanisms between F3 and Expected Consensus and are exposed through the Lotus Gateway.
Expand Down
36 changes: 35 additions & 1 deletion chain/actors/builtin/miner/actor.go.template
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (
gstbuiltin "github.com/filecoin-project/go-state-types/builtin"
minertypes13 "github.com/filecoin-project/go-state-types/builtin/v13/miner"
{{- range .versions}}
{{- if (ge . 16)}}
{{- if (ge . 13)}}
minertypes{{.}} "github.com/filecoin-project/go-state-types/builtin/v{{.}}/miner"
smoothing{{.}} "github.com/filecoin-project/go-state-types/builtin/v{{.}}/util/smoothing"
{{- end -}}
Expand Down Expand Up @@ -403,4 +403,38 @@ func PledgePenaltyForTermination(
default:
return big.Zero(), xerrors.Errorf("unsupported network version: %d", nwVer)
}
}

func ExpectedRewardForPower(
nwVer network.Version,
rewardEstimate, networkQAPowerEstimate builtin.FilterEstimate,
qaSectorPower abi.StoragePower,
projectionDuration abi.ChainEpoch,
) (abi.TokenAmount, error) {
v, err := actorstypes.VersionForNetwork(nwVer)
if err != nil {
return big.Zero(), err
}

switch v {
{{- range .versions}}
{{- if (ge . 13)}}
case actorstypes.Version{{.}}:
return minertypes{{.}}.ExpectedRewardForPower(
smoothing{{.}}.FilterEstimate{
PositionEstimate: rewardEstimate.PositionEstimate,
VelocityEstimate: rewardEstimate.VelocityEstimate,
},
smoothing{{.}}.FilterEstimate{
PositionEstimate: networkQAPowerEstimate.PositionEstimate,
VelocityEstimate: networkQAPowerEstimate.VelocityEstimate,
},
qaSectorPower,
projectionDuration,
), nil
{{- end}}
{{- end}}
default:
return big.Zero(), xerrors.Errorf("unsupported network version: %d", nwVer)
}
}
74 changes: 74 additions & 0 deletions chain/actors/builtin/miner/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import (
"github.com/filecoin-project/go-state-types/big"
gstbuiltin "github.com/filecoin-project/go-state-types/builtin"
minertypes13 "github.com/filecoin-project/go-state-types/builtin/v13/miner"
smoothing13 "github.com/filecoin-project/go-state-types/builtin/v13/util/smoothing"
minertypes14 "github.com/filecoin-project/go-state-types/builtin/v14/miner"
smoothing14 "github.com/filecoin-project/go-state-types/builtin/v14/util/smoothing"
minertypes15 "github.com/filecoin-project/go-state-types/builtin/v15/miner"
smoothing15 "github.com/filecoin-project/go-state-types/builtin/v15/util/smoothing"
minertypes16 "github.com/filecoin-project/go-state-types/builtin/v16/miner"
smoothing16 "github.com/filecoin-project/go-state-types/builtin/v16/util/smoothing"
minertypes "github.com/filecoin-project/go-state-types/builtin/v9/miner"
Expand Down Expand Up @@ -479,3 +484,72 @@ func PledgePenaltyForTermination(
return big.Zero(), xerrors.Errorf("unsupported network version: %d", nwVer)
}
}

func ExpectedRewardForPower(
nwVer network.Version,
rewardEstimate, networkQAPowerEstimate builtin.FilterEstimate,
qaSectorPower abi.StoragePower,
projectionDuration abi.ChainEpoch,
) (abi.TokenAmount, error) {
v, err := actorstypes.VersionForNetwork(nwVer)
if err != nil {
return big.Zero(), err
}

switch v {
case actorstypes.Version13:
return minertypes13.ExpectedRewardForPower(
smoothing13.FilterEstimate{
PositionEstimate: rewardEstimate.PositionEstimate,
VelocityEstimate: rewardEstimate.VelocityEstimate,
},
smoothing13.FilterEstimate{
PositionEstimate: networkQAPowerEstimate.PositionEstimate,
VelocityEstimate: networkQAPowerEstimate.VelocityEstimate,
},
qaSectorPower,
projectionDuration,
), nil
case actorstypes.Version14:
return minertypes14.ExpectedRewardForPower(
smoothing14.FilterEstimate{
PositionEstimate: rewardEstimate.PositionEstimate,
VelocityEstimate: rewardEstimate.VelocityEstimate,
},
smoothing14.FilterEstimate{
PositionEstimate: networkQAPowerEstimate.PositionEstimate,
VelocityEstimate: networkQAPowerEstimate.VelocityEstimate,
},
qaSectorPower,
projectionDuration,
), nil
case actorstypes.Version15:
return minertypes15.ExpectedRewardForPower(
smoothing15.FilterEstimate{
PositionEstimate: rewardEstimate.PositionEstimate,
VelocityEstimate: rewardEstimate.VelocityEstimate,
},
smoothing15.FilterEstimate{
PositionEstimate: networkQAPowerEstimate.PositionEstimate,
VelocityEstimate: networkQAPowerEstimate.VelocityEstimate,
},
qaSectorPower,
projectionDuration,
), nil
case actorstypes.Version16:
return minertypes16.ExpectedRewardForPower(
smoothing16.FilterEstimate{
PositionEstimate: rewardEstimate.PositionEstimate,
VelocityEstimate: rewardEstimate.VelocityEstimate,
},
smoothing16.FilterEstimate{
PositionEstimate: networkQAPowerEstimate.PositionEstimate,
VelocityEstimate: networkQAPowerEstimate.VelocityEstimate,
},
qaSectorPower,
projectionDuration,
), nil
default:
return big.Zero(), xerrors.Errorf("unsupported network version: %d", nwVer)
}
}
82 changes: 82 additions & 0 deletions cmd/lotus-shed/miner-fees.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/filecoin-project/lotus/chain/actors/builtin"
minertypes "github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/actors/builtin/power"
"github.com/filecoin-project/lotus/chain/actors/builtin/reward"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
"github.com/filecoin-project/lotus/lib/must"
Expand Down Expand Up @@ -607,3 +608,84 @@ var minerFeesInspect = &cli.Command{
return nil
},
}

var minerExpectedRewardCmd = &cli.Command{
Name: "expected-reward",
Usage: "Calculate the expected block reward for a miner over a specified projection period " +
"(e.g. lotus-shed miner expected-reward --tipset @head --projection-period 1025280 --qapower 69793218560)",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "tipset",
Usage: "tipset or height (@X or @head for latest)",
Value: "@head",
},
&cli.Int64Flag{
Name: "projection-period",
Usage: "number of epochs to project reward for",
Value: builtin.EpochsInDay,
},
&cli.Int64Flag{
Name: "qapower",
Usage: "Quality Adjusted Power in bytes",
Value: 32 * 1024 * 1024 * 1024, // 32 GiB
},
},
Action: func(cctx *cli.Context) error {
api, closer, err := lcli.GetFullNodeAPIV1(cctx)
if err != nil {
return err
}
defer closer()

ctx := lcli.ReqContext(cctx)
bstore := blockstore.NewAPIBlockstore(api)
adtStore := adt.WrapStore(ctx, cbor.NewCborStore(bstore))

ts, err := lcli.LoadTipSet(ctx, cctx, api)
if err != nil {
return err
}

projectionPeriod := abi.ChainEpoch(cctx.Int64("projection-period"))
qapower := abi.NewStoragePower(cctx.Int64("qapower"))

// Get the reward value for the current tipset from the reward actor
var rewardSmoothed builtin.FilterEstimate
if act, err := api.StateGetActor(ctx, reward.Address, ts.Key()); err != nil {
return xerrors.Errorf("loading reward actor: %w", err)
} else if s, err := reward.Load(adtStore, act); err != nil {
return xerrors.Errorf("loading reward actor state: %w", err)
} else if rewardSmoothed, err = s.ThisEpochRewardSmoothed(); err != nil {
return xerrors.Errorf("failed to determine smoothed reward: %w", err)
}

// Get the network power value for the current tipset from the power actor
var powerSmoothed builtin.FilterEstimate
if act, err := api.StateGetActor(ctx, power.Address, ts.Key()); err != nil {
return xerrors.Errorf("loading power actor: %w", err)
} else if s, err := power.Load(adtStore, act); err != nil {
return xerrors.Errorf("loading power actor state: %w", err)
} else if powerSmoothed, err = s.TotalPowerSmoothed(); err != nil {
return xerrors.Errorf("failed to determine total power: %w", err)
}

nv, err := api.StateNetworkVersion(ctx, ts.Key())
if err != nil {
return xerrors.Errorf("getting network version: %w", err)
}

rew, err := minertypes.ExpectedRewardForPower(nv, rewardSmoothed, powerSmoothed, qapower, projectionPeriod)
if err != nil {
return xerrors.Errorf("calculating expected reward: %w", err)
}
_, _ = fmt.Fprintf(
cctx.App.Writer,
"Expected reward for %s bytes of QA power @ epoch %d for %d epochs: %s attoFIL\n",
qapower,
ts.Height(),
projectionPeriod,
rew,
)
return nil
},
}
1 change: 1 addition & 0 deletions cmd/lotus-shed/miner.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ var minerCmd = &cli.Command{
minerFeesCmd,
minerFeesInspect,
minerListBalancesCmd,
minerExpectedRewardCmd,
},
}

Expand Down
Loading