diff --git a/api/types.go b/api/types.go index 7fd607750..e29b7c6f7 100644 --- a/api/types.go +++ b/api/types.go @@ -291,6 +291,7 @@ type ExportRef struct { } type MinerInfo struct { + MinerID address.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. diff --git a/cmd/lotus-provider/deps/deps.go b/cmd/lotus-provider/deps/deps.go index 7a8db855f..34db9f461 100644 --- a/cmd/lotus-provider/deps/deps.go +++ b/cmd/lotus-provider/deps/deps.go @@ -10,6 +10,7 @@ import ( "net" "net/http" "os" + "regexp" "strings" "github.com/BurntSushi/toml" @@ -95,7 +96,7 @@ type Deps struct { Full api.FullNode Verif storiface.Verifier LW *sealer.LocalWorker - As *ctladdr.AddressSelector + As *ctladdr.MultiAddressSelector Maddrs []dtypes.MinerAddress Stor *paths.Remote Si *paths.DBIndex @@ -151,7 +152,7 @@ func (deps *Deps) PopulateRemainingDeps(ctx context.Context, cctx *cli.Context, } if deps.As == nil { - deps.As, err = provider.AddressSelector(&deps.Cfg.Addresses)() + deps.As, err = provider.AddressSelector(deps.Cfg.Addresses)() if err != nil { return err } @@ -236,18 +237,22 @@ Get it with: jq .PrivateKey ~/.lotus-miner/keystore/MF2XI2BNNJ3XILLQOJUXMYLUMU`, deps.LW = sealer.NewLocalWorker(sealer.WorkerConfig{}, deps.Stor, deps.LocalStore, deps.Si, nil, wstates) } if len(deps.Maddrs) == 0 { - for _, s := range deps.Cfg.Addresses.MinerAddresses { - addr, err := address.NewFromString(s) - if err != nil { - return err + for _, s := range deps.Cfg.Addresses { + for _, s := range s.MinerAddresses { + addr, err := address.NewFromString(s) + if err != nil { + return err + } + deps.Maddrs = append(deps.Maddrs, dtypes.MinerAddress(addr)) } - deps.Maddrs = append(deps.Maddrs, dtypes.MinerAddress(addr)) } } fmt.Println("last line of populate") return nil } +var oldAddresses = regexp.MustCompile("(?i)^[addresses]$") + func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig, error) { lp := config.DefaultLotusProvider() have := []string{} @@ -265,7 +270,10 @@ func GetConfig(cctx *cli.Context, db *harmonydb.DB) (*config.LotusProviderConfig } return nil, fmt.Errorf("could not read layer '%s': %w", layer, err) } - meta, err := toml.Decode(text, &lp) + + // allow migration from old config format that was limited to 1 wallet setup. + newText := oldAddresses.ReplaceAllString(text, "[[addresses]]") + meta, err := toml.Decode(newText, &lp) if err != nil { return nil, fmt.Errorf("could not read layer, bad toml %s: %w", layer, err) } diff --git a/cmd/lotus-provider/migrate.go b/cmd/lotus-provider/migrate.go index 3869c7dfb..315691788 100644 --- a/cmd/lotus-provider/migrate.go +++ b/cmd/lotus-provider/migrate.go @@ -148,7 +148,9 @@ func fromMiner(cctx *cli.Context) (err error) { return xerrors.Errorf("parsing miner actor address: %w", err) } - lpCfg.Addresses.MinerAddresses = []string{addr.String()} + lpCfg.Addresses = []config.LotusProviderAddresses{{ + MinerAddresses: []string{addr.String()}, + }} ks, err := lr.KeyStore() if err != nil { diff --git a/cmd/lotus-provider/proving.go b/cmd/lotus-provider/proving.go index 379bfdf85..175372249 100644 --- a/cmd/lotus-provider/proving.go +++ b/cmd/lotus-provider/proving.go @@ -74,7 +74,8 @@ var wdPostTaskCmd = &cli.Command{ } ht := ts.Height() - addr, err := address.NewFromString(deps.Cfg.Addresses.MinerAddresses[0]) + // It's not important to be super-accurate as it's only for basic testing. + addr, err := address.NewFromString(deps.Cfg.Addresses[0].MinerAddresses[0]) if err != nil { return xerrors.Errorf("cannot get miner address %w", err) } diff --git a/node/config/def.go b/node/config/def.go index 192ab4f5e..48b877b3b 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -349,11 +349,11 @@ func DefaultLotusProvider() *LotusProviderConfig { MaxWindowPoStGasFee: types.MustParseFIL("5"), MaxPublishDealsFee: types.MustParseFIL("0.05"), }, - Addresses: LotusProviderAddresses{ + Addresses: []LotusProviderAddresses{{ PreCommitControl: []string{}, CommitControl: []string{}, TerminateControl: []string{}, - }, + }}, Proving: ProvingConfig{ ParallelCheckLimit: 32, PartitionCheckTimeout: Duration(20 * time.Minute), diff --git a/node/config/types.go b/node/config/types.go index 8661ce190..208d85939 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -68,8 +68,10 @@ type StorageMiner struct { type LotusProviderConfig struct { Subsystems ProviderSubsystemsConfig - Fees LotusProviderFees - Addresses LotusProviderAddresses + Fees LotusProviderFees + + // Addresses of wallets per MinerAddress (one of the fields). + Addresses []LotusProviderAddresses Proving ProvingConfig Journal JournalConfig Apis ApisConfig diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 0e92c8e5b..d294307c1 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -169,6 +169,7 @@ func (m *StateModule) StateMinerInfo(ctx context.Context, actor address.Address, } ret := api.MinerInfo{ + MinerID: actor, Owner: info.Owner, Worker: info.Worker, ControlAddresses: info.ControlAddresses, diff --git a/provider/address.go b/provider/address.go index f69ca3fac..298fe8784 100644 --- a/provider/address.go +++ b/provider/address.go @@ -5,47 +5,60 @@ import ( "github.com/filecoin-project/go-address" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/node/config" "github.com/filecoin-project/lotus/storage/ctladdr" ) -func AddressSelector(addrConf *config.LotusProviderAddresses) func() (*ctladdr.AddressSelector, error) { - return func() (*ctladdr.AddressSelector, error) { - as := &ctladdr.AddressSelector{} +func AddressSelector(addrConf []config.LotusProviderAddresses) func() (*ctladdr.MultiAddressSelector, error) { + return func() (*ctladdr.MultiAddressSelector, error) { + as := &ctladdr.MultiAddressSelector{ + MinerMap: make(map[address.Address]api.AddressConfig), + } if addrConf == nil { return as, nil } - as.DisableOwnerFallback = addrConf.DisableOwnerFallback - as.DisableWorkerFallback = addrConf.DisableWorkerFallback + for _, addrConf := range addrConf { + for _, minerID := range addrConf.MinerAddresses { + tmp := api.AddressConfig{ + DisableOwnerFallback: addrConf.DisableOwnerFallback, + DisableWorkerFallback: addrConf.DisableWorkerFallback, + } - for _, s := range addrConf.PreCommitControl { - addr, err := address.NewFromString(s) - if err != nil { - return nil, xerrors.Errorf("parsing precommit control address: %w", err) + for _, s := range addrConf.PreCommitControl { + addr, err := address.NewFromString(s) + if err != nil { + return nil, xerrors.Errorf("parsing precommit control address: %w", err) + } + + tmp.PreCommitControl = append(tmp.PreCommitControl, addr) + } + + for _, s := range addrConf.CommitControl { + addr, err := address.NewFromString(s) + if err != nil { + return nil, xerrors.Errorf("parsing commit control address: %w", err) + } + + tmp.CommitControl = append(tmp.CommitControl, addr) + } + + for _, s := range addrConf.TerminateControl { + addr, err := address.NewFromString(s) + if err != nil { + return nil, xerrors.Errorf("parsing terminate control address: %w", err) + } + + tmp.TerminateControl = append(tmp.TerminateControl, addr) + } + a, err := address.NewFromString(minerID) + if err != nil { + return nil, xerrors.Errorf("parsing miner address %s: %w", minerID, err) + } + as.MinerMap[a] = tmp } - - as.PreCommitControl = append(as.PreCommitControl, addr) } - - for _, s := range addrConf.CommitControl { - addr, err := address.NewFromString(s) - if err != nil { - return nil, xerrors.Errorf("parsing commit control address: %w", err) - } - - as.CommitControl = append(as.CommitControl, addr) - } - - for _, s := range addrConf.TerminateControl { - addr, err := address.NewFromString(s) - if err != nil { - return nil, xerrors.Errorf("parsing terminate control address: %w", err) - } - - as.TerminateControl = append(as.TerminateControl, addr) - } - return as, nil } } diff --git a/provider/builder.go b/provider/builder.go index 81a1a7a0a..82781d211 100644 --- a/provider/builder.go +++ b/provider/builder.go @@ -21,7 +21,7 @@ import ( func WindowPostScheduler(ctx context.Context, fc config.LotusProviderFees, pc config.ProvingConfig, api api.FullNode, verif storiface.Verifier, lw *sealer.LocalWorker, sender *lpmessage.Sender, - as *ctladdr.AddressSelector, addresses []dtypes.MinerAddress, db *harmonydb.DB, + as *ctladdr.MultiAddressSelector, addresses []dtypes.MinerAddress, db *harmonydb.DB, stor paths.Store, idx paths.SectorIndex, max int) (*lpwindow.WdPostTask, *lpwindow.WdPostSubmitTask, *lpwindow.WdPostRecoverDeclareTask, error) { chainSched := chainsched.New(api) diff --git a/provider/lpwindow/recover_task.go b/provider/lpwindow/recover_task.go index 12f8522b5..bd88db4dc 100644 --- a/provider/lpwindow/recover_task.go +++ b/provider/lpwindow/recover_task.go @@ -34,7 +34,7 @@ type WdPostRecoverDeclareTask struct { faultTracker sealer.FaultTracker maxDeclareRecoveriesGasFee types.FIL - as *ctladdr.AddressSelector + as *ctladdr.MultiAddressSelector actors []dtypes.MinerAddress startCheckTF promise.Promise[harmonytask.AddTaskFunc] @@ -61,7 +61,7 @@ func NewWdPostRecoverDeclareTask(sender *lpmessage.Sender, db *harmonydb.DB, api WdPostRecoverDeclareTaskApi, faultTracker sealer.FaultTracker, - as *ctladdr.AddressSelector, + as *ctladdr.MultiAddressSelector, pcs *chainsched.ProviderChainSched, maxDeclareRecoveriesGasFee types.FIL, diff --git a/provider/lpwindow/submit_task.go b/provider/lpwindow/submit_task.go index 72f2499f6..6bb92ea42 100644 --- a/provider/lpwindow/submit_task.go +++ b/provider/lpwindow/submit_task.go @@ -47,12 +47,12 @@ type WdPostSubmitTask struct { api WdPoStSubmitTaskApi maxWindowPoStGasFee types.FIL - as *ctladdr.AddressSelector + as *ctladdr.MultiAddressSelector submitPoStTF promise.Promise[harmonytask.AddTaskFunc] } -func NewWdPostSubmitTask(pcs *chainsched.ProviderChainSched, send *lpmessage.Sender, db *harmonydb.DB, api WdPoStSubmitTaskApi, maxWindowPoStGasFee types.FIL, as *ctladdr.AddressSelector) (*WdPostSubmitTask, error) { +func NewWdPostSubmitTask(pcs *chainsched.ProviderChainSched, send *lpmessage.Sender, db *harmonydb.DB, api WdPoStSubmitTaskApi, maxWindowPoStGasFee types.FIL, as *ctladdr.MultiAddressSelector) (*WdPostSubmitTask, error) { res := &WdPostSubmitTask{ sender: send, db: db, @@ -248,7 +248,7 @@ type MsgPrepAPI interface { StateLookupID(context.Context, address.Address, types.TipSetKey) (address.Address, error) } -func preparePoStMessage(w MsgPrepAPI, as *ctladdr.AddressSelector, maddr address.Address, msg *types.Message, maxFee abi.TokenAmount) (*types.Message, *api.MessageSendSpec, error) { +func preparePoStMessage(w MsgPrepAPI, as *ctladdr.MultiAddressSelector, maddr address.Address, msg *types.Message, maxFee abi.TokenAmount) (*types.Message, *api.MessageSendSpec, error) { mi, err := w.StateMinerInfo(context.Background(), maddr, types.EmptyTSK) if err != nil { return nil, nil, xerrors.Errorf("error getting miner info: %w", err) diff --git a/storage/ctladdr/multiaddresses.go b/storage/ctladdr/multiaddresses.go new file mode 100644 index 000000000..54a3433e0 --- /dev/null +++ b/storage/ctladdr/multiaddresses.go @@ -0,0 +1,76 @@ +package ctladdr + +import ( + "context" + + "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/api" + "github.com/filecoin-project/lotus/chain/types" +) + +type MultiAddressSelector struct { + MinerMap map[address.Address]api.AddressConfig +} + +func (as *MultiAddressSelector) AddressFor(ctx context.Context, a NodeApi, mi api.MinerInfo, use api.AddrUse, goodFunds, minFunds abi.TokenAmount) (address.Address, abi.TokenAmount, error) { + if as == nil { + // should only happen in some tests + log.Warnw("smart address selection disabled, using worker address") + return mi.Worker, big.Zero(), nil + } + + tmp := as.MinerMap[mi.MinerID] + + var addrs []address.Address + switch use { + case api.PreCommitAddr: + addrs = append(addrs, tmp.PreCommitControl...) + case api.CommitAddr: + addrs = append(addrs, tmp.CommitControl...) + case api.TerminateSectorsAddr: + addrs = append(addrs, tmp.TerminateControl...) + case api.DealPublishAddr: + addrs = append(addrs, tmp.DealPublishControl...) + default: + defaultCtl := map[address.Address]struct{}{} + for _, a := range mi.ControlAddresses { + defaultCtl[a] = struct{}{} + } + delete(defaultCtl, mi.Owner) + delete(defaultCtl, mi.Worker) + + configCtl := append([]address.Address{}, tmp.PreCommitControl...) + configCtl = append(configCtl, tmp.CommitControl...) + configCtl = append(configCtl, tmp.TerminateControl...) + configCtl = append(configCtl, tmp.DealPublishControl...) + + for _, addr := range configCtl { + if addr.Protocol() != address.ID { + var err error + addr, err = a.StateLookupID(ctx, addr, types.EmptyTSK) + if err != nil { + log.Warnw("looking up control address", "address", addr, "error", err) + continue + } + } + + delete(defaultCtl, addr) + } + + for a := range defaultCtl { + addrs = append(addrs, a) + } + } + + if len(addrs) == 0 || !tmp.DisableWorkerFallback { + addrs = append(addrs, mi.Worker) + } + if !tmp.DisableOwnerFallback { + addrs = append(addrs, mi.Owner) + } + + return pickAddress(ctx, a, mi, goodFunds, minFunds, addrs) +}