From 673f9238be6c77529cdb05cf9c73aa6570530746 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Magiera?= Date: Thu, 7 Jul 2022 12:33:40 +0200 Subject: [PATCH 1/3] feat: wdpost: Config for maximum partition count per message --- .../en/default-lotus-miner-config.toml | 6 ++ node/builder_miner.go | 2 +- node/config/doc_gen.go | 6 ++ node/config/types.go | 14 +++ node/modules/storageminer.go | 4 +- storage/wdpost_run.go | 7 ++ storage/wdpost_run_test.go | 99 +++++++++++++++++++ storage/wdpost_sched.go | 37 +++---- 8 files changed, 155 insertions(+), 20 deletions(-) diff --git a/documentation/en/default-lotus-miner-config.toml b/documentation/en/default-lotus-miner-config.toml index 1e1b0369d..276e1dd5f 100644 --- a/documentation/en/default-lotus-miner-config.toml +++ b/documentation/en/default-lotus-miner-config.toml @@ -315,6 +315,12 @@ # env var: LOTUS_PROVING_PARALLELCHECKLIMIT #ParallelCheckLimit = 128 + # Setting this value above the network limit has no effect + # + # type: int + # env var: LOTUS_PROVING_MAXPARTITIONSPERMESSAGE + #MaxPartitionsPerMessage = 0 + [Sealing] # Upper bound on how many sectors can be waiting for more deals to be packed in it before it begins sealing at any given time. diff --git a/node/builder_miner.go b/node/builder_miner.go index 2223d14ce..3bce63281 100644 --- a/node/builder_miner.go +++ b/node/builder_miner.go @@ -116,7 +116,7 @@ func ConfigStorageMiner(c interface{}) Option { Override(new(*miner.Miner), modules.SetupBlockProducer), Override(new(gen.WinningPoStProver), storage.NewWinningPoStProver), Override(new(*storage.Miner), modules.StorageMiner(cfg.Fees)), - Override(new(*storage.WindowPoStScheduler), modules.WindowPostScheduler(cfg.Fees)), + Override(new(*storage.WindowPoStScheduler), modules.WindowPostScheduler(cfg.Fees, cfg.Proving)), Override(new(sectorblocks.SectorBuilder), From(new(*storage.Miner))), ), diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index cf51fb13e..4a90cfcc6 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -629,6 +629,12 @@ over the worker address if this flag is set.`, Comment: `Maximum number of sector checks to run in parallel. (0 = unlimited)`, }, + { + Name: "MaxPartitionsPerMessage", + Type: "int", + + Comment: `Setting this value above the network limit has no effect`, + }, }, "Pubsub": []DocField{ { diff --git a/node/config/types.go b/node/config/types.go index b5b1fae7e..bc1098b74 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -223,6 +223,20 @@ type ProvingConfig struct { ParallelCheckLimit int // todo disable builtin post + // Maximum number of partitions to prove in a single SubmitWindowPoSt messace. 0 = network limit (10 in nv16) + // + // A single partition may contain up to 2349 32GiB sectors, or 2300 64GiB sectors. + // + // The maximum number of sectors which can be proven in a single PoSt message is 25000 in network version 16, which + // means that a single message can prove at most 10 partinions + // + // In some cases when submitting PoSt messages which are recovering sectors, the default network limit may still be + // too high to fit in the block gas limit; In those cases it may be necessary to set this value to something lower + // than 10; Note that setting this value lower may result in less efficient gas use - more messages will be sent, + // to prove each deadline, resulting in more total gas use (but each message will have lower gas limit) + // + // Setting this value above the network limit has no effect + MaxPartitionsPerMessage int } type SealingConfig struct { diff --git a/node/modules/storageminer.go b/node/modules/storageminer.go index 09b8b6f31..5ac6070fe 100644 --- a/node/modules/storageminer.go +++ b/node/modules/storageminer.go @@ -256,7 +256,7 @@ func StorageMiner(fc config.MinerFeeConfig) func(params StorageMinerParams) (*st } } -func WindowPostScheduler(fc config.MinerFeeConfig) func(params StorageMinerParams) (*storage.WindowPoStScheduler, error) { +func WindowPostScheduler(fc config.MinerFeeConfig, pc config.ProvingConfig) func(params StorageMinerParams) (*storage.WindowPoStScheduler, error) { return func(params StorageMinerParams) (*storage.WindowPoStScheduler, error) { var ( mctx = params.MetricsCtx @@ -271,7 +271,7 @@ func WindowPostScheduler(fc config.MinerFeeConfig) func(params StorageMinerParam ctx := helpers.LifecycleCtx(mctx, lc) - fps, err := storage.NewWindowedPoStScheduler(api, fc, as, sealer, verif, sealer, j, maddr) + fps, err := storage.NewWindowedPoStScheduler(api, fc, pc, as, sealer, verif, sealer, j, maddr) if err != nil { return nil, err } diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index 916c5a905..d8c324865 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -778,6 +778,13 @@ func (s *WindowPoStScheduler) batchPartitions(partitions []api.Partition, nv net partitionsPerMsg = declMax } + // respect user config if set + if s.maxPartitionsPerMessage > 0 { + if partitionsPerMsg > s.maxPartitionsPerMessage { + partitionsPerMsg = s.maxPartitionsPerMessage + } + } + // The number of messages will be: // ceiling(number of partitions / partitions per message) batchCount := len(partitions) / partitionsPerMsg diff --git a/storage/wdpost_run_test.go b/storage/wdpost_run_test.go index 6efb3e547..dcb5e9525 100644 --- a/storage/wdpost_run_test.go +++ b/storage/wdpost_run_test.go @@ -275,6 +275,105 @@ func TestWDPostDoPost(t *testing.T) { } } +// TestWDPostDoPost verifies that doPost will send the correct number of window +// PoST messages for a given number of partitions based on user config +func TestWDPostDoPostPartLimitConfig(t *testing.T) { + //stm: @CHAIN_SYNCER_LOAD_GENESIS_001, @CHAIN_SYNCER_FETCH_TIPSET_001, + //stm: @CHAIN_SYNCER_START_001, @CHAIN_SYNCER_SYNC_001, @BLOCKCHAIN_BEACON_VALIDATE_BLOCK_VALUES_01 + //stm: @CHAIN_SYNCER_COLLECT_CHAIN_001, @CHAIN_SYNCER_COLLECT_HEADERS_001, @CHAIN_SYNCER_VALIDATE_TIPSET_001 + //stm: @CHAIN_SYNCER_NEW_PEER_HEAD_001, @CHAIN_SYNCER_VALIDATE_MESSAGE_META_001, @CHAIN_SYNCER_STOP_001 + ctx := context.Background() + expectedMsgCount := 364 + + proofType := abi.RegisteredPoStProof_StackedDrgWindow2KiBV1 + postAct := tutils.NewIDAddr(t, 100) + + mockStgMinerAPI := newMockStorageMinerAPI() + + // Get the number of sectors allowed in a partition for this proof type + sectorsPerPartition, err := builtin.PoStProofWindowPoStPartitionSectors(proofType) + require.NoError(t, err) + // Work out the number of partitions that can be included in a message + // without exceeding the message sector limit + + //stm: @BLOCKCHAIN_POLICY_GET_MAX_POST_PARTITIONS_001 + partitionsPerMsg, err := policy.GetMaxPoStPartitions(network.Version13, proofType) + require.NoError(t, err) + if partitionsPerMsg > minertypes.AddressedPartitionsMax { + partitionsPerMsg = minertypes.AddressedPartitionsMax + } + + partitionCount := 4 * partitionsPerMsg + + // Assert that user config is less than network limit + userPartLimit := 33 + lastMsgParts := 21 + require.Greater(t, partitionCount, userPartLimit) + + // Assert that we consts are correct + require.Equal(t, (expectedMsgCount-1)*userPartLimit+lastMsgParts, 4*partitionsPerMsg) + + var partitions []api.Partition + for p := 0; p < partitionCount; p++ { + sectors := bitfield.New() + for s := uint64(0); s < sectorsPerPartition; s++ { + sectors.Set(s) + } + partitions = append(partitions, api.Partition{ + AllSectors: sectors, + FaultySectors: bitfield.New(), + RecoveringSectors: bitfield.New(), + LiveSectors: sectors, + ActiveSectors: sectors, + }) + } + mockStgMinerAPI.setPartitions(partitions) + + // Run window PoST + scheduler := &WindowPoStScheduler{ + api: mockStgMinerAPI, + prover: &mockProver{}, + verifier: &mockVerif{}, + faultTracker: &mockFaultTracker{}, + proofType: proofType, + actor: postAct, + journal: journal.NilJournal(), + addrSel: &ctladdr.AddressSelector{}, + + maxPartitionsPerMessage: userPartLimit, + } + + di := &dline.Info{ + WPoStPeriodDeadlines: minertypes.WPoStPeriodDeadlines, + WPoStProvingPeriod: minertypes.WPoStProvingPeriod, + WPoStChallengeWindow: minertypes.WPoStChallengeWindow, + WPoStChallengeLookback: minertypes.WPoStChallengeLookback, + FaultDeclarationCutoff: minertypes.FaultDeclarationCutoff, + } + ts := mockTipSet(t) + + scheduler.startGeneratePoST(ctx, ts, di, func(posts []minertypes.SubmitWindowedPoStParams, err error) { + scheduler.startSubmitPoST(ctx, ts, di, posts, func(err error) {}) + }) + + // Read the window PoST messages + for i := 0; i < expectedMsgCount; i++ { + msg := <-mockStgMinerAPI.pushedMessages + require.Equal(t, builtin.MethodsMiner.SubmitWindowedPoSt, msg.Method) + var params minertypes.SubmitWindowedPoStParams + err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)) + require.NoError(t, err) + + if i == expectedMsgCount-1 { + // In the last message we only included a 21 partitions + require.Len(t, params.Partitions, lastMsgParts) + } else { + // All previous messages should include the full number of partitions + require.Len(t, params.Partitions, userPartLimit) + } + } +} + func mockTipSet(t *testing.T) *types.TipSet { minerAct := tutils.NewActorAddr(t, "miner") c, err := cid.Decode("QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH") diff --git a/storage/wdpost_sched.go b/storage/wdpost_sched.go index 53801e362..897705646 100644 --- a/storage/wdpost_sched.go +++ b/storage/wdpost_sched.go @@ -30,15 +30,16 @@ import ( // WindowPoStScheduler watches the chain though the changeHandler, which in turn // turn calls the scheduler when the time arrives to do work. type WindowPoStScheduler struct { - api fullNodeFilteredAPI - feeCfg config.MinerFeeConfig - addrSel *AddressSelector - prover storage.Prover - verifier ffiwrapper.Verifier - faultTracker sectorstorage.FaultTracker - proofType abi.RegisteredPoStProof - partitionSectors uint64 - ch *changeHandler + api fullNodeFilteredAPI + feeCfg config.MinerFeeConfig + addrSel *AddressSelector + prover storage.Prover + verifier ffiwrapper.Verifier + faultTracker sectorstorage.FaultTracker + proofType abi.RegisteredPoStProof + partitionSectors uint64 + maxPartitionsPerMessage int + ch *changeHandler actor address.Address @@ -52,6 +53,7 @@ type WindowPoStScheduler struct { // NewWindowedPoStScheduler creates a new WindowPoStScheduler scheduler. func NewWindowedPoStScheduler(api fullNodeFilteredAPI, cfg config.MinerFeeConfig, + pcfg config.ProvingConfig, as *AddressSelector, sp storage.Prover, verif ffiwrapper.Verifier, @@ -64,14 +66,15 @@ func NewWindowedPoStScheduler(api fullNodeFilteredAPI, } return &WindowPoStScheduler{ - api: api, - feeCfg: cfg, - addrSel: as, - prover: sp, - verifier: verif, - faultTracker: ft, - proofType: mi.WindowPoStProofType, - partitionSectors: mi.WindowPoStPartitionSectors, + api: api, + feeCfg: cfg, + addrSel: as, + prover: sp, + verifier: verif, + faultTracker: ft, + proofType: mi.WindowPoStProofType, + partitionSectors: mi.WindowPoStPartitionSectors, + maxPartitionsPerMessage: pcfg.MaxPartitionsPerMessage, actor: actor, evtTypes: [...]journal.EventType{ From d68a60e8d9f53fee6a0251ba004fe47618f436e9 Mon Sep 17 00:00:00 2001 From: Aayush Date: Thu, 7 Jul 2022 10:52:22 -0400 Subject: [PATCH 2/3] feat: recovery: Config for maximum partition count per message --- .../en/default-lotus-miner-config.toml | 14 +- node/config/doc_gen.go | 12 +- node/config/types.go | 13 +- storage/wdpost_run.go | 144 +++++++++--------- storage/wdpost_run_test.go | 97 +++++++++++- storage/wdpost_sched.go | 42 ++--- 6 files changed, 226 insertions(+), 96 deletions(-) diff --git a/documentation/en/default-lotus-miner-config.toml b/documentation/en/default-lotus-miner-config.toml index 276e1dd5f..c10a4f476 100644 --- a/documentation/en/default-lotus-miner-config.toml +++ b/documentation/en/default-lotus-miner-config.toml @@ -318,8 +318,18 @@ # Setting this value above the network limit has no effect # # type: int - # env var: LOTUS_PROVING_MAXPARTITIONSPERMESSAGE - #MaxPartitionsPerMessage = 0 + # env var: LOTUS_PROVING_MAXPARTITIONSPERPOSTMESSAGE + #MaxPartitionsPerPoStMessage = 0 + + # In some cases when submitting DeclareFaultsRecovered messages, + # there may be too many recoveries to fit in a BlockGasLimit. + # In those cases it may be necessary to set this value to something low (eg 1); + # Note that setting this value lower may result in less efficient gas use - more messages will be sent than needed, + # resulting in more total gas use (but each message will have lower gas limit) + # + # type: int + # env var: LOTUS_PROVING_MAXPARTITIONSPERRECOVERYMESSAGE + #MaxPartitionsPerRecoveryMessage = 0 [Sealing] diff --git a/node/config/doc_gen.go b/node/config/doc_gen.go index 4a90cfcc6..8b8b14999 100644 --- a/node/config/doc_gen.go +++ b/node/config/doc_gen.go @@ -630,11 +630,21 @@ over the worker address if this flag is set.`, Comment: `Maximum number of sector checks to run in parallel. (0 = unlimited)`, }, { - Name: "MaxPartitionsPerMessage", + Name: "MaxPartitionsPerPoStMessage", Type: "int", Comment: `Setting this value above the network limit has no effect`, }, + { + Name: "MaxPartitionsPerRecoveryMessage", + Type: "int", + + Comment: `In some cases when submitting DeclareFaultsRecovered messages, +there may be too many recoveries to fit in a BlockGasLimit. +In those cases it may be necessary to set this value to something low (eg 1); +Note that setting this value lower may result in less efficient gas use - more messages will be sent than needed, +resulting in more total gas use (but each message will have lower gas limit)`, + }, }, "Pubsub": []DocField{ { diff --git a/node/config/types.go b/node/config/types.go index bc1098b74..ecced5514 100644 --- a/node/config/types.go +++ b/node/config/types.go @@ -222,7 +222,6 @@ type ProvingConfig struct { // Maximum number of sector checks to run in parallel. (0 = unlimited) ParallelCheckLimit int - // todo disable builtin post // Maximum number of partitions to prove in a single SubmitWindowPoSt messace. 0 = network limit (10 in nv16) // // A single partition may contain up to 2349 32GiB sectors, or 2300 64GiB sectors. @@ -236,7 +235,17 @@ type ProvingConfig struct { // to prove each deadline, resulting in more total gas use (but each message will have lower gas limit) // // Setting this value above the network limit has no effect - MaxPartitionsPerMessage int + MaxPartitionsPerPoStMessage int + + // Maximum number of partitions to declare in a single DeclareFaultsRecovered message. 0 = no limit. + + // In some cases when submitting DeclareFaultsRecovered messages, + // there may be too many recoveries to fit in a BlockGasLimit. + // In those cases it may be necessary to set this value to something low (eg 1); + // Note that setting this value lower may result in less efficient gas use - more messages will be sent than needed, + // resulting in more total gas use (but each message will have lower gas limit) + MaxPartitionsPerRecoveryMessage int + // todo disable builtin post } type SealingConfig struct { diff --git a/storage/wdpost_run.go b/storage/wdpost_run.go index d8c324865..2c0a89d6c 100644 --- a/storage/wdpost_run.go +++ b/storage/wdpost_run.go @@ -261,14 +261,15 @@ func (s *WindowPoStScheduler) checkSectors(ctx context.Context, check bitfield.B // TODO: the waiting should happen in the background. Right now this // is blocking/delaying the actual generation and submission of WindowPoSts in // this deadline! -func (s *WindowPoStScheduler) declareRecoveries(ctx context.Context, dlIdx uint64, partitions []api.Partition, tsk types.TipSetKey) ([]miner.RecoveryDeclaration, *types.SignedMessage, error) { +func (s *WindowPoStScheduler) declareRecoveries(ctx context.Context, dlIdx uint64, partitions []api.Partition, tsk types.TipSetKey) ([][]miner.RecoveryDeclaration, []*types.SignedMessage, error) { ctx, span := trace.StartSpan(ctx, "storage.declareRecoveries") defer span.End() faulty := uint64(0) - params := &miner.DeclareFaultsRecoveredParams{ - Recoveries: []miner.RecoveryDeclaration{}, - } + + var batchedRecoveryDecls [][]miner.RecoveryDeclaration + batchedRecoveryDecls = append(batchedRecoveryDecls, []miner.RecoveryDeclaration{}) + totalRecoveries := 0 for partIdx, partition := range partitions { unrecovered, err := bitfield.SubtractBitField(partition.FaultySectors, partition.RecoveringSectors) @@ -302,55 +303,72 @@ func (s *WindowPoStScheduler) declareRecoveries(ctx context.Context, dlIdx uint6 continue } - params.Recoveries = append(params.Recoveries, miner.RecoveryDeclaration{ + // respect user config if set + if s.maxPartitionsPerRecoveryMessage > 0 && + len(batchedRecoveryDecls[len(batchedRecoveryDecls)-1]) >= s.maxPartitionsPerRecoveryMessage { + batchedRecoveryDecls = append(batchedRecoveryDecls, []miner.RecoveryDeclaration{}) + } + + batchedRecoveryDecls[len(batchedRecoveryDecls)-1] = append(batchedRecoveryDecls[len(batchedRecoveryDecls)-1], miner.RecoveryDeclaration{ Deadline: dlIdx, Partition: uint64(partIdx), Sectors: recovered, }) + + totalRecoveries++ } - recoveries := params.Recoveries - if len(recoveries) == 0 { + if totalRecoveries == 0 { if faulty != 0 { log.Warnw("No recoveries to declare", "deadline", dlIdx, "faulty", faulty) } - return recoveries, nil, nil + return nil, nil, nil } - enc, aerr := actors.SerializeParams(params) - if aerr != nil { - return recoveries, nil, xerrors.Errorf("could not serialize declare recoveries parameters: %w", aerr) + var msgs []*types.SignedMessage + for _, recovery := range batchedRecoveryDecls { + params := &miner.DeclareFaultsRecoveredParams{ + Recoveries: recovery, + } + + enc, aerr := actors.SerializeParams(params) + if aerr != nil { + return nil, nil, xerrors.Errorf("could not serialize declare recoveries parameters: %w", aerr) + } + + msg := &types.Message{ + To: s.actor, + Method: builtin.MethodsMiner.DeclareFaultsRecovered, + Params: enc, + Value: types.NewInt(0), + } + spec := &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)} + if err := s.prepareMessage(ctx, msg, spec); err != nil { + return nil, nil, err + } + + sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)}) + if err != nil { + return nil, nil, xerrors.Errorf("pushing message to mpool: %w", err) + } + + log.Warnw("declare faults recovered Message CID", "cid", sm.Cid()) + msgs = append(msgs, sm) } - msg := &types.Message{ - To: s.actor, - Method: builtin.MethodsMiner.DeclareFaultsRecovered, - Params: enc, - Value: types.NewInt(0), - } - spec := &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)} - if err := s.prepareMessage(ctx, msg, spec); err != nil { - return recoveries, nil, err + for _, msg := range msgs { + rec, err := s.api.StateWaitMsg(context.TODO(), msg.Cid(), build.MessageConfidence, api.LookbackNoLimit, true) + if err != nil { + return batchedRecoveryDecls, msgs, xerrors.Errorf("declare faults recovered wait error: %w", err) + } + + if rec.Receipt.ExitCode != 0 { + return batchedRecoveryDecls, msgs, xerrors.Errorf("declare faults recovered wait non-0 exit code: %d", rec.Receipt.ExitCode) + } } - sm, err := s.api.MpoolPushMessage(ctx, msg, &api.MessageSendSpec{MaxFee: abi.TokenAmount(s.feeCfg.MaxWindowPoStGasFee)}) - if err != nil { - return recoveries, sm, xerrors.Errorf("pushing message to mpool: %w", err) - } - - log.Warnw("declare faults recovered Message CID", "cid", sm.Cid()) - - rec, err := s.api.StateWaitMsg(context.TODO(), sm.Cid(), build.MessageConfidence, api.LookbackNoLimit, true) - if err != nil { - return recoveries, sm, xerrors.Errorf("declare faults recovered wait error: %w", err) - } - - if rec.Receipt.ExitCode != 0 { - return recoveries, sm, xerrors.Errorf("declare faults recovered wait non-0 exit code: %d", rec.Receipt.ExitCode) - } - - return recoveries, sm, nil + return batchedRecoveryDecls, msgs, nil } // declareFaults identifies the sectors on the specified proving deadline that @@ -464,9 +482,8 @@ func (s *WindowPoStScheduler) asyncFaultRecover(di dline.Info, ts *types.TipSet) } var ( - sigmsg *types.SignedMessage - recoveries []miner.RecoveryDeclaration - faults []miner.FaultDeclaration + sigmsgs []*types.SignedMessage + recoveries [][]miner.RecoveryDeclaration // optionalCid returns the CID of the message, or cid.Undef is the // message is nil. We don't need the argument (could capture the @@ -479,37 +496,28 @@ func (s *WindowPoStScheduler) asyncFaultRecover(di dline.Info, ts *types.TipSet) } ) - if recoveries, sigmsg, err = s.declareRecoveries(context.TODO(), declDeadline, partitions, ts.Key()); err != nil { + if recoveries, sigmsgs, err = s.declareRecoveries(context.TODO(), declDeadline, partitions, ts.Key()); err != nil { // TODO: This is potentially quite bad, but not even trying to post when this fails is objectively worse log.Errorf("checking sector recoveries: %v", err) } - s.journal.RecordEvent(s.evtTypes[evtTypeWdPoStRecoveries], func() interface{} { - j := WdPoStRecoveriesProcessedEvt{ - evtCommon: s.getEvtCommon(err), - Declarations: recoveries, - MessageCID: optionalCid(sigmsg), + // should always be true, skip journaling if not for some reason + if len(recoveries) == len(sigmsgs) { + for i, recovery := range recoveries { + // clone for function literal + recovery := recovery + msgCID := optionalCid(sigmsgs[i]) + s.journal.RecordEvent(s.evtTypes[evtTypeWdPoStRecoveries], func() interface{} { + j := WdPoStRecoveriesProcessedEvt{ + evtCommon: s.getEvtCommon(err), + Declarations: recovery, + MessageCID: msgCID, + } + j.Error = err + return j + }) } - j.Error = err - return j - }) - - if ts.Height() > build.UpgradeIgnitionHeight { - return // FORK: declaring faults after ignition upgrade makes no sense } - - if faults, sigmsg, err = s.declareFaults(context.TODO(), declDeadline, partitions, ts.Key()); err != nil { - // TODO: This is also potentially really bad, but we try to post anyways - log.Errorf("checking sector faults: %v", err) - } - - s.journal.RecordEvent(s.evtTypes[evtTypeWdPoStFaults], func() interface{} { - return WdPoStFaultsProcessedEvt{ - evtCommon: s.getEvtCommon(err), - Declarations: faults, - MessageCID: optionalCid(sigmsg), - } - }) }() } @@ -779,9 +787,9 @@ func (s *WindowPoStScheduler) batchPartitions(partitions []api.Partition, nv net } // respect user config if set - if s.maxPartitionsPerMessage > 0 { - if partitionsPerMsg > s.maxPartitionsPerMessage { - partitionsPerMsg = s.maxPartitionsPerMessage + if s.maxPartitionsPerPostMessage > 0 { + if partitionsPerMsg > s.maxPartitionsPerPostMessage { + partitionsPerMsg = s.maxPartitionsPerPostMessage } } diff --git a/storage/wdpost_run_test.go b/storage/wdpost_run_test.go index dcb5e9525..51dc5782f 100644 --- a/storage/wdpost_run_test.go +++ b/storage/wdpost_run_test.go @@ -275,7 +275,7 @@ func TestWDPostDoPost(t *testing.T) { } } -// TestWDPostDoPost verifies that doPost will send the correct number of window +// TestWDPostDoPostPartLimitConfig verifies that doPost will send the correct number of window // PoST messages for a given number of partitions based on user config func TestWDPostDoPostPartLimitConfig(t *testing.T) { //stm: @CHAIN_SYNCER_LOAD_GENESIS_001, @CHAIN_SYNCER_FETCH_TIPSET_001, @@ -338,9 +338,8 @@ func TestWDPostDoPostPartLimitConfig(t *testing.T) { proofType: proofType, actor: postAct, journal: journal.NilJournal(), - addrSel: &ctladdr.AddressSelector{}, - maxPartitionsPerMessage: userPartLimit, + maxPartitionsPerPostMessage: userPartLimit, } di := &dline.Info{ @@ -374,6 +373,98 @@ func TestWDPostDoPostPartLimitConfig(t *testing.T) { } } +// TestWDPostDeclareRecoveriesPartLimitConfig verifies that declareRecoveries will send the correct number of +// DeclareFaultsRecovered messages for a given number of partitions based on user config +func TestWDPostDeclareRecoveriesPartLimitConfig(t *testing.T) { + //stm: @CHAIN_SYNCER_LOAD_GENESIS_001, @CHAIN_SYNCER_FETCH_TIPSET_001, + //stm: @CHAIN_SYNCER_START_001, @CHAIN_SYNCER_SYNC_001, @BLOCKCHAIN_BEACON_VALIDATE_BLOCK_VALUES_01 + //stm: @CHAIN_SYNCER_COLLECT_CHAIN_001, @CHAIN_SYNCER_COLLECT_HEADERS_001, @CHAIN_SYNCER_VALIDATE_TIPSET_001 + //stm: @CHAIN_SYNCER_NEW_PEER_HEAD_001, @CHAIN_SYNCER_VALIDATE_MESSAGE_META_001, @CHAIN_SYNCER_STOP_001 + ctx := context.Background() + + proofType := abi.RegisteredPoStProof_StackedDrgWindow2KiBV1 + postAct := tutils.NewIDAddr(t, 100) + + mockStgMinerAPI := newMockStorageMinerAPI() + + // Get the number of sectors allowed in a partition for this proof type + sectorsPerPartition, err := builtin.PoStProofWindowPoStPartitionSectors(proofType) + require.NoError(t, err) + + // Let's have 11/20 partitions with faulty sectors, and a config of 3 partitions per message + userPartLimit := 3 + partitionCount := 20 + faultyPartitionCount := 11 + + var partitions []api.Partition + for p := 0; p < partitionCount; p++ { + sectors := bitfield.New() + for s := uint64(0); s < sectorsPerPartition; s++ { + sectors.Set(s) + } + + partition := api.Partition{ + AllSectors: sectors, + FaultySectors: bitfield.New(), + RecoveringSectors: bitfield.New(), + LiveSectors: sectors, + ActiveSectors: sectors, + } + + if p < faultyPartitionCount { + partition.FaultySectors = sectors + } + + partitions = append(partitions, partition) + } + + mockStgMinerAPI.setPartitions(partitions) + + // Run declareRecoverios + scheduler := &WindowPoStScheduler{ + api: mockStgMinerAPI, + prover: &mockProver{}, + verifier: &mockVerif{}, + faultTracker: &mockFaultTracker{}, + proofType: proofType, + actor: postAct, + journal: journal.NilJournal(), + + maxPartitionsPerRecoveryMessage: userPartLimit, + } + + di := uint64(0) + ts := mockTipSet(t) + + expectedMsgCount := faultyPartitionCount/userPartLimit + 1 + lastMsgParts := faultyPartitionCount % userPartLimit + + go func() { + batchedRecoveries, msgs, err := scheduler.declareRecoveries(ctx, di, partitions, ts.Key()) + require.NoError(t, err, "failed to declare recoveries") + require.Equal(t, len(batchedRecoveries), len(msgs)) + require.Equal(t, expectedMsgCount, len(msgs)) + }() + + // Read the window PoST messages + for i := 0; i < expectedMsgCount; i++ { + msg := <-mockStgMinerAPI.pushedMessages + require.Equal(t, builtin.MethodsMiner.DeclareFaultsRecovered, msg.Method) + var params minertypes.DeclareFaultsRecoveredParams + err := params.UnmarshalCBOR(bytes.NewReader(msg.Params)) + require.NoError(t, err) + + if i == expectedMsgCount-1 { + // In the last message we only included a 21 partitions + require.Len(t, params.Recoveries, lastMsgParts) + } else { + // All previous messages should include the full number of partitions + require.Len(t, params.Recoveries, userPartLimit) + } + + } +} + func mockTipSet(t *testing.T) *types.TipSet { minerAct := tutils.NewActorAddr(t, "miner") c, err := cid.Decode("QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH") diff --git a/storage/wdpost_sched.go b/storage/wdpost_sched.go index 897705646..c9a791b4f 100644 --- a/storage/wdpost_sched.go +++ b/storage/wdpost_sched.go @@ -30,16 +30,17 @@ import ( // WindowPoStScheduler watches the chain though the changeHandler, which in turn // turn calls the scheduler when the time arrives to do work. type WindowPoStScheduler struct { - api fullNodeFilteredAPI - feeCfg config.MinerFeeConfig - addrSel *AddressSelector - prover storage.Prover - verifier ffiwrapper.Verifier - faultTracker sectorstorage.FaultTracker - proofType abi.RegisteredPoStProof - partitionSectors uint64 - maxPartitionsPerMessage int - ch *changeHandler + api fullNodeFilteredAPI + feeCfg config.MinerFeeConfig + addrSel *AddressSelector + prover storage.Prover + verifier ffiwrapper.Verifier + faultTracker sectorstorage.FaultTracker + proofType abi.RegisteredPoStProof + partitionSectors uint64 + maxPartitionsPerPostMessage int + maxPartitionsPerRecoveryMessage int + ch *changeHandler actor address.Address @@ -66,17 +67,18 @@ func NewWindowedPoStScheduler(api fullNodeFilteredAPI, } return &WindowPoStScheduler{ - api: api, - feeCfg: cfg, - addrSel: as, - prover: sp, - verifier: verif, - faultTracker: ft, - proofType: mi.WindowPoStProofType, - partitionSectors: mi.WindowPoStPartitionSectors, - maxPartitionsPerMessage: pcfg.MaxPartitionsPerMessage, + api: api, + feeCfg: cfg, + addrSel: as, + prover: sp, + verifier: verif, + faultTracker: ft, + proofType: mi.WindowPoStProofType, + partitionSectors: mi.WindowPoStPartitionSectors, - actor: actor, + maxPartitionsPerPostMessage: pcfg.MaxPartitionsPerPoStMessage, + maxPartitionsPerRecoveryMessage: pcfg.MaxPartitionsPerRecoveryMessage, + actor: actor, evtTypes: [...]journal.EventType{ evtTypeWdPoStScheduler: j.RegisterEventType("wdpost", "scheduler"), evtTypeWdPoStProofs: j.RegisterEventType("wdpost", "proofs_processed"), From a596b9c2ac3cddda4ab85bd5be699f62ecc4b19e Mon Sep 17 00:00:00 2001 From: Jennifer Wang Date: Thu, 7 Jul 2022 16:03:32 -0400 Subject: [PATCH 3/3] v1.16.1 prep --- CHANGELOG.md | 12 +++++++++++- build/openrpc/full.json.gz | Bin 27786 -> 27786 bytes build/openrpc/miner.json.gz | Bin 13706 -> 13706 bytes build/openrpc/worker.json.gz | Bin 4610 -> 4610 bytes build/version.go | 2 +- documentation/en/cli-lotus-miner.md | 2 +- documentation/en/cli-lotus-worker.md | 2 +- documentation/en/cli-lotus.md | 2 +- 8 files changed, 15 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7225afd86..2e0e6316c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,17 @@ # Lotus changelog -# 1.16.0 / 2022-06-24 +# 1.16.1 / 2022-07-07 +This is an OPTIONAL PATCH releases for storage providers who have failed to publish `SubmitWindowedPoSt` due to out of gas error. The error log looks like `/wdpost_run.go:xxx estimating gas {"error": "estimating gas used: message execution failed: exit SysErrOutOfGas(7)...`. + +## New Features + +- feat: declare fault recovery: Config for maximum partition count per message (#8988 / #8986) + - configure `MaxPartitionsPerRecoveryMessage` in miner configuration setting. +- feat: wdpost: Config for maximum partition count per message (#8982 / #8986) + - configure `MaxPartitionsPerPoStMessage` in miner configuration setting. + +# 1.16.0 / 2022-06-24 This is a MANDATORY release of Lotus that introduces [Filecoin network v16, codenamed the Skyr upgrade](https://github.com/filecoin-project/community/discussions/74?sort=new#discussioncomment-2392151). diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index daa38a92349fea71fbbfbfb79f9269724be761d7..e033641eda82d861012e3b89ad51d0e1808d2e75 100644 GIT binary patch delta 23 fcmeCW$=G$1aY7d(@5b)$*&LS)W_Q@;urdGud?E>I delta 23 fcmeCW$=G$1aY7d(-^T9m*&GEQpM0>*VPyaSf+Y%j diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index 3a260231aa419bcdf92508a9fac77f681d025741..dbe35cd4ae292e68a2ea2931f364338a83b9975e 100644 GIT binary patch delta 13685 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%0Dn*OGDPBE>5q+w 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 delta 13685 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 TJpKOw00960tkNbl$EyMW4}k~% diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index adf31802bbc087e1fe1539af2ef93eefca86f0be..cb632c8a715abc90d0ec68b4ddbed0166cc97e08 100644 GIT binary patch delta 4520 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 G4*>v8*Xc6= delta 4520 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 G4*>vxp9Ycu diff --git a/build/version.go b/build/version.go index e59313127..6326fe38b 100644 --- a/build/version.go +++ b/build/version.go @@ -37,7 +37,7 @@ func BuildTypeString() string { } // BuildVersion is the local build version -const BuildVersion = "1.16.0" +const BuildVersion = "1.16.1" func UserVersion() string { if os.Getenv("LOTUS_VERSION_IGNORE_COMMIT") == "1" { diff --git a/documentation/en/cli-lotus-miner.md b/documentation/en/cli-lotus-miner.md index 726bf7561..879ac82e6 100644 --- a/documentation/en/cli-lotus-miner.md +++ b/documentation/en/cli-lotus-miner.md @@ -7,7 +7,7 @@ USAGE: lotus-miner [global options] command [command options] [arguments...] VERSION: - 1.16.0 + 1.16.1 COMMANDS: init Initialize a lotus miner repo diff --git a/documentation/en/cli-lotus-worker.md b/documentation/en/cli-lotus-worker.md index d2f616655..8d2df0eb6 100644 --- a/documentation/en/cli-lotus-worker.md +++ b/documentation/en/cli-lotus-worker.md @@ -7,7 +7,7 @@ USAGE: lotus-worker [global options] command [command options] [arguments...] VERSION: - 1.16.0 + 1.16.1 COMMANDS: run Start lotus worker diff --git a/documentation/en/cli-lotus.md b/documentation/en/cli-lotus.md index d1a16ce18..527f44438 100644 --- a/documentation/en/cli-lotus.md +++ b/documentation/en/cli-lotus.md @@ -7,7 +7,7 @@ USAGE: lotus [global options] command [command options] [arguments...] VERSION: - 1.16.0 + 1.16.1 COMMANDS: daemon Start a lotus daemon process