From 83f2368507e0a6de78ac53742eaaba7100022c5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 29 Jun 2021 18:17:08 +0200 Subject: [PATCH 1/3] Add CollateralFromMinerBalance config --- .circleci/config.yml | 9 +- extern/storage-sealing/commit_batch.go | 16 ++- extern/storage-sealing/precommit_batch.go | 3 + extern/storage-sealing/sealiface/config.go | 2 + extern/storage-sealing/states_sealing.go | 13 ++- itests/sector_miner_collateral_test.go | 129 +++++++++++++++++++++ node/config/def.go | 16 ++- node/modules/storageminer.go | 26 +++-- 8 files changed, 188 insertions(+), 26 deletions(-) create mode 100644 itests/sector_miner_collateral_test.go diff --git a/.circleci/config.yml b/.circleci/config.yml index 9a74a523d07..d3f77c5f10b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -815,7 +815,7 @@ workflows: name: test-itest-nonce suite: itest-nonce target: "./itests/nonce_test.go" - + - test: name: test-itest-paych_api suite: itest-paych_api @@ -835,7 +835,12 @@ workflows: name: test-itest-sector_finalize_early suite: itest-sector_finalize_early target: "./itests/sector_finalize_early_test.go" - + + - test: + name: test-itest-sector_miner_collateral + suite: itest-sector_miner_collateral + target: "./itests/sector_miner_collateral_test.go" + - test: name: test-itest-sector_pledge suite: itest-sector_pledge diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 63bd3c7db57..312fb734681 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -225,7 +225,7 @@ func (b *CommitBatcher) maybeStartBatch(notif bool) ([]sealiface.CommitBatchRes, } if individual { - res, err = b.processIndividually() + res, err = b.processIndividually(cfg) } else { res, err = b.processBatch(cfg) } @@ -342,6 +342,10 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa needFunds := big.Add(collateral, aggFee) + if cfg.CollateralFromMinerBalance { + needFunds = big.Zero() + } + goodFunds := big.Add(maxFee, needFunds) from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, goodFunds, needFunds) @@ -361,7 +365,7 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa return []sealiface.CommitBatchRes{res}, nil } -func (b *CommitBatcher) processIndividually() ([]sealiface.CommitBatchRes, error) { +func (b *CommitBatcher) processIndividually(cfg sealiface.Config) ([]sealiface.CommitBatchRes, error) { mi, err := b.api.StateMinerInfo(b.mctx, b.maddr, nil) if err != nil { return nil, xerrors.Errorf("couldn't get miner info: %w", err) @@ -380,7 +384,7 @@ func (b *CommitBatcher) processIndividually() ([]sealiface.CommitBatchRes, error FailedSectors: map[abi.SectorNumber]string{}, } - mcid, err := b.processSingle(mi, sn, info, tok) + mcid, err := b.processSingle(cfg, mi, sn, info, tok) if err != nil { log.Errorf("process single error: %+v", err) // todo: return to user r.FailedSectors[sn] = err.Error() @@ -394,7 +398,7 @@ func (b *CommitBatcher) processIndividually() ([]sealiface.CommitBatchRes, error return res, nil } -func (b *CommitBatcher) processSingle(mi miner.MinerInfo, sn abi.SectorNumber, info AggregateInput, tok TipSetToken) (cid.Cid, error) { +func (b *CommitBatcher) processSingle(cfg sealiface.Config, mi miner.MinerInfo, sn abi.SectorNumber, info AggregateInput, tok TipSetToken) (cid.Cid, error) { enc := new(bytes.Buffer) params := &miner.ProveCommitSectorParams{ SectorNumber: sn, @@ -410,6 +414,10 @@ func (b *CommitBatcher) processSingle(mi miner.MinerInfo, sn abi.SectorNumber, i return cid.Undef, err } + if cfg.CollateralFromMinerBalance { + collateral = big.Zero() + } + goodFunds := big.Add(collateral, big.Int(b.feeCfg.MaxCommitGasFee)) from, _, err := b.addrSel(b.mctx, mi, api.CommitAddr, goodFunds, collateral) diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index 8b132a2ebfb..2b00fb79269 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -225,6 +225,9 @@ func (b *PreCommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.PreCo params.Sectors = append(params.Sectors, *p.pci) deposit = big.Add(deposit, p.deposit) } + if cfg.CollateralFromMinerBalance { + deposit = big.Zero() + } enc := new(bytes.Buffer) if err := params.MarshalCBOR(enc); err != nil { diff --git a/extern/storage-sealing/sealiface/config.go b/extern/storage-sealing/sealiface/config.go index 0410b92c09e..7233895d60f 100644 --- a/extern/storage-sealing/sealiface/config.go +++ b/extern/storage-sealing/sealiface/config.go @@ -24,6 +24,8 @@ type Config struct { FinalizeEarly bool + CollateralFromMinerBalance bool + BatchPreCommits bool MaxPreCommitBatch int PreCommitBatchWait time.Duration diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 5c798f4eb87..710883be821 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -357,11 +357,16 @@ func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInf } } - params, deposit, tok, err := m.preCommitParams(ctx, sector) + params, pcd, tok, err := m.preCommitParams(ctx, sector) if params == nil || err != nil { return err } + deposit := pcd + if cfg.CollateralFromMinerBalance { + deposit = big.Zero() // pay using available miner balance + } + enc := new(bytes.Buffer) if err := params.MarshalCBOR(enc); err != nil { return ctx.Send(SectorChainPreCommitFailed{xerrors.Errorf("could not serialize pre-commit sector parameters: %w", err)}) @@ -389,7 +394,7 @@ func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInf return ctx.Send(SectorChainPreCommitFailed{xerrors.Errorf("pushing message to mpool: %w", err)}) } - return ctx.Send(SectorPreCommitted{Message: mcid, PreCommitDeposit: deposit, PreCommitInfo: *params}) + return ctx.Send(SectorPreCommitted{Message: mcid, PreCommitDeposit: pcd, PreCommitInfo: *params}) } func (m *Sealing) handleSubmitPreCommitBatch(ctx statemachine.Context, sector SectorInfo) error { @@ -628,6 +633,10 @@ func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo collateral = big.Zero() } + if cfg.CollateralFromMinerBalance { + collateral = big.Zero() // pay using available miner balance + } + goodFunds := big.Add(collateral, big.Int(m.feeCfg.MaxCommitGasFee)) from, _, err := m.addrSel(ctx.Context(), mi, api.CommitAddr, goodFunds, collateral) diff --git a/itests/sector_miner_collateral_test.go b/itests/sector_miner_collateral_test.go new file mode 100644 index 00000000000..229f594ccea --- /dev/null +++ b/itests/sector_miner_collateral_test.go @@ -0,0 +1,129 @@ +package itests + +import ( + "context" + "strings" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" + sealing "github.com/filecoin-project/lotus/extern/storage-sealing" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" + "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/node" + "github.com/filecoin-project/lotus/node/modules/dtypes" + "github.com/filecoin-project/lotus/node/repo" +) + +func TestMinerBalanceCollateral(t *testing.T) { + kit.QuietMiningLogs() + + blockTime := 5 * time.Millisecond + + runTest := func(t *testing.T, enabled bool, nSectors int, batching bool) { + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + opts := kit.ConstructorOpts( + kit.LatestActorsAt(-1), + node.ApplyIf(node.IsType(repo.StorageMiner), node.Override(new(dtypes.GetSealingConfigFunc), func() (dtypes.GetSealingConfigFunc, error) { + return func() (sealiface.Config, error) { + return sealiface.Config{ + MaxWaitDealsSectors: 4, + MaxSealingSectors: 4, + MaxSealingSectorsForDeals: 4, + AlwaysKeepUnsealedCopy: true, + WaitDealsDelay: time.Hour, + + BatchPreCommits: batching, + AggregateCommits: batching, + + PreCommitBatchWait: time.Hour, + CommitBatchWait: time.Hour, + + MinCommitBatch: nSectors, + MaxPreCommitBatch: nSectors, + MaxCommitBatch: nSectors, + + CollateralFromMinerBalance: enabled, + }, nil + }, nil + })), + ) + full, miner, ens := kit.EnsembleMinimal(t, kit.MockProofs(), opts) + ens.InterconnectAll().BeginMining(blockTime) + full.WaitTillChain(ctx, kit.HeightAtLeast(10)) + + toCheck := miner.StartPledge(ctx, nSectors, 0, nil) + + for len(toCheck) > 0 { + states := map[api.SectorState]int{} + for n := range toCheck { + st, err := miner.StorageMiner.SectorsStatus(ctx, n, false) + require.NoError(t, err) + states[st.State]++ + if st.State == api.SectorState(sealing.Proving) { + delete(toCheck, n) + } + if strings.Contains(string(st.State), "Fail") { + t.Fatal("sector in a failed state", st.State) + } + } + + build.Clock.Sleep(100 * time.Millisecond) + } + + // check that sector messages had zero value set + sl, err := miner.SectorsList(ctx) + require.NoError(t, err) + + for _, number := range sl { + si, err := miner.SectorsStatus(ctx, number, false) + require.NoError(t, err) + + require.NotNil(t, si.PreCommitMsg) + pc, err := full.ChainGetMessage(ctx, *si.PreCommitMsg) + require.NoError(t, err) + if enabled { + require.Equal(t, big.Zero(), pc.Value) + } else { + require.NotEqual(t, big.Zero(), pc.Value) + } + + require.NotNil(t, si.CommitMsg) + c, err := full.ChainGetMessage(ctx, *si.CommitMsg) + require.NoError(t, err) + if enabled { + require.Equal(t, big.Zero(), c.Value) + } + // commit value might be zero even with !enabled because in test devnets + // precommit deposit tends to be greater than collateral required at + // commit time. + } + } + + t.Run("nobatch", func(t *testing.T) { + runTest(t, true, 1, false) + }) + t.Run("batch-1", func(t *testing.T) { + runTest(t, true, 1, true) // individual commit instead of aggregate + }) + t.Run("batch-4", func(t *testing.T) { + runTest(t, true, 4, true) + }) + + t.Run("nobatch-frombalance-disabled", func(t *testing.T) { + runTest(t, false, 1, false) + }) + t.Run("batch-1-frombalance-disabled", func(t *testing.T) { + runTest(t, false, 1, true) // individual commit instead of aggregate + }) + t.Run("batch-4-frombalance-disabled", func(t *testing.T) { + runTest(t, false, 4, true) + }) +} diff --git a/node/config/def.go b/node/config/def.go index f9a479d75d8..f474c56ed4b 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -125,6 +125,9 @@ type SealingConfig struct { // Run sector finalization before submitting sector proof to the chain FinalizeEarly bool + // Whether to use available miner balance for sector collateral instead of sending it with each message + CollateralFromMinerBalance bool + // enable / disable precommit batching (takes effect after nv13) BatchPreCommits bool // maximum precommit batch size - batches will be sent immediately above this size @@ -317,12 +320,13 @@ func DefaultStorageMiner() *StorageMiner { Common: defCommon(), Sealing: SealingConfig{ - MaxWaitDealsSectors: 2, // 64G with 32G sectors - MaxSealingSectors: 0, - MaxSealingSectorsForDeals: 0, - WaitDealsDelay: Duration(time.Hour * 6), - AlwaysKeepUnsealedCopy: true, - FinalizeEarly: false, + MaxWaitDealsSectors: 2, // 64G with 32G sectors + MaxSealingSectors: 0, + MaxSealingSectorsForDeals: 0, + WaitDealsDelay: Duration(time.Hour * 6), + AlwaysKeepUnsealedCopy: true, + FinalizeEarly: false, + CollateralFromMinerBalance: false, BatchPreCommits: true, MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize, // up to 256 sectors diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 0f281327e16..fba79785dcf 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -863,12 +863,13 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error return func(cfg sealiface.Config) (err error) { err = mutateCfg(r, func(c *config.StorageMiner) { c.Sealing = config.SealingConfig{ - MaxWaitDealsSectors: cfg.MaxWaitDealsSectors, - MaxSealingSectors: cfg.MaxSealingSectors, - MaxSealingSectorsForDeals: cfg.MaxSealingSectorsForDeals, - WaitDealsDelay: config.Duration(cfg.WaitDealsDelay), - AlwaysKeepUnsealedCopy: cfg.AlwaysKeepUnsealedCopy, - FinalizeEarly: cfg.FinalizeEarly, + MaxWaitDealsSectors: cfg.MaxWaitDealsSectors, + MaxSealingSectors: cfg.MaxSealingSectors, + MaxSealingSectorsForDeals: cfg.MaxSealingSectorsForDeals, + WaitDealsDelay: config.Duration(cfg.WaitDealsDelay), + AlwaysKeepUnsealedCopy: cfg.AlwaysKeepUnsealedCopy, + FinalizeEarly: cfg.FinalizeEarly, + CollateralFromMinerBalance: cfg.CollateralFromMinerBalance, BatchPreCommits: cfg.BatchPreCommits, MaxPreCommitBatch: cfg.MaxPreCommitBatch, @@ -893,12 +894,13 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error func ToSealingConfig(cfg *config.StorageMiner) sealiface.Config { return sealiface.Config{ - MaxWaitDealsSectors: cfg.Sealing.MaxWaitDealsSectors, - MaxSealingSectors: cfg.Sealing.MaxSealingSectors, - MaxSealingSectorsForDeals: cfg.Sealing.MaxSealingSectorsForDeals, - WaitDealsDelay: time.Duration(cfg.Sealing.WaitDealsDelay), - AlwaysKeepUnsealedCopy: cfg.Sealing.AlwaysKeepUnsealedCopy, - FinalizeEarly: cfg.Sealing.FinalizeEarly, + MaxWaitDealsSectors: cfg.Sealing.MaxWaitDealsSectors, + MaxSealingSectors: cfg.Sealing.MaxSealingSectors, + MaxSealingSectorsForDeals: cfg.Sealing.MaxSealingSectorsForDeals, + WaitDealsDelay: time.Duration(cfg.Sealing.WaitDealsDelay), + AlwaysKeepUnsealedCopy: cfg.Sealing.AlwaysKeepUnsealedCopy, + FinalizeEarly: cfg.Sealing.FinalizeEarly, + CollateralFromMinerBalance: cfg.Sealing.CollateralFromMinerBalance, BatchPreCommits: cfg.Sealing.BatchPreCommits, MaxPreCommitBatch: cfg.Sealing.MaxPreCommitBatch, From 7526a074d9ac42699f32df918e7eae42648fe974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Mon, 12 Jul 2021 18:46:05 +0200 Subject: [PATCH 2/3] sealing: collateral buffer / falback config --- .circleci/config.yml | 6 +-- extern/storage-sealing/commit_batch.go | 43 ++++++++++++++----- .../mocks/mock_commit_batcher.go | 15 +++++++ .../mocks/mock_precommit_batcher.go | 15 +++++++ extern/storage-sealing/precommit_batch.go | 12 +++--- extern/storage-sealing/sealiface/config.go | 2 + extern/storage-sealing/sealing.go | 1 + extern/storage-sealing/states_sealing.go | 11 ++--- extern/storage-sealing/utils.go | 36 ++++++++++++++++ itests/deals_pricing_test.go | 2 +- itests/sector_miner_collateral_test.go | 3 ++ node/config/def.go | 19 +++++--- node/modules/storageminer.go | 30 +++++++------ storage/adapter_storage_miner.go | 9 ++++ storage/miner.go | 1 + 15 files changed, 163 insertions(+), 42 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index d3f77c5f10b..50f1b1696e3 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -815,7 +815,7 @@ workflows: name: test-itest-nonce suite: itest-nonce target: "./itests/nonce_test.go" - + - test: name: test-itest-paych_api suite: itest-paych_api @@ -835,12 +835,12 @@ workflows: name: test-itest-sector_finalize_early suite: itest-sector_finalize_early target: "./itests/sector_finalize_early_test.go" - + - test: name: test-itest-sector_miner_collateral suite: itest-sector_miner_collateral target: "./itests/sector_miner_collateral_test.go" - + - test: name: test-itest-sector_pledge suite: itest-sector_pledge diff --git a/extern/storage-sealing/commit_batch.go b/extern/storage-sealing/commit_batch.go index 312fb734681..e9ace820e29 100644 --- a/extern/storage-sealing/commit_batch.go +++ b/extern/storage-sealing/commit_batch.go @@ -7,10 +7,6 @@ import ( "sync" "time" - "github.com/filecoin-project/go-state-types/network" - - "github.com/filecoin-project/lotus/chain/actors" - "github.com/ipfs/go-cid" "golang.org/x/xerrors" @@ -18,13 +14,16 @@ import ( "github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/network" miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" proof5 "github.com/filecoin-project/specs-actors/v5/actors/runtime/proof" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" + "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/types" "github.com/filecoin-project/lotus/extern/sector-storage/ffiwrapper" "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" "github.com/filecoin-project/lotus/node/config" @@ -46,6 +45,7 @@ type CommitBatcherApi interface { StateSectorPreCommitInfo(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok TipSetToken) (*miner.SectorPreCommitOnChainInfo, error) StateMinerInitialPledgeCollateral(context.Context, address.Address, miner.SectorPreCommitInfo, TipSetToken) (big.Int, error) StateNetworkVersion(ctx context.Context, tok TipSetToken) (network.Version, error) + StateMinerAvailableBalance(context.Context, address.Address, TipSetToken) (big.Int, error) } type AggregateInput struct { @@ -341,9 +341,9 @@ func (b *CommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.CommitBa aggFee := big.Div(big.Mul(policy.AggregateNetworkFee(nv, len(infos), bf), aggFeeNum), aggFeeDen) needFunds := big.Add(collateral, aggFee) - - if cfg.CollateralFromMinerBalance { - needFunds = big.Zero() + needFunds, err = collateralSendAmount(b.mctx, b.api, b.maddr, cfg, needFunds) + if err != nil { + return []sealiface.CommitBatchRes{res}, err } goodFunds := big.Add(maxFee, needFunds) @@ -371,6 +371,20 @@ func (b *CommitBatcher) processIndividually(cfg sealiface.Config) ([]sealiface.C return nil, xerrors.Errorf("couldn't get miner info: %w", err) } + avail := types.TotalFilecoinInt + + if cfg.CollateralFromMinerBalance && !cfg.DisableCollateralFallback { + avail, err = b.api.StateMinerAvailableBalance(b.mctx, b.maddr, nil) + if err != nil { + return nil, xerrors.Errorf("getting available miner balance: %w", err) + } + + avail = big.Sub(avail, cfg.AvailableBalanceBuffer) + if avail.LessThan(big.Zero()) { + avail = big.Zero() + } + } + tok, _, err := b.api.ChainHead(b.mctx) if err != nil { return nil, err @@ -384,7 +398,7 @@ func (b *CommitBatcher) processIndividually(cfg sealiface.Config) ([]sealiface.C FailedSectors: map[abi.SectorNumber]string{}, } - mcid, err := b.processSingle(cfg, mi, sn, info, tok) + mcid, err := b.processSingle(cfg, mi, &avail, sn, info, tok) if err != nil { log.Errorf("process single error: %+v", err) // todo: return to user r.FailedSectors[sn] = err.Error() @@ -398,7 +412,7 @@ func (b *CommitBatcher) processIndividually(cfg sealiface.Config) ([]sealiface.C return res, nil } -func (b *CommitBatcher) processSingle(cfg sealiface.Config, mi miner.MinerInfo, sn abi.SectorNumber, info AggregateInput, tok TipSetToken) (cid.Cid, error) { +func (b *CommitBatcher) processSingle(cfg sealiface.Config, mi miner.MinerInfo, avail *abi.TokenAmount, sn abi.SectorNumber, info AggregateInput, tok TipSetToken) (cid.Cid, error) { enc := new(bytes.Buffer) params := &miner.ProveCommitSectorParams{ SectorNumber: sn, @@ -415,7 +429,16 @@ func (b *CommitBatcher) processSingle(cfg sealiface.Config, mi miner.MinerInfo, } if cfg.CollateralFromMinerBalance { - collateral = big.Zero() + c := big.Sub(collateral, *avail) + *avail = big.Sub(*avail, collateral) + collateral = c + + if collateral.LessThan(big.Zero()) { + collateral = big.Zero() + } + if (*avail).LessThan(big.Zero()) { + *avail = big.Zero() + } } goodFunds := big.Add(collateral, big.Int(b.feeCfg.MaxCommitGasFee)) diff --git a/extern/storage-sealing/mocks/mock_commit_batcher.go b/extern/storage-sealing/mocks/mock_commit_batcher.go index c4746e0ebf7..061121899c8 100644 --- a/extern/storage-sealing/mocks/mock_commit_batcher.go +++ b/extern/storage-sealing/mocks/mock_commit_batcher.go @@ -88,6 +88,21 @@ func (mr *MockCommitBatcherApiMockRecorder) SendMsg(arg0, arg1, arg2, arg3, arg4 return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockCommitBatcherApi)(nil).SendMsg), arg0, arg1, arg2, arg3, arg4, arg5, arg6) } +// StateMinerAvailableBalance mocks base method. +func (m *MockCommitBatcherApi) StateMinerAvailableBalance(arg0 context.Context, arg1 address.Address, arg2 sealing.TipSetToken) (big.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateMinerAvailableBalance", arg0, arg1, arg2) + ret0, _ := ret[0].(big.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateMinerAvailableBalance indicates an expected call of StateMinerAvailableBalance. +func (mr *MockCommitBatcherApiMockRecorder) StateMinerAvailableBalance(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateMinerAvailableBalance", reflect.TypeOf((*MockCommitBatcherApi)(nil).StateMinerAvailableBalance), arg0, arg1, arg2) +} + // StateMinerInfo mocks base method. func (m *MockCommitBatcherApi) StateMinerInfo(arg0 context.Context, arg1 address.Address, arg2 sealing.TipSetToken) (miner.MinerInfo, error) { m.ctrl.T.Helper() diff --git a/extern/storage-sealing/mocks/mock_precommit_batcher.go b/extern/storage-sealing/mocks/mock_precommit_batcher.go index 4a50740271c..ed97229b405 100644 --- a/extern/storage-sealing/mocks/mock_precommit_batcher.go +++ b/extern/storage-sealing/mocks/mock_precommit_batcher.go @@ -71,6 +71,21 @@ func (mr *MockPreCommitBatcherApiMockRecorder) SendMsg(arg0, arg1, arg2, arg3, a return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendMsg", reflect.TypeOf((*MockPreCommitBatcherApi)(nil).SendMsg), arg0, arg1, arg2, arg3, arg4, arg5, arg6) } +// StateMinerAvailableBalance mocks base method. +func (m *MockPreCommitBatcherApi) StateMinerAvailableBalance(arg0 context.Context, arg1 address.Address, arg2 sealing.TipSetToken) (big.Int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "StateMinerAvailableBalance", arg0, arg1, arg2) + ret0, _ := ret[0].(big.Int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// StateMinerAvailableBalance indicates an expected call of StateMinerAvailableBalance. +func (mr *MockPreCommitBatcherApiMockRecorder) StateMinerAvailableBalance(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StateMinerAvailableBalance", reflect.TypeOf((*MockPreCommitBatcherApi)(nil).StateMinerAvailableBalance), arg0, arg1, arg2) +} + // StateMinerInfo mocks base method. func (m *MockPreCommitBatcherApi) StateMinerInfo(arg0 context.Context, arg1 address.Address, arg2 sealing.TipSetToken) (miner.MinerInfo, error) { m.ctrl.T.Helper() diff --git a/extern/storage-sealing/precommit_batch.go b/extern/storage-sealing/precommit_batch.go index 2b00fb79269..719455b909f 100644 --- a/extern/storage-sealing/precommit_batch.go +++ b/extern/storage-sealing/precommit_batch.go @@ -7,9 +7,6 @@ import ( "sync" "time" - "github.com/filecoin-project/lotus/build" - "github.com/filecoin-project/lotus/chain/actors/policy" - "github.com/ipfs/go-cid" "golang.org/x/xerrors" @@ -20,7 +17,9 @@ import ( miner5 "github.com/filecoin-project/specs-actors/v5/actors/builtin/miner" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/actors/policy" "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" "github.com/filecoin-project/lotus/node/config" ) @@ -30,6 +29,7 @@ import ( type PreCommitBatcherApi interface { SendMsg(ctx context.Context, from, to address.Address, method abi.MethodNum, value, maxFee abi.TokenAmount, params []byte) (cid.Cid, error) StateMinerInfo(context.Context, address.Address, TipSetToken) (miner.MinerInfo, error) + StateMinerAvailableBalance(context.Context, address.Address, TipSetToken) (big.Int, error) ChainHead(ctx context.Context) (TipSetToken, abi.ChainEpoch, error) } @@ -225,8 +225,10 @@ func (b *PreCommitBatcher) processBatch(cfg sealiface.Config) ([]sealiface.PreCo params.Sectors = append(params.Sectors, *p.pci) deposit = big.Add(deposit, p.deposit) } - if cfg.CollateralFromMinerBalance { - deposit = big.Zero() + + deposit, err := collateralSendAmount(b.mctx, b.api, b.maddr, cfg, deposit) + if err != nil { + return []sealiface.PreCommitBatchRes{res}, err } enc := new(bytes.Buffer) diff --git a/extern/storage-sealing/sealiface/config.go b/extern/storage-sealing/sealiface/config.go index 7233895d60f..e33b3626319 100644 --- a/extern/storage-sealing/sealiface/config.go +++ b/extern/storage-sealing/sealiface/config.go @@ -25,6 +25,8 @@ type Config struct { FinalizeEarly bool CollateralFromMinerBalance bool + AvailableBalanceBuffer abi.TokenAmount + DisableCollateralFallback bool BatchPreCommits bool MaxPreCommitBatch int diff --git a/extern/storage-sealing/sealing.go b/extern/storage-sealing/sealing.go index 8a70704c416..c18caa21f47 100644 --- a/extern/storage-sealing/sealing.go +++ b/extern/storage-sealing/sealing.go @@ -59,6 +59,7 @@ type SealingAPI interface { StateMinerPreCommitDepositForPower(context.Context, address.Address, miner.SectorPreCommitInfo, TipSetToken) (big.Int, error) StateMinerInitialPledgeCollateral(context.Context, address.Address, miner.SectorPreCommitInfo, TipSetToken) (big.Int, error) StateMinerInfo(context.Context, address.Address, TipSetToken) (miner.MinerInfo, error) + StateMinerAvailableBalance(context.Context, address.Address, TipSetToken) (big.Int, error) StateMinerSectorAllocated(context.Context, address.Address, abi.SectorNumber, TipSetToken) (bool, error) StateMarketStorageDeal(context.Context, abi.DealID, TipSetToken) (*api.MarketDeal, error) StateMarketStorageDealProposal(context.Context, abi.DealID, TipSetToken) (market.DealProposal, error) diff --git a/extern/storage-sealing/states_sealing.go b/extern/storage-sealing/states_sealing.go index 710883be821..04922a8dd87 100644 --- a/extern/storage-sealing/states_sealing.go +++ b/extern/storage-sealing/states_sealing.go @@ -362,9 +362,9 @@ func (m *Sealing) handlePreCommitting(ctx statemachine.Context, sector SectorInf return err } - deposit := pcd - if cfg.CollateralFromMinerBalance { - deposit = big.Zero() // pay using available miner balance + deposit, err := collateralSendAmount(ctx.Context(), m.api, m.maddr, cfg, pcd) + if err != nil { + return err } enc := new(bytes.Buffer) @@ -633,8 +633,9 @@ func (m *Sealing) handleSubmitCommit(ctx statemachine.Context, sector SectorInfo collateral = big.Zero() } - if cfg.CollateralFromMinerBalance { - collateral = big.Zero() // pay using available miner balance + collateral, err = collateralSendAmount(ctx.Context(), m.api, m.maddr, cfg, collateral) + if err != nil { + return err } goodFunds := big.Add(collateral, big.Int(m.feeCfg.MaxCommitGasFee)) diff --git a/extern/storage-sealing/utils.go b/extern/storage-sealing/utils.go index dadef227d66..7b95bdf7f91 100644 --- a/extern/storage-sealing/utils.go +++ b/extern/storage-sealing/utils.go @@ -1,9 +1,17 @@ package sealing import ( + "context" "math/bits" + "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/types" + "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" ) func fillersFromRem(in abi.UnpaddedPieceSize) ([]abi.UnpaddedPieceSize, error) { @@ -55,3 +63,31 @@ func (m *Sealing) GetSectorInfo(sid abi.SectorNumber) (SectorInfo, error) { err := m.sectors.Get(uint64(sid)).Get(&out) return out, err } + +func collateralSendAmount(ctx context.Context, api interface { + StateMinerAvailableBalance(context.Context, address.Address, TipSetToken) (big.Int, error) +}, maddr address.Address, cfg sealiface.Config, collateral abi.TokenAmount) (abi.TokenAmount, error) { + if cfg.CollateralFromMinerBalance { + avail := types.TotalFilecoinInt + + if !cfg.DisableCollateralFallback { + var err error + avail, err = api.StateMinerAvailableBalance(ctx, maddr, nil) + if err != nil { + return big.Zero(), xerrors.Errorf("getting available miner balance: %w", err) + } + + avail = big.Sub(avail, cfg.AvailableBalanceBuffer) + if avail.LessThan(big.Zero()) { + avail = big.Zero() + } + } + + collateral = big.Sub(collateral, avail) + if collateral.LessThan(big.Zero()) { + collateral = big.Zero() + } + } + + return collateral, nil +} diff --git a/itests/deals_pricing_test.go b/itests/deals_pricing_test.go index 357abec1ed9..82c85f5e430 100644 --- a/itests/deals_pricing_test.go +++ b/itests/deals_pricing_test.go @@ -14,7 +14,7 @@ import ( func TestQuotePriceForUnsealedRetrieval(t *testing.T) { var ( ctx = context.Background() - blocktime = time.Second + blocktime = 50 * time.Millisecond ) kit.QuietMiningLogs() diff --git a/itests/sector_miner_collateral_test.go b/itests/sector_miner_collateral_test.go index 229f594ccea..8e7525dba1d 100644 --- a/itests/sector_miner_collateral_test.go +++ b/itests/sector_miner_collateral_test.go @@ -51,6 +51,9 @@ func TestMinerBalanceCollateral(t *testing.T) { MaxCommitBatch: nSectors, CollateralFromMinerBalance: enabled, + AvailableBalanceBuffer: big.Zero(), + DisableCollateralFallback: false, + AggregateAboveBaseFee: big.Zero(), }, nil }, nil })), diff --git a/node/config/def.go b/node/config/def.go index f474c56ed4b..ad23e3a6786 100644 --- a/node/config/def.go +++ b/node/config/def.go @@ -127,6 +127,10 @@ type SealingConfig struct { // Whether to use available miner balance for sector collateral instead of sending it with each message CollateralFromMinerBalance bool + // Minimum available balance to keep in the miner actor before sending it with messages + AvailableBalanceBuffer types.FIL + // Don't send collateral with messages even if there is no available balance in the miner actor + DisableCollateralFallback bool // enable / disable precommit batching (takes effect after nv13) BatchPreCommits bool @@ -320,13 +324,16 @@ func DefaultStorageMiner() *StorageMiner { Common: defCommon(), Sealing: SealingConfig{ - MaxWaitDealsSectors: 2, // 64G with 32G sectors - MaxSealingSectors: 0, - MaxSealingSectorsForDeals: 0, - WaitDealsDelay: Duration(time.Hour * 6), - AlwaysKeepUnsealedCopy: true, - FinalizeEarly: false, + MaxWaitDealsSectors: 2, // 64G with 32G sectors + MaxSealingSectors: 0, + MaxSealingSectorsForDeals: 0, + WaitDealsDelay: Duration(time.Hour * 6), + AlwaysKeepUnsealedCopy: true, + FinalizeEarly: false, + CollateralFromMinerBalance: false, + AvailableBalanceBuffer: types.FIL(big.Zero()), + DisableCollateralFallback: false, BatchPreCommits: true, MaxPreCommitBatch: miner5.PreCommitSectorBatchMaxSize, // up to 256 sectors diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index fba79785dcf..f669af0cdf9 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -863,13 +863,16 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error return func(cfg sealiface.Config) (err error) { err = mutateCfg(r, func(c *config.StorageMiner) { c.Sealing = config.SealingConfig{ - MaxWaitDealsSectors: cfg.MaxWaitDealsSectors, - MaxSealingSectors: cfg.MaxSealingSectors, - MaxSealingSectorsForDeals: cfg.MaxSealingSectorsForDeals, - WaitDealsDelay: config.Duration(cfg.WaitDealsDelay), - AlwaysKeepUnsealedCopy: cfg.AlwaysKeepUnsealedCopy, - FinalizeEarly: cfg.FinalizeEarly, + MaxWaitDealsSectors: cfg.MaxWaitDealsSectors, + MaxSealingSectors: cfg.MaxSealingSectors, + MaxSealingSectorsForDeals: cfg.MaxSealingSectorsForDeals, + WaitDealsDelay: config.Duration(cfg.WaitDealsDelay), + AlwaysKeepUnsealedCopy: cfg.AlwaysKeepUnsealedCopy, + FinalizeEarly: cfg.FinalizeEarly, + CollateralFromMinerBalance: cfg.CollateralFromMinerBalance, + AvailableBalanceBuffer: types.FIL(cfg.AvailableBalanceBuffer), + DisableCollateralFallback: cfg.DisableCollateralFallback, BatchPreCommits: cfg.BatchPreCommits, MaxPreCommitBatch: cfg.MaxPreCommitBatch, @@ -894,13 +897,16 @@ func NewSetSealConfigFunc(r repo.LockedRepo) (dtypes.SetSealingConfigFunc, error func ToSealingConfig(cfg *config.StorageMiner) sealiface.Config { return sealiface.Config{ - MaxWaitDealsSectors: cfg.Sealing.MaxWaitDealsSectors, - MaxSealingSectors: cfg.Sealing.MaxSealingSectors, - MaxSealingSectorsForDeals: cfg.Sealing.MaxSealingSectorsForDeals, - WaitDealsDelay: time.Duration(cfg.Sealing.WaitDealsDelay), - AlwaysKeepUnsealedCopy: cfg.Sealing.AlwaysKeepUnsealedCopy, - FinalizeEarly: cfg.Sealing.FinalizeEarly, + MaxWaitDealsSectors: cfg.Sealing.MaxWaitDealsSectors, + MaxSealingSectors: cfg.Sealing.MaxSealingSectors, + MaxSealingSectorsForDeals: cfg.Sealing.MaxSealingSectorsForDeals, + WaitDealsDelay: time.Duration(cfg.Sealing.WaitDealsDelay), + AlwaysKeepUnsealedCopy: cfg.Sealing.AlwaysKeepUnsealedCopy, + FinalizeEarly: cfg.Sealing.FinalizeEarly, + CollateralFromMinerBalance: cfg.Sealing.CollateralFromMinerBalance, + AvailableBalanceBuffer: types.BigInt(cfg.Sealing.AvailableBalanceBuffer), + DisableCollateralFallback: cfg.Sealing.DisableCollateralFallback, BatchPreCommits: cfg.Sealing.BatchPreCommits, MaxPreCommitBatch: cfg.Sealing.MaxPreCommitBatch, diff --git a/storage/adapter_storage_miner.go b/storage/adapter_storage_miner.go index 895e7846db3..531fe2d03a4 100644 --- a/storage/adapter_storage_miner.go +++ b/storage/adapter_storage_miner.go @@ -76,6 +76,15 @@ func (s SealingAPIAdapter) StateMinerInfo(ctx context.Context, maddr address.Add return s.delegate.StateMinerInfo(ctx, maddr, tsk) } +func (s SealingAPIAdapter) StateMinerAvailableBalance(ctx context.Context, maddr address.Address, tok sealing.TipSetToken) (big.Int, error) { + tsk, err := types.TipSetKeyFromBytes(tok) + if err != nil { + return big.Zero(), xerrors.Errorf("failed to unmarshal TipSetToken to TipSetKey: %w", err) + } + + return s.delegate.StateMinerAvailableBalance(ctx, maddr, tsk) +} + func (s SealingAPIAdapter) StateMinerWorkerAddress(ctx context.Context, maddr address.Address, tok sealing.TipSetToken) (address.Address, error) { // TODO: update storage-fsm to just StateMinerInfo mi, err := s.StateMinerInfo(ctx, maddr, tok) diff --git a/storage/miner.go b/storage/miner.go index 3d29e0ef11e..04aafdd67bd 100644 --- a/storage/miner.go +++ b/storage/miner.go @@ -89,6 +89,7 @@ type fullNodeFilteredAPI interface { StateSectorGetInfo(context.Context, address.Address, abi.SectorNumber, types.TipSetKey) (*miner.SectorOnChainInfo, error) StateSectorPartition(ctx context.Context, maddr address.Address, sectorNumber abi.SectorNumber, tok types.TipSetKey) (*miner.SectorLocation, error) StateMinerInfo(context.Context, address.Address, types.TipSetKey) (miner.MinerInfo, error) + StateMinerAvailableBalance(ctx context.Context, maddr address.Address, tok types.TipSetKey) (types.BigInt, error) StateMinerDeadlines(context.Context, address.Address, types.TipSetKey) ([]api.Deadline, error) StateMinerPartitions(context.Context, address.Address, uint64, types.TipSetKey) ([]api.Partition, error) StateMinerProvingDeadline(context.Context, address.Address, types.TipSetKey) (*dline.Info, error) From 0678b3fa05ba99a1793d921e0f2cbb854c00808f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Tue, 13 Jul 2021 17:06:41 +0200 Subject: [PATCH 3/3] sealing: Cleanup DisableCollateralFallback handling --- extern/storage-sealing/utils.go | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/extern/storage-sealing/utils.go b/extern/storage-sealing/utils.go index 7b95bdf7f91..3dc4c4d1ea3 100644 --- a/extern/storage-sealing/utils.go +++ b/extern/storage-sealing/utils.go @@ -10,7 +10,6 @@ import ( "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" - "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/extern/storage-sealing/sealiface" ) @@ -68,19 +67,18 @@ func collateralSendAmount(ctx context.Context, api interface { StateMinerAvailableBalance(context.Context, address.Address, TipSetToken) (big.Int, error) }, maddr address.Address, cfg sealiface.Config, collateral abi.TokenAmount) (abi.TokenAmount, error) { if cfg.CollateralFromMinerBalance { - avail := types.TotalFilecoinInt - - if !cfg.DisableCollateralFallback { - var err error - avail, err = api.StateMinerAvailableBalance(ctx, maddr, nil) - if err != nil { - return big.Zero(), xerrors.Errorf("getting available miner balance: %w", err) - } - - avail = big.Sub(avail, cfg.AvailableBalanceBuffer) - if avail.LessThan(big.Zero()) { - avail = big.Zero() - } + if cfg.DisableCollateralFallback { + return big.Zero(), nil + } + + avail, err := api.StateMinerAvailableBalance(ctx, maddr, nil) + if err != nil { + return big.Zero(), xerrors.Errorf("getting available miner balance: %w", err) + } + + avail = big.Sub(avail, cfg.AvailableBalanceBuffer) + if avail.LessThan(big.Zero()) { + avail = big.Zero() } collateral = big.Sub(collateral, avail)