Merge pull request #4217 from filecoin-project/fix/post-smaller-feecap

Set lower feecap on PoSt messages with low balance
This commit is contained in:
Łukasz Magiera 2020-11-20 13:15:48 +01:00 committed by GitHub
commit 96f004032b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 91 additions and 63 deletions

View File

@ -414,7 +414,7 @@ var actorControlList = &cli.Command{
tablewriter.Col("balance"), 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 { if err != nil {
return xerrors.Errorf("getting address for post: %w", err) return xerrors.Errorf("getting address for post: %w", err)
} }

View File

@ -3,13 +3,11 @@ package storage
import ( import (
"context" "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-address"
"github.com/filecoin-project/go-state-types/abi" "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" "github.com/filecoin-project/lotus/chain/types"
) )
@ -28,67 +26,62 @@ type addrSelectApi interface {
StateAccountKey(context.Context, address.Address, types.TipSetKey) (address.Address, error) 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 { switch use {
case PreCommitAddr, CommitAddr: case PreCommitAddr, CommitAddr:
// always use worker, at least for now // always use worker, at least for now
return mi.Worker, nil return mi.Worker, big.Zero(), nil
} }
for _, addr := range mi.ControlAddresses { leastBad := mi.Worker
b, err := a.WalletBalance(ctx, addr) bestAvail := minFunds
if err != nil {
return address.Undef, xerrors.Errorf("checking control address balance: %w", err) for _, addr := range append(mi.ControlAddresses, mi.Owner, mi.Worker) {
if maybeUseAddress(ctx, a, addr, goodFunds, &leastBad, &bestAvail) {
return leastBad, bestAvail, nil
} }
if b.GreaterThanEqual(minFunds) {
k, err := a.StateAccountKey(ctx, addr, types.EmptyTSK)
if err != nil {
log.Errorw("getting account key", "error", err)
continue
}
have, err := a.WalletHas(ctx, k)
if err != nil {
return address.Undef, xerrors.Errorf("failed to check control address: %w", err)
}
if !have {
log.Errorw("don't have key", "key", k)
continue
}
return addr, nil
}
log.Warnw("control address didn't have enough funds for window post message", "address", addr, "required", types.FIL(minFunds), "balance", types.FIL(b))
} }
// Try to use the owner account if we can, fallback to worker if we can't 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))
b, err := a.WalletBalance(ctx, mi.Owner) return leastBad, bestAvail, nil
if err != nil { }
return address.Undef, xerrors.Errorf("checking owner balance: %w", err)
} func maybeUseAddress(ctx context.Context, a addrSelectApi, addr address.Address, goodFunds abi.TokenAmount, leastBad *address.Address, bestAvail *abi.TokenAmount) bool {
b, err := a.WalletBalance(ctx, addr)
if !b.GreaterThanEqual(minFunds) { if err != nil {
return mi.Worker, nil log.Errorw("checking control address balance", "addr", addr, "error", err)
} return false
}
k, err := a.StateAccountKey(ctx, mi.Owner, types.EmptyTSK)
if err != nil { if b.GreaterThanEqual(goodFunds) {
log.Errorw("getting owner account key", "error", err) k, err := a.StateAccountKey(ctx, addr, types.EmptyTSK)
return mi.Worker, nil if err != nil {
} log.Errorw("getting account key", "error", err)
return false
have, err := a.WalletHas(ctx, k) }
if err != nil {
return address.Undef, xerrors.Errorf("failed to check owner address: %w", err) have, err := a.WalletHas(ctx, k)
} if err != nil {
log.Errorw("failed to check control address", "addr", addr, "error", err)
if !have { return false
return mi.Worker, nil }
}
if !have {
return mi.Owner, nil log.Errorw("don't have key", "key", k)
return false
}
*leastBad = addr
*bestAvail = b
return true
}
if b.GreaterThan(*bestAvail) {
*leastBad = addr
*bestAvail = b
}
log.Warnw("address didn't have enough funds for window post message", "address", addr, "required", types.FIL(goodFunds), "balance", types.FIL(b))
return false
} }

View File

@ -95,6 +95,8 @@ type storageMinerApi interface {
MpoolPushMessage(context.Context, *types.Message, *api.MessageSendSpec) (*types.SignedMessage, error) MpoolPushMessage(context.Context, *types.Message, *api.MessageSendSpec) (*types.SignedMessage, error)
GasEstimateMessageGas(context.Context, *types.Message, *api.MessageSendSpec, types.TipSetKey) (*types.Message, 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) ChainHead(context.Context) (*types.TipSet, error)
ChainNotify(context.Context) (<-chan []*api.HeadChange, 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"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/chain/actors/policy"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
) )
@ -781,14 +782,38 @@ func (s *WindowPoStScheduler) setSender(ctx context.Context, msg *types.Message,
} }
*msg = *gm *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 { if err != nil {
log.Errorw("error selecting address for window post", "error", err) log.Errorw("error selecting address for window post", "error", err)
return nil return nil
} }
msg.From = pa msg.From = pa
bestReq := big.Add(msg.RequiredFunds(), msg.Value)
if avail.LessThan(bestReq) {
mff := func() (abi.TokenAmount, error) {
return msg.RequiredFunds(), nil
}
messagepool.CapGasFee(mff, msg, big.Min(big.Sub(avail, msg.Value), msg.RequiredFunds()))
}
return nil return nil
} }

View File

@ -49,6 +49,10 @@ func (m *mockStorageMinerAPI) StateMinerInfo(ctx context.Context, a address.Addr
}, nil }, 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) { 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 return abi.Randomness("ticket rand"), nil
} }
@ -94,8 +98,12 @@ func (m *mockStorageMinerAPI) StateWaitMsg(ctx context.Context, cid cid.Cid, con
}, nil }, nil
} }
func (m *mockStorageMinerAPI) StateNetworkVersion(context.Context, types.TipSetKey) (network.Version, error) { func (m *mockStorageMinerAPI) GasEstimateGasPremium(_ context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
return build.NewestNetworkVersion, nil return big.Zero(), nil
}
func (m *mockStorageMinerAPI) GasEstimateFeeCap(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error) {
return big.Zero(), nil
} }
type mockProver struct { type mockProver struct {