package storage import ( "context" "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" ) type AddrUse int const ( PreCommitAddr AddrUse = iota CommitAddr PoStAddr ) type addrSelectApi interface { WalletBalance(context.Context, address.Address) (types.BigInt, error) WalletHas(context.Context, address.Address) (bool, error) StateAccountKey(context.Context, address.Address, types.TipSetKey) (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, big.Zero(), nil } 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 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 workerBalance, err := a.WalletBalance(ctx, mi.Worker) if err != nil { return address.Undef, big.Zero(), xerrors.Errorf("checking owner balance: %w", err) } 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 } 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 }