Set lower feecap on PoSt messages with low balance

This commit is contained in:
Łukasz Magiera 2020-10-07 19:41:07 +02:00
parent 648f02cbd6
commit 16dbbadddf
5 changed files with 88 additions and 31 deletions

View File

@ -414,7 +414,7 @@ var actorControlList = &cli.Command{
tablewriter.Col("balance"),
)
postAddr, err := storage.AddressFor(ctx, api, mi, storage.PoStAddr, types.FromFil(1))
postAddr, _, err := storage.AddressFor(ctx, api, mi, storage.PoStAddr, types.FromFil(1), types.FromFil(1))
if err != nil {
return xerrors.Errorf("getting address for post: %w", err)
}

View File

@ -3,13 +3,13 @@ package storage
import (
"context"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/types"
)
@ -28,20 +28,23 @@ type addrSelectApi interface {
StateAccountKey(context.Context, address.Address, types.TipSetKey) (address.Address, error)
}
func AddressFor(ctx context.Context, a addrSelectApi, mi miner.MinerInfo, use AddrUse, minFunds abi.TokenAmount) (address.Address, error) {
func AddressFor(ctx context.Context, a addrSelectApi, mi miner.MinerInfo, use AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) {
switch use {
case PreCommitAddr, CommitAddr:
// always use worker, at least for now
return mi.Worker, nil
return mi.Worker, big.Zero(), nil
}
leastBad := address.Undef
bestAvail := minFunds
for _, addr := range mi.ControlAddresses {
b, err := a.WalletBalance(ctx, addr)
if err != nil {
return address.Undef, xerrors.Errorf("checking control address balance: %w", err)
return address.Undef, big.Zero(), xerrors.Errorf("checking control address balance: %w", err)
}
if b.GreaterThanEqual(minFunds) {
if b.GreaterThanEqual(goodFunds) {
k, err := a.StateAccountKey(ctx, addr, types.EmptyTSK)
if err != nil {
log.Errorw("getting account key", "error", err)
@ -50,7 +53,7 @@ func AddressFor(ctx context.Context, a addrSelectApi, mi miner.MinerInfo, use Ad
have, err := a.WalletHas(ctx, k)
if err != nil {
return address.Undef, xerrors.Errorf("failed to check control address: %w", err)
return address.Undef, big.Zero(), xerrors.Errorf("failed to check control address: %w", err)
}
if !have {
@ -58,37 +61,68 @@ func AddressFor(ctx context.Context, a addrSelectApi, mi miner.MinerInfo, use Ad
continue
}
return addr, nil
return addr, b, nil
}
log.Warnw("control address didn't have enough funds for window post message", "address", addr, "required", types.FIL(minFunds), "balance", types.FIL(b))
if b.GreaterThan(bestAvail) {
leastBad = addr
bestAvail = b
}
log.Warnw("control address didn't have enough funds for window post message", "address", addr, "required", types.FIL(goodFunds), "balance", types.FIL(b))
}
// Try to use the owner account if we can, fallback to worker if we can't
b, err := a.WalletBalance(ctx, mi.Owner)
if err != nil {
return address.Undef, xerrors.Errorf("checking owner balance: %w", err)
}
if !b.GreaterThanEqual(minFunds) {
return mi.Worker, nil
}
k, err := a.StateAccountKey(ctx, mi.Owner, types.EmptyTSK)
if err != nil {
log.Errorw("getting owner account key", "error", err)
return mi.Worker, nil
return mi.Worker, big.Zero(), nil
}
have, err := a.WalletHas(ctx, k)
haveOwner, err := a.WalletHas(ctx, k)
if err != nil {
return address.Undef, xerrors.Errorf("failed to check owner address: %w", err)
return address.Undef, big.Zero(), xerrors.Errorf("failed to check owner address: %w", err)
}
if !have {
return mi.Worker, nil
if haveOwner {
ownerBalance, err := a.WalletBalance(ctx, mi.Owner)
if err != nil {
return address.Undef, big.Zero(), xerrors.Errorf("checking owner balance: %w", err)
}
if ownerBalance.GreaterThanEqual(goodFunds) {
return mi.Owner, goodFunds, nil
}
if ownerBalance.GreaterThan(bestAvail) {
leastBad = mi.Owner
bestAvail = ownerBalance
}
}
return mi.Owner, nil
workerBalance, err := a.WalletBalance(ctx, mi.Worker)
if err != nil {
return address.Undef, big.Zero(), xerrors.Errorf("checking owner balance: %w", err)
}
if workerBalance.GreaterThanEqual(goodFunds) {
return mi.Worker, goodFunds, nil
}
if workerBalance.GreaterThan(bestAvail) {
leastBad = mi.Worker
bestAvail = workerBalance
}
if bestAvail.GreaterThan(minFunds) {
log.Warnw("No address had enough funds to for full PoSt message Fee, selecting least bad address", "address", leastBad, "balance", types.FIL(bestAvail), "optimalFunds", types.FIL(goodFunds), "minFunds", types.FIL(minFunds))
return leastBad, bestAvail, nil
}
// This most likely won't work, but can't hurt to try
log.Warnw("No address had enough funds to for minimum PoSt message Fee, selecting worker address as a fallback", "address", mi.Worker, "balance", types.FIL(workerBalance), "optimalFunds", types.FIL(goodFunds), "minFunds", types.FIL(minFunds))
return mi.Worker, workerBalance, nil
}

View File

@ -95,6 +95,8 @@ type storageMinerApi interface {
MpoolPushMessage(context.Context, *types.Message, *api.MessageSendSpec) (*types.SignedMessage, error)
GasEstimateMessageGas(context.Context, *types.Message, *api.MessageSendSpec, types.TipSetKey) (*types.Message, error)
GasEstimateFeeCap(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error)
GasEstimateGasPremium(_ context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error)
ChainHead(context.Context) (*types.TipSet, error)
ChainNotify(context.Context) (<-chan []*api.HeadChange, error)

View File

@ -26,6 +26,7 @@ import (
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/types"
)
@ -781,14 +782,34 @@ func (s *WindowPoStScheduler) setSender(ctx context.Context, msg *types.Message,
}
*msg = *gm
minFunds := big.Add(msg.RequiredFunds(), msg.Value)
// estimate
minGasFeeMsg := *msg
pa, err := AddressFor(ctx, s.api, mi, PoStAddr, minFunds)
minGasFeeMsg.GasPremium, err = s.api.GasEstimateGasPremium(ctx, 5, msg.From, msg.GasLimit, types.TipSetKey{})
if err != nil {
log.Errorf("failed to estimate minimum gas premium: %+v", err)
minGasFeeMsg.GasPremium = msg.GasPremium
}
minGasFeeMsg.GasFeeCap, err = s.api.GasEstimateFeeCap(ctx, &minGasFeeMsg, 4, types.EmptyTSK)
if err != nil {
log.Errorf("failed to estimate minimum gas fee cap: %+v", err)
minGasFeeMsg.GasFeeCap = msg.GasFeeCap
}
goodFunds := big.Add(msg.RequiredFunds(), msg.Value)
minFunds := big.Min(big.Add(minGasFeeMsg.RequiredFunds(), minGasFeeMsg.Value), goodFunds)
pa, avail, err := AddressFor(ctx, s.api, mi, PoStAddr, goodFunds, minFunds)
if err != nil {
log.Errorw("error selecting address for window post", "error", err)
return nil
}
msg.From = pa
bestReq := big.Add(msg.RequiredFunds(), msg.Value)
if avail.LessThan(bestReq) {
messagepool.CapGasFee(msg, big.Min(big.Sub(avail, msg.Value), msg.RequiredFunds()))
}
return nil
}

View File

@ -49,6 +49,10 @@ func (m *mockStorageMinerAPI) StateMinerInfo(ctx context.Context, a address.Addr
}, nil
}
func (m *mockStorageMinerAPI) StateNetworkVersion(ctx context.Context, key types.TipSetKey) (network.Version, error) {
return build.NewestNetworkVersion, nil
}
func (m *mockStorageMinerAPI) ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) {
return abi.Randomness("ticket rand"), nil
}
@ -94,10 +98,6 @@ func (m *mockStorageMinerAPI) StateWaitMsg(ctx context.Context, cid cid.Cid, con
}, nil
}
func (m *mockStorageMinerAPI) StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error) {
return build.NewestNetworkVersion, nil
}
type mockProver struct {
}