From 5ea61abfe142e700c65e1a69992ecd52bd0a8ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 19 Aug 2020 22:08:04 +0200 Subject: [PATCH 1/5] Wire up miner control addresses, use for post --- api/types.go | 12 +- cli/state.go | 3 + cmd/lotus-storage-miner/actor.go | 243 ++++++++++++++++++++++++++++++- storage/addresses.go | 92 ++++++++++++ storage/miner.go | 1 + storage/wdpost_run.go | 29 +++- 6 files changed, 370 insertions(+), 10 deletions(-) create mode 100644 storage/addresses.go diff --git a/api/types.go b/api/types.go index 9a874d1c2..6b601e241 100644 --- a/api/types.go +++ b/api/types.go @@ -52,6 +52,7 @@ type MinerInfo struct { Owner address.Address // Must be an ID-address. Worker address.Address // Must be an ID-address. NewWorker address.Address // Must be an ID-address. + ControlAddresses []address.Address // Must be an ID-addresses. WorkerChangeEpoch abi.ChainEpoch PeerId *peer.ID Multiaddrs []abi.Multiaddrs @@ -67,10 +68,13 @@ func NewApiMinerInfo(info *miner.MinerInfo) MinerInfo { } mi := MinerInfo{ - Owner: info.Owner, - Worker: info.Worker, - NewWorker: address.Undef, - WorkerChangeEpoch: -1, + Owner: info.Owner, + Worker: info.Worker, + ControlAddresses: info.ControlAddresses, + + NewWorker: address.Undef, + WorkerChangeEpoch: -1, + PeerId: pid, Multiaddrs: info.Multiaddrs, SealProofType: info.SealProofType, diff --git a/cli/state.go b/cli/state.go index c8daade15..c49994e2a 100644 --- a/cli/state.go +++ b/cli/state.go @@ -105,6 +105,9 @@ var stateMinerInfo = &cli.Command{ fmt.Printf("Owner:\t%s\n", mi.Owner) fmt.Printf("Worker:\t%s\n", mi.Worker) + for i, controlAddress := range mi.ControlAddresses { + fmt.Printf("Control %d: \t%s\n", i, controlAddress) + } fmt.Printf("PeerID:\t%s\n", mi.PeerId) fmt.Printf("SectorSize:\t%s (%d)\n", types.SizeStr(types.NewInt(uint64(mi.SectorSize))), mi.SectorSize) fmt.Printf("Multiaddrs: \t") diff --git a/cmd/lotus-storage-miner/actor.go b/cmd/lotus-storage-miner/actor.go index bd5caf0f0..613b0f99a 100644 --- a/cmd/lotus-storage-miner/actor.go +++ b/cmd/lotus-storage-miner/actor.go @@ -2,19 +2,26 @@ package main import ( "fmt" - "github.com/filecoin-project/specs-actors/actors/builtin" - "github.com/libp2p/go-libp2p-core/peer" - "golang.org/x/xerrors" + "os" + "strings" + "github.com/fatih/color" + "github.com/libp2p/go-libp2p-core/peer" ma "github.com/multiformats/go-multiaddr" "github.com/urfave/cli/v2" + "golang.org/x/xerrors" + "github.com/filecoin-project/go-address" "github.com/filecoin-project/specs-actors/actors/abi" + "github.com/filecoin-project/specs-actors/actors/abi/big" + "github.com/filecoin-project/specs-actors/actors/builtin" "github.com/filecoin-project/specs-actors/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/types" lcli "github.com/filecoin-project/lotus/cli" + "github.com/filecoin-project/lotus/lib/tablewriter" + "github.com/filecoin-project/lotus/storage" ) var actorCmd = &cli.Command{ @@ -24,6 +31,7 @@ var actorCmd = &cli.Command{ actorSetAddrsCmd, actorWithdrawCmd, actorSetPeeridCmd, + actorControl, }, } @@ -240,3 +248,232 @@ var actorWithdrawCmd = &cli.Command{ return nil }, } + +var actorControl = &cli.Command{ + Name: "control", + Usage: "Manage control addresses", + Subcommands: []*cli.Command{ + actorControlList, + actorControlSet, + }, +} + +var actorControlList = &cli.Command{ + Name: "list", + Usage: "Get currently set control addresses", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "verbose", + }, + &cli.BoolFlag{ + Name: "color", + Value: true, + }, + }, + Action: func(cctx *cli.Context) error { + color.NoColor = !cctx.Bool("color") + + nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return err + } + defer closer() + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := nodeApi.ActorAddress(ctx) + if err != nil { + return err + } + + mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + tw := tablewriter.New( + tablewriter.Col("name"), + tablewriter.Col("ID"), + tablewriter.Col("key"), + tablewriter.Col("use"), + tablewriter.Col("balance"), + ) + + postAddr, err := storage.AddressFor(ctx, api, mi, storage.PoStAddr, types.FromFil(1)) + if err != nil { + return xerrors.Errorf("getting address for post: %w", err) + } + + printKey := func(name string, a address.Address) { + b, err := api.WalletBalance(ctx, a) + if err != nil { + fmt.Printf("%s\t%s: error getting balance: %s\n", name, a, err) + return + } + + k, err := api.StateAccountKey(ctx, a, types.EmptyTSK) + if err != nil { + fmt.Printf("%s\t%s: error getting account key: %s\n", name, a, err) + return + } + + kstr := k.String() + if !cctx.Bool("verbose") { + kstr = kstr[:9] + "..." + } + + bstr := types.FIL(b).String() + switch { + case b.LessThan(types.FromFil(10)): + bstr = color.RedString(bstr) + case b.LessThan(types.FromFil(50)): + bstr = color.YellowString(bstr) + default: + bstr = color.GreenString(bstr) + } + + var uses []string + if a == mi.Worker { + uses = append(uses, color.YellowString("other")) + } + if a == postAddr { + uses = append(uses, color.GreenString("post")) + } + + tw.Write(map[string]interface{}{ + "name": name, + "ID": a, + "key": kstr, + "use": strings.Join(uses, " "), + "balance": bstr, + }) + } + + printKey("owner", mi.Owner) + printKey("worker", mi.Worker) + for i, ca := range mi.ControlAddresses { + printKey(fmt.Sprintf("control-%d", i), ca) + } + + return tw.Flush(os.Stdout) + }, +} + +var actorControlSet = &cli.Command{ + Name: "set", + Usage: "Set control address(-es)", + ArgsUsage: "[...address]", + Flags: []cli.Flag{ + &cli.BoolFlag{ + Name: "really-do-it", + Usage: "Actually send transaction performing the action", + Value: false, + }, + }, + Action: func(cctx *cli.Context) error { + nodeApi, closer, err := lcli.GetStorageMinerAPI(cctx) + if err != nil { + return err + } + defer closer() + + api, acloser, err := lcli.GetFullNodeAPI(cctx) + if err != nil { + return err + } + defer acloser() + + ctx := lcli.ReqContext(cctx) + + maddr, err := nodeApi.ActorAddress(ctx) + if err != nil { + return err + } + + mi, err := api.StateMinerInfo(ctx, maddr, types.EmptyTSK) + if err != nil { + return err + } + + del := map[address.Address]struct{}{} + existing := map[address.Address]struct{}{} + for _, controlAddress := range mi.ControlAddresses { + ka, err := api.StateAccountKey(ctx, controlAddress, types.EmptyTSK) + if err != nil { + return err + } + + del[ka] = struct{}{} + existing[ka] = struct{}{} + } + + var toSet []address.Address + + for i, as := range cctx.Args().Slice() { + a, err := address.NewFromString(as) + if err != nil { + return xerrors.Errorf("parsing address %d: %w", i, err) + } + + ka, err := api.StateAccountKey(ctx, a, types.EmptyTSK) + if err != nil { + return err + } + + // make sure the address exists on chain + _, err = api.StateLookupID(ctx, ka, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("looking up %s: %w", ka, err) + } + + delete(del, ka) + toSet = append(toSet, ka) + } + + for a := range del { + fmt.Println("Remove", a) + } + for _, a := range toSet { + if _, exists := existing[a]; !exists { + fmt.Println("Add", a) + } + } + + if !cctx.Bool("really-do-it") { + fmt.Println("Pass --really-do-it to actually execute this action") + return nil + } + + cwp := &miner.ChangeWorkerAddressParams{ + NewWorker: mi.Worker, + NewControlAddrs: toSet, + } + + sp, err := actors.SerializeParams(cwp) + if err != nil { + return xerrors.Errorf("serializing params: %w", err) + } + + smsg, err := api.MpoolPushMessage(ctx, &types.Message{ + From: mi.Owner, + To: maddr, + Method: builtin.MethodsMiner.ChangeWorkerAddress, + + Value: big.Zero(), + Params: sp, + }, nil) + if err != nil { + return xerrors.Errorf("mpool push: %w", err) + } + + fmt.Println("Message CID:", smsg.Cid()) + + return nil + }, +} diff --git a/storage/addresses.go b/storage/addresses.go new file mode 100644 index 000000000..ff5da96ca --- /dev/null +++ b/storage/addresses.go @@ -0,0 +1,92 @@ +package storage + +import ( + "context" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/specs-actors/actors/abi" + + "github.com/filecoin-project/lotus/api" + "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 api.MinerInfo, use AddrUse, minFunds abi.TokenAmount) (address.Address, error) { + switch use { + case PreCommitAddr, CommitAddr: + // always use worker, at least for now + return mi.Worker, 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) + } + + 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 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 + + 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 +} \ No newline at end of file diff --git a/storage/miner.go b/storage/miner.go index 7baffee30..c1e0048ee 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -67,6 +67,7 @@ type storageMinerApi interface { StateMarketStorageDeal(context.Context, abi.DealID, types.TipSetKey) (*api.MarketDeal, error) StateMinerFaults(context.Context, address.Address, types.TipSetKey) (abi.BitField, error) StateMinerRecoveries(context.Context, address.Address, types.TipSetKey) (abi.BitField, error) + StateAccountKey(context.Context, address.Address, types.TipSetKey) (address.Address, error) MpoolPushMessage(context.Context, *types.Message, *api.MessageSendSpec) (*types.SignedMessage, error) diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index 8d61f5c9f..aea274b90 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "errors" + "github.com/filecoin-project/specs-actors/actors/abi/big" "time" "github.com/filecoin-project/go-bitfield" @@ -172,11 +173,11 @@ func (s *WindowPoStScheduler) checkNextRecoveries(ctx context.Context, dlIdx uin msg := &types.Message{ To: s.actor, - From: s.worker, Method: builtin.MethodsMiner.DeclareFaultsRecovered, Params: enc, Value: types.NewInt(0), } + s.setSender(ctx, msg) sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)}) if err != nil { @@ -254,11 +255,11 @@ func (s *WindowPoStScheduler) checkNextFaults(ctx context.Context, dlIdx uint64, msg := &types.Message{ To: s.actor, - From: s.worker, Method: builtin.MethodsMiner.DeclareFaults, Params: enc, Value: types.NewInt(0), // TODO: Is there a fee? } + s.setSender(ctx, msg) sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)}) if err != nil { @@ -460,11 +461,11 @@ func (s *WindowPoStScheduler) submitPost(ctx context.Context, proof *miner.Submi msg := &types.Message{ To: s.actor, - From: s.worker, Method: builtin.MethodsMiner.SubmitWindowedPoSt, Params: enc, Value: types.NewInt(1000), // currently hard-coded late fee in actor, returned if not late } + s.setSender(ctx, msg) // TODO: consider maybe caring about the output sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)}) @@ -490,3 +491,25 @@ func (s *WindowPoStScheduler) submitPost(ctx context.Context, proof *miner.Submi return nil } + +func (s *WindowPoStScheduler) setSender(ctx context.Context, msg *types.Message) { + mi, err := s.api.StateMinerInfo(ctx, s.actor, types.EmptyTSK) + if err != nil { + log.Errorw("error getting miner info", "error", err) + + // better than just failing + msg.From = s.worker + return + } + + minFunds := big.Add(msg.RequiredFunds(), msg.Value) + + pa, err := AddressFor(ctx, s.api, mi, PoStAddr, minFunds) + if err != nil { + log.Errorw("error selecting address for post", "error", err) + msg.From = s.worker + return + } + + msg.From = pa +} \ No newline at end of file From 71cf358ee37cfc79f52d7c67d771c76996cced9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Wed, 19 Aug 2020 23:25:58 +0200 Subject: [PATCH 2/5] Fix windowpost test --- api/api_full.go | 3 +++ api/apistruct/struct.go | 21 ++++++++++++--------- node/impl/full/gas.go | 31 +++++++++++++++++++++++++++++++ node/impl/full/mpool.go | 27 +++------------------------ storage/miner.go | 2 ++ storage/wdpost_run.go | 26 ++++++++++++++++++++------ 6 files changed, 71 insertions(+), 39 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 72799c846..42af888b8 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -132,6 +132,9 @@ type FullNode interface { GasEstimateGasPremium(_ context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) + // GasEstimateMessageGas estimates gas values unset message gas fields + GasEstimateMessageGas(context.Context, *types.Message, *MessageSendSpec, types.TipSetKey) (*types.Message, error) + // MethodGroup: Sync // The Sync method group contains methods for interacting with and // observing the lotus sync service. diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index 282dd3116..d2313c8f7 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -90,9 +90,10 @@ type FullNodeStruct struct { BeaconGetEntry func(ctx context.Context, epoch abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"` - GasEstimateGasPremium func(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"` - GasEstimateGasLimit func(context.Context, *types.Message, types.TipSetKey) (int64, error) `perm:"read"` - GasEstimateFeeCap func(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"` + GasEstimateGasPremium func(context.Context, uint64, address.Address, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"` + GasEstimateGasLimit func(context.Context, *types.Message, types.TipSetKey) (int64, error) `perm:"read"` + GasEstimateFeeCap func(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error) `perm:"read"` + GasEstimateMessageGas func(context.Context, *types.Message, *api.MessageSendSpec, types.TipSetKey) (*types.Message, error) `perm:"read"` SyncState func(context.Context) (*api.SyncState, error) `perm:"read"` SyncSubmitBlock func(ctx context.Context, blk *types.BlockMsg) error `perm:"write"` @@ -459,17 +460,19 @@ func (c *FullNodeStruct) ClientDataTransferUpdates(ctx context.Context) (<-chan return c.Internal.ClientDataTransferUpdates(ctx) } -func (c *FullNodeStruct) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64, - sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) { +func (c *FullNodeStruct) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) { return c.Internal.GasEstimateGasPremium(ctx, nblocksincl, sender, gaslimit, tsk) } -func (c *FullNodeStruct) GasEstimateFeeCap(ctx context.Context, msg *types.Message, - maxqueueblks int64, tsk types.TipSetKey) (types.BigInt, error) { + +func (c *FullNodeStruct) GasEstimateFeeCap(ctx context.Context, msg *types.Message, maxqueueblks int64, tsk types.TipSetKey) (types.BigInt, error) { return c.Internal.GasEstimateFeeCap(ctx, msg, maxqueueblks, tsk) } -func (c *FullNodeStruct) GasEstimateGasLimit(ctx context.Context, msg *types.Message, - tsk types.TipSetKey) (int64, error) { +func (c *FullNodeStruct) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, tsk types.TipSetKey) (*types.Message, error) { + return c.Internal.GasEstimateMessageGas(ctx, msg, spec, tsk) +} + +func (c *FullNodeStruct) GasEstimateGasLimit(ctx context.Context, msg *types.Message, tsk types.TipSetKey) (int64, error) { return c.Internal.GasEstimateGasLimit(ctx, msg, tsk) } diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index 0e140c7a1..984df4d15 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -6,6 +6,7 @@ import ( "math/rand" "sort" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/stmgr" @@ -176,3 +177,33 @@ func (a *GasAPI) GasEstimateGasLimit(ctx context.Context, msgIn *types.Message, return res.MsgRct.GasUsed, nil } + +func (a *GasAPI) GasEstimateMessageGas(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec, _ types.TipSetKey) (*types.Message, error) { + if msg.GasLimit == 0 { + gasLimit, err := a.GasEstimateGasLimit(ctx, msg, types.TipSetKey{}) + if err != nil { + return nil, xerrors.Errorf("estimating gas used: %w", err) + } + msg.GasLimit = int64(float64(gasLimit) * a.Mpool.GetConfig().GasLimitOverestimation) + } + + if msg.GasPremium == types.EmptyInt || types.BigCmp(msg.GasPremium, types.NewInt(0)) == 0 { + gasPremium, err := a.GasEstimateGasPremium(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{}) + if err != nil { + return nil, xerrors.Errorf("estimating gas price: %w", err) + } + msg.GasPremium = gasPremium + } + + if msg.GasFeeCap == types.EmptyInt || types.BigCmp(msg.GasFeeCap, types.NewInt(0)) == 0 { + feeCap, err := a.GasEstimateFeeCap(ctx, msg, 10, types.EmptyTSK) + if err != nil { + return nil, xerrors.Errorf("estimating fee cap: %w", err) + } + msg.GasFeeCap = big.Add(feeCap, msg.GasPremium) + } + + capGasFee(msg, spec.Get().MaxFee) + + return msg, nil +} diff --git a/node/impl/full/mpool.go b/node/impl/full/mpool.go index 64befa738..bde7d4f81 100644 --- a/node/impl/full/mpool.go +++ b/node/impl/full/mpool.go @@ -146,32 +146,12 @@ func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spe if msg.Nonce != 0 { return nil, xerrors.Errorf("MpoolPushMessage expects message nonce to be 0, was %d", msg.Nonce) } - if msg.GasLimit == 0 { - gasLimit, err := a.GasEstimateGasLimit(ctx, msg, types.TipSetKey{}) - if err != nil { - return nil, xerrors.Errorf("estimating gas used: %w", err) - } - msg.GasLimit = int64(float64(gasLimit) * a.Mpool.GetConfig().GasLimitOverestimation) - } - if msg.GasPremium == types.EmptyInt || types.BigCmp(msg.GasPremium, types.NewInt(0)) == 0 { - gasPremium, err := a.GasEstimateGasPremium(ctx, 2, msg.From, msg.GasLimit, types.TipSetKey{}) - if err != nil { - return nil, xerrors.Errorf("estimating gas price: %w", err) - } - msg.GasPremium = gasPremium + msg, err := a.GasAPI.GasEstimateMessageGas(ctx, msg, spec, types.EmptyTSK) + if err != nil { + return nil, xerrors.Errorf("GasEstimateMessageGas error: %w", err) } - if msg.GasFeeCap == types.EmptyInt || types.BigCmp(msg.GasFeeCap, types.NewInt(0)) == 0 { - feeCap, err := a.GasEstimateFeeCap(ctx, msg, 10, types.EmptyTSK) - if err != nil { - return nil, xerrors.Errorf("estimating fee cap: %w", err) - } - msg.GasFeeCap = big.Add(feeCap, msg.GasPremium) - } - - capGasFee(msg, spec.Get().MaxFee) - sign := func(from address.Address, nonce uint64) (*types.SignedMessage, error) { msg.Nonce = nonce if msg.From.Protocol() == address.ID { @@ -192,7 +172,6 @@ func (a *MpoolAPI) MpoolPushMessage(ctx context.Context, msg *types.Message, spe } var m *types.SignedMessage - var err error again: m, err = a.Mpool.PushWithNonce(ctx, msg.From, sign) if err == messagepool.ErrTryAgain { diff --git a/storage/miner.go b/storage/miner.go index c1e0048ee..eb548eb78 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -71,6 +71,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) + ChainHead(context.Context) (*types.TipSet, error) ChainNotify(context.Context) (<-chan []*api.HeadChange, error) ChainGetRandomnessFromTickets(ctx context.Context, tsk types.TipSetKey, personalization crypto.DomainSeparationTag, randEpoch abi.ChainEpoch, entropy []byte) (abi.Randomness, error) diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index aea274b90..5380deb54 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -173,11 +173,13 @@ func (s *WindowPoStScheduler) checkNextRecoveries(ctx context.Context, dlIdx uin msg := &types.Message{ To: s.actor, + From: s.worker, Method: builtin.MethodsMiner.DeclareFaultsRecovered, Params: enc, Value: types.NewInt(0), } - s.setSender(ctx, msg) + spec := &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)} + s.setSender(ctx, msg, spec) sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)}) if err != nil { @@ -255,13 +257,15 @@ func (s *WindowPoStScheduler) checkNextFaults(ctx context.Context, dlIdx uint64, msg := &types.Message{ To: s.actor, + From: s.worker, Method: builtin.MethodsMiner.DeclareFaults, Params: enc, Value: types.NewInt(0), // TODO: Is there a fee? } - s.setSender(ctx, msg) + spec := &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)} + s.setSender(ctx, msg, spec) - sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)}) + sm, err := s.api.MpoolPushMessage(ctx, msg, spec) if err != nil { return xerrors.Errorf("pushing message to mpool: %w", err) } @@ -461,14 +465,16 @@ func (s *WindowPoStScheduler) submitPost(ctx context.Context, proof *miner.Submi msg := &types.Message{ To: s.actor, + From: s.worker, Method: builtin.MethodsMiner.SubmitWindowedPoSt, Params: enc, Value: types.NewInt(1000), // currently hard-coded late fee in actor, returned if not late } - s.setSender(ctx, msg) + spec := &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)} + s.setSender(ctx, msg, spec) // TODO: consider maybe caring about the output - sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)}) + sm, err := s.api.MpoolPushMessage(ctx, msg, spec) if err != nil { return xerrors.Errorf("pushing message to mpool: %w", err) } @@ -492,7 +498,7 @@ func (s *WindowPoStScheduler) submitPost(ctx context.Context, proof *miner.Submi return nil } -func (s *WindowPoStScheduler) setSender(ctx context.Context, msg *types.Message) { +func (s *WindowPoStScheduler) setSender(ctx context.Context, msg *types.Message, spec *api.MessageSendSpec) { mi, err := s.api.StateMinerInfo(ctx, s.actor, types.EmptyTSK) if err != nil { log.Errorw("error getting miner info", "error", err) @@ -502,6 +508,14 @@ func (s *WindowPoStScheduler) setSender(ctx context.Context, msg *types.Message) return } + gm, err := s.api.GasEstimateMessageGas(ctx, msg, spec, types.EmptyTSK) + if err != nil { + log.Errorw("estimating gas", "error", err) + msg.From = s.worker + return + } + *msg = *gm + minFunds := big.Add(msg.RequiredFunds(), msg.Value) pa, err := AddressFor(ctx, s.api, mi, PoStAddr, minFunds) From b42625d8243dd21b83ec1011f6f9afe64ac4d051 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Aug 2020 01:16:09 +0200 Subject: [PATCH 3/5] docs: Explain setting miner control addresses --- documentation/en/mining.md | 51 +++++++++++++++++++++++++++++++++++--- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/documentation/en/mining.md b/documentation/en/mining.md index 3a3bef956..84903409a 100644 --- a/documentation/en/mining.md +++ b/documentation/en/mining.md @@ -109,8 +109,8 @@ The addresses passed to `set-addrs` parameter in the commands below should be cu Once the config file has been updated, set the on-chain record of the miner's listen addresses: - ``` - lotus-miner actor set-addrs ... +``` +lotus-miner actor set-addrs ... ``` This updates the `MinerInfo` object in the miner's actor, which will be looked up @@ -119,5 +119,50 @@ when a client attempts to make a deal. Any number of addresses can be provided. Example: ``` - lotus-miner actor set-addrs /ip4/123.123.73.123/tcp/12345 /ip4/223.223.83.223/tcp/23456 +lotus-miner actor set-addrs /ip4/123.123.73.123/tcp/12345 /ip4/223.223.83.223/tcp/23456 +``` + +# Separate address for windowPoSt messages + +WindowPoSt is the mechanism through which storage is verified in Filecoin. It requires miners to submit proofs for all sectors every 24h, which require sending messages to the chain. + +Because many other mining related actions require sending messages to the chain, and not all of those are "high value", it may be desirable to use a separate account to send PoSt messages from. This allows for setting lower GasFeeCaps on the lower value messages without creating head-of-line blocking problems for the PoSt messages in congested chain conditions + +To set this up, first create a new account, and send it some funds for gas fees: +```sh +lotus wallet new bls +t3defg... + +lotus send t3defg... 100 +``` + +Next add the control address +```sh +lotus-miner actor control set t3defg... +Add t3defg... +Pass --really-do-it to actually execute this action +``` + +Now actually set the addresses +```sh +lotus-miner actor control set --really-do-it t3defg... +Add t3defg... +Message CID: bafy2.. +``` + +Wait for the message to land on chain +```sh +lotus state wait-msg bafy2.. +... +Exit Code: 0 +... +``` + +Check miner control address list to make sure the address was correctly setup +```sh +lotus-miner actor control list +name ID key use balance +owner t01111 t3abcd... other 300 FIL +worker t01111 t3abcd... other 300 FIL +control-0 t02222 t3defg... post 100 FIL ``` From a4807b18bcc341caf2865a5b63da0c3055635255 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Aug 2020 01:26:13 +0200 Subject: [PATCH 4/5] gofmt, api docstring --- api/api_full.go | 2 +- api/apistruct/struct.go | 4 ++-- api/types.go | 6 +++--- cmd/lotus-storage-miner/actor.go | 26 +++++++++++++------------- storage/addresses.go | 2 +- storage/wdpost_run.go | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/api/api_full.go b/api/api_full.go index 42af888b8..5588a3268 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -132,7 +132,7 @@ type FullNode interface { GasEstimateGasPremium(_ context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) - // GasEstimateMessageGas estimates gas values unset message gas fields + // GasEstimateMessageGas estimates gas values for unset message gas fields GasEstimateMessageGas(context.Context, *types.Message, *MessageSendSpec, types.TipSetKey) (*types.Message, error) // MethodGroup: Sync diff --git a/api/apistruct/struct.go b/api/apistruct/struct.go index d2313c8f7..f5203909e 100644 --- a/api/apistruct/struct.go +++ b/api/apistruct/struct.go @@ -460,11 +460,11 @@ func (c *FullNodeStruct) ClientDataTransferUpdates(ctx context.Context) (<-chan return c.Internal.ClientDataTransferUpdates(ctx) } -func (c *FullNodeStruct) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) { +func (c *FullNodeStruct) GasEstimateGasPremium(ctx context.Context, nblocksincl uint64, sender address.Address, gaslimit int64, tsk types.TipSetKey) (types.BigInt, error) { return c.Internal.GasEstimateGasPremium(ctx, nblocksincl, sender, gaslimit, tsk) } -func (c *FullNodeStruct) GasEstimateFeeCap(ctx context.Context, msg *types.Message, maxqueueblks int64, tsk types.TipSetKey) (types.BigInt, error) { +func (c *FullNodeStruct) GasEstimateFeeCap(ctx context.Context, msg *types.Message, maxqueueblks int64, tsk types.TipSetKey) (types.BigInt, error) { return c.Internal.GasEstimateFeeCap(ctx, msg, maxqueueblks, tsk) } diff --git a/api/types.go b/api/types.go index 6b601e241..9bbbca1f6 100644 --- a/api/types.go +++ b/api/types.go @@ -49,9 +49,9 @@ type PubsubScore struct { } type MinerInfo struct { - Owner address.Address // Must be an ID-address. - Worker address.Address // Must be an ID-address. - NewWorker address.Address // Must be an ID-address. + Owner address.Address // Must be an ID-address. + Worker address.Address // Must be an ID-address. + NewWorker address.Address // Must be an ID-address. ControlAddresses []address.Address // Must be an ID-addresses. WorkerChangeEpoch abi.ChainEpoch PeerId *peer.ID diff --git a/cmd/lotus-storage-miner/actor.go b/cmd/lotus-storage-miner/actor.go index 613b0f99a..01c6f57d7 100644 --- a/cmd/lotus-storage-miner/actor.go +++ b/cmd/lotus-storage-miner/actor.go @@ -250,8 +250,8 @@ var actorWithdrawCmd = &cli.Command{ } var actorControl = &cli.Command{ - Name: "control", - Usage: "Manage control addresses", + Name: "control", + Usage: "Manage control addresses", Subcommands: []*cli.Command{ actorControlList, actorControlSet, @@ -259,14 +259,14 @@ var actorControl = &cli.Command{ } var actorControlList = &cli.Command{ - Name: "list", - Usage: "Get currently set control addresses", + Name: "list", + Usage: "Get currently set control addresses", Flags: []cli.Flag{ &cli.BoolFlag{ Name: "verbose", }, &cli.BoolFlag{ - Name: "color", + Name: "color", Value: true, }, }, @@ -347,10 +347,10 @@ var actorControlList = &cli.Command{ } tw.Write(map[string]interface{}{ - "name": name, - "ID": a, - "key": kstr, - "use": strings.Join(uses, " "), + "name": name, + "ID": a, + "key": kstr, + "use": strings.Join(uses, " "), "balance": bstr, }) } @@ -371,7 +371,7 @@ var actorControlSet = &cli.Command{ ArgsUsage: "[...address]", Flags: []cli.Flag{ &cli.BoolFlag{ - Name: "really-do-it", + Name: "really-do-it", Usage: "Actually send transaction performing the action", Value: false, }, @@ -461,11 +461,11 @@ var actorControlSet = &cli.Command{ } smsg, err := api.MpoolPushMessage(ctx, &types.Message{ - From: mi.Owner, - To: maddr, + From: mi.Owner, + To: maddr, Method: builtin.MethodsMiner.ChangeWorkerAddress, - Value: big.Zero(), + Value: big.Zero(), Params: sp, }, nil) if err != nil { diff --git a/storage/addresses.go b/storage/addresses.go index ff5da96ca..e041024c6 100644 --- a/storage/addresses.go +++ b/storage/addresses.go @@ -89,4 +89,4 @@ func AddressFor(ctx context.Context, a addrSelectApi, mi api.MinerInfo, use Addr } return mi.Owner, nil -} \ No newline at end of file +} diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index 5380deb54..7367ceafa 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -526,4 +526,4 @@ func (s *WindowPoStScheduler) setSender(ctx context.Context, msg *types.Message, } msg.From = pa -} \ No newline at end of file +} From de41f63fa42029ace681591cdec9c3fb2b28fd39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 20 Aug 2020 02:03:42 +0200 Subject: [PATCH 5/5] miner info: Also print locked pledge --- cmd/lotus-storage-miner/info.go | 1 + 1 file changed, 1 insertion(+) diff --git a/cmd/lotus-storage-miner/info.go b/cmd/lotus-storage-miner/info.go index 22cf8916a..79487cc2e 100644 --- a/cmd/lotus-storage-miner/info.go +++ b/cmd/lotus-storage-miner/info.go @@ -143,6 +143,7 @@ func infoCmdAct(cctx *cli.Context) error { fmt.Printf("Miner Balance: %s\n", color.YellowString("%s", types.FIL(mact.Balance))) fmt.Printf("\tPreCommit: %s\n", types.FIL(mas.PreCommitDeposits)) + fmt.Printf("\tPledge: %s\n", types.FIL(mas.InitialPledgeRequirement)) fmt.Printf("\tLocked: %s\n", types.FIL(mas.LockedFunds)) color.Green("\tAvailable: %s", types.FIL(types.BigSub(mact.Balance, types.BigAdd(mas.LockedFunds, mas.PreCommitDeposits)))) wb, err := api.WalletBalance(ctx, mi.Worker)