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"),
)
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,11 @@ 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,67 +26,62 @@ 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
}
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)
leastBad := mi.Worker
bestAvail := minFunds
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)
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
}
have, err := a.WalletHas(ctx, k)
if err != nil {
return address.Undef, xerrors.Errorf("failed to check owner address: %w", err)
}
if !have {
return mi.Worker, nil
}
return mi.Owner, nil
return leastBad, bestAvail, nil
}
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 err != nil {
log.Errorw("checking control address balance", "addr", addr, "error", err)
return false
}
if b.GreaterThanEqual(goodFunds) {
k, err := a.StateAccountKey(ctx, addr, types.EmptyTSK)
if err != nil {
log.Errorw("getting account key", "error", err)
return false
}
have, err := a.WalletHas(ctx, k)
if err != nil {
log.Errorw("failed to check control address", "addr", addr, "error", err)
return false
}
if !have {
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)
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,38 @@ 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) {
mff := func() (abi.TokenAmount, error) {
return msg.RequiredFunds(), nil
}
messagepool.CapGasFee(mff, 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,8 +98,12 @@ 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
func (m *mockStorageMinerAPI) GasEstimateGasPremium(_ context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) {
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 {