From a60a8c7f8a56ad7541125827e631228fa2978b22 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Tue, 25 Jun 2024 20:47:43 +1000 Subject: [PATCH] chore: test: refactor, fix and improve unmanaged miner (#12143) from niporep work, including some necessary bits from there but are useful in general --- itests/kit/node_unmanaged.go | 523 ++++++++++++++++++------------- itests/manual_onboarding_test.go | 22 +- 2 files changed, 323 insertions(+), 222 deletions(-) diff --git a/itests/kit/node_unmanaged.go b/itests/kit/node_unmanaged.go index 338d3e3d91a..2cab930713d 100644 --- a/itests/kit/node_unmanaged.go +++ b/itests/kit/node_unmanaged.go @@ -6,6 +6,7 @@ import ( "crypto/rand" "fmt" "io" + "math" "os" "path/filepath" "testing" @@ -224,82 +225,6 @@ func (tm *TestUnmanagedMiner) makeAndSaveCCSector(_ context.Context, sectorNumbe tm.cacheDirPaths[sectorNumber] = cacheDirPath } -func (tm *TestUnmanagedMiner) OnboardSectorWithPieces(ctx context.Context, proofType abi.RegisteredSealProof) (abi.SectorNumber, chan WindowPostResp, - context.CancelFunc) { - req := require.New(tm.t) - sectorNumber := tm.currentSectorNum - tm.currentSectorNum++ - - // Step 1: Wait for the pre-commitseal randomness to be available (we can only draw seal randomness from tipsets that have already achieved finality) - preCommitSealRand := tm.waitPreCommitSealRandomness(ctx, sectorNumber) - - // Step 2: Build a sector with non 0 Pieces that we want to onboard - var pieces []abi.PieceInfo - if !tm.mockProofs { - pieces = tm.mkAndSavePiecesToOnboard(ctx, sectorNumber, proofType) - } else { - pieces = []abi.PieceInfo{{ - Size: abi.PaddedPieceSize(tm.options.sectorSize), - PieceCID: cid.MustParse("baga6ea4seaqjtovkwk4myyzj56eztkh5pzsk5upksan6f5outesy62bsvl4dsha"), - }} - } - - // Step 3: Generate a Pre-Commit for the CC sector -> this persists the proof on the `TestUnmanagedMiner` Miner State - if !tm.mockProofs { - tm.generatePreCommit(ctx, sectorNumber, preCommitSealRand, proofType, pieces) - } else { - tm.sealedCids[sectorNumber] = cid.MustParse("bagboea4b5abcatlxechwbp7kjpjguna6r6q7ejrhe6mdp3lf34pmswn27pkkiekz") - tm.unsealedCids[sectorNumber] = cid.MustParse("baga6ea4seaqjtovkwk4myyzj56eztkh5pzsk5upksan6f5outesy62bsvl4dsha") - } - - // Step 4 : Submit the Pre-Commit to the network - unsealedCid := tm.unsealedCids[sectorNumber] - r, err := tm.submitMessage(ctx, &miner14.PreCommitSectorBatchParams2{ - Sectors: []miner14.SectorPreCommitInfo{{ - Expiration: 2880 * 300, - SectorNumber: sectorNumber, - SealProof: TestSpt, - SealedCID: tm.sealedCids[sectorNumber], - SealRandEpoch: preCommitSealRand, - UnsealedCid: &unsealedCid, - }}, - }, 1, builtin.MethodsMiner.PreCommitSectorBatch2) - req.NoError(err) - req.True(r.Receipt.ExitCode.IsSuccess()) - - // Step 5: Generate a ProveCommit for the CC sector - waitSeedRandomness := tm.proveCommitWaitSeed(ctx, sectorNumber) - - proveCommit := []byte{0xde, 0xad, 0xbe, 0xef} // mock prove commit - if !tm.mockProofs { - proveCommit = tm.generateProveCommit(ctx, sectorNumber, proofType, waitSeedRandomness, pieces) - } - - // Step 6: Submit the ProveCommit to the network - tm.t.Log("Submitting ProveCommitSector ...") - - var manifest []miner14.PieceActivationManifest - for _, piece := range pieces { - manifest = append(manifest, miner14.PieceActivationManifest{ - CID: piece.PieceCID, - Size: piece.Size, - }) - } - - r, err = tm.submitMessage(ctx, &miner14.ProveCommitSectors3Params{ - SectorActivations: []miner14.SectorActivationManifest{{SectorNumber: sectorNumber, Pieces: manifest}}, - SectorProofs: [][]byte{proveCommit}, - RequireActivationSuccess: true, - }, 1, builtin.MethodsMiner.ProveCommitSectors3) - req.NoError(err) - req.True(r.Receipt.ExitCode.IsSuccess()) - - tm.proofType[sectorNumber] = proofType - - respCh, cancelFn := tm.wdPostLoop(ctx, sectorNumber, tm.sealedCids[sectorNumber], tm.sealedSectorPaths[sectorNumber], tm.cacheDirPaths[sectorNumber]) - return sectorNumber, respCh, cancelFn -} - func (tm *TestUnmanagedMiner) mkStagedFileWithPieces(pt abi.RegisteredSealProof) ([]abi.PieceInfo, string) { paddedPieceSize := abi.PaddedPieceSize(tm.options.sectorSize) unpaddedPieceSize := paddedPieceSize.Unpadded() @@ -340,39 +265,51 @@ func (tm *TestUnmanagedMiner) mkStagedFileWithPieces(pt abi.RegisteredSealProof) return publicPieces, unsealedSectorFile.Name() } -func (tm *TestUnmanagedMiner) SnapDeal(ctx context.Context, proofType abi.RegisteredSealProof, sectorNumber abi.SectorNumber) { - if tm.mockProofs { - tm.t.Fatal("snap deal with mock proofs currently not supported") - } - - // generate sector key - pieces, unsealedPath := tm.mkStagedFileWithPieces(proofType) +func (tm *TestUnmanagedMiner) SnapDeal(ctx context.Context, proofType abi.RegisteredSealProof, sectorNumber abi.SectorNumber) []abi.PieceInfo { updateProofType := abi.SealProofInfos[proofType].UpdateProof + var pieces []abi.PieceInfo + var snapProof []byte + var newSealedCid cid.Cid - s, err := os.Stat(tm.sealedSectorPaths[sectorNumber]) - require.NoError(tm.t, err) + if !tm.mockProofs { + // generate sector key + var unsealedPath string + pieces, unsealedPath = tm.mkStagedFileWithPieces(proofType) - randomBytes := make([]byte, s.Size()) - _, err = io.ReadFull(rand.Reader, randomBytes) - require.NoError(tm.t, err) + s, err := os.Stat(tm.sealedSectorPaths[sectorNumber]) + require.NoError(tm.t, err) - updatePath := requireTempFile(tm.t, bytes.NewReader(randomBytes), uint64(s.Size())) - require.NoError(tm.t, updatePath.Close()) - updateDir := filepath.Join(tm.t.TempDir(), fmt.Sprintf("update-%d", sectorNumber)) - require.NoError(tm.t, os.MkdirAll(updateDir, 0700)) + randomBytes := make([]byte, s.Size()) + _, err = io.ReadFull(rand.Reader, randomBytes) + require.NoError(tm.t, err) - newSealed, newUnsealed, err := ffi.SectorUpdate.EncodeInto(updateProofType, updatePath.Name(), updateDir, - tm.sealedSectorPaths[sectorNumber], tm.cacheDirPaths[sectorNumber], unsealedPath, pieces) - require.NoError(tm.t, err) + updatePath := requireTempFile(tm.t, bytes.NewReader(randomBytes), uint64(s.Size())) + require.NoError(tm.t, updatePath.Close()) + updateDir := filepath.Join(tm.t.TempDir(), fmt.Sprintf("update-%d", sectorNumber)) + require.NoError(tm.t, os.MkdirAll(updateDir, 0700)) - vp, err := ffi.SectorUpdate.GenerateUpdateVanillaProofs(updateProofType, tm.sealedCids[sectorNumber], - newSealed, newUnsealed, updatePath.Name(), updateDir, tm.sealedSectorPaths[sectorNumber], - tm.cacheDirPaths[sectorNumber]) - require.NoError(tm.t, err) + var newUnsealedCid cid.Cid + newSealedCid, newUnsealedCid, err = ffi.SectorUpdate.EncodeInto(updateProofType, updatePath.Name(), updateDir, + tm.sealedSectorPaths[sectorNumber], tm.cacheDirPaths[sectorNumber], unsealedPath, pieces) + require.NoError(tm.t, err) + + vp, err := ffi.SectorUpdate.GenerateUpdateVanillaProofs(updateProofType, tm.sealedCids[sectorNumber], + newSealedCid, newUnsealedCid, updatePath.Name(), updateDir, tm.sealedSectorPaths[sectorNumber], + tm.cacheDirPaths[sectorNumber]) + require.NoError(tm.t, err) + + snapProof, err = ffi.SectorUpdate.GenerateUpdateProofWithVanilla(updateProofType, tm.sealedCids[sectorNumber], + newSealedCid, newUnsealedCid, vp) + require.NoError(tm.t, err) + } else { + pieces = []abi.PieceInfo{{ + Size: abi.PaddedPieceSize(tm.options.sectorSize), + PieceCID: cid.MustParse("baga6ea4seaqlhznlutptgfwhffupyer6txswamerq5fc2jlwf2lys2mm5jtiaeq"), + }} + snapProof = []byte{0xde, 0xad, 0xbe, 0xef} + newSealedCid = cid.MustParse("bagboea4b5abcatlxechwbp7kjpjguna6r6q7ejrhe6mdp3lf34pmswn27pkkieka") + } - snapProof, err := ffi.SectorUpdate.GenerateUpdateProofWithVanilla(updateProofType, tm.sealedCids[sectorNumber], - newSealed, newUnsealed, vp) - require.NoError(tm.t, err) tm.waitForMutableDeadline(ctx, sectorNumber) // submit proof @@ -396,7 +333,7 @@ func (tm *TestUnmanagedMiner) SnapDeal(ctx context.Context, proofType abi.Regist Sector: sectorNumber, Deadline: sl.Deadline, Partition: sl.Partition, - NewSealedCID: newSealed, + NewSealedCID: newSealedCid, Pieces: manifest, }, }, @@ -405,9 +342,11 @@ func (tm *TestUnmanagedMiner) SnapDeal(ctx context.Context, proofType abi.Regist RequireActivationSuccess: true, RequireNotificationSuccess: false, } - r, err := tm.submitMessage(ctx, params, 1, builtin.MethodsMiner.ProveReplicaUpdates3) + r, err := tm.SubmitMessage(ctx, params, 1, builtin.MethodsMiner.ProveReplicaUpdates3) require.NoError(tm.t, err) require.True(tm.t, r.Receipt.ExitCode.IsSuccess()) + + return pieces } func (tm *TestUnmanagedMiner) waitForMutableDeadline(ctx context.Context, sectorNum abi.SectorNumber) { @@ -440,66 +379,228 @@ func (tm *TestUnmanagedMiner) waitForMutableDeadline(ctx context.Context, sector } } -func (tm *TestUnmanagedMiner) OnboardCCSector(ctx context.Context, proofType abi.RegisteredSealProof) (abi.SectorNumber, chan WindowPostResp, - context.CancelFunc) { - req := require.New(tm.t) +func (tm *TestUnmanagedMiner) NextSectorNumber() abi.SectorNumber { sectorNumber := tm.currentSectorNum tm.currentSectorNum++ + return sectorNumber +} + +func (tm *TestUnmanagedMiner) PrepareSectorForProveCommit( + ctx context.Context, + proofType abi.RegisteredSealProof, + sectorNumber abi.SectorNumber, + pieces []abi.PieceInfo, +) (seedEpoch abi.ChainEpoch, proveCommit []byte) { + + req := require.New(tm.t) + + // Wait for the pre-commitseal randomness to be available (we can only draw seal randomness from tipsets that have already achieved finality) + preCommitSealRandEpoch := tm.waitPreCommitSealRandomness(ctx, sectorNumber, proofType) + + // Generate a Pre-Commit for the CC sector -> this persists the proof on the `TestUnmanagedMiner` Miner State + tm.generatePreCommit(ctx, sectorNumber, preCommitSealRandEpoch, proofType, pieces) // --------------------Create pre-commit for the CC sector -> we'll just pre-commit `sector size` worth of 0s for this CC sector - // Step 1: Wait for the pre-commitseal randomness to be available (we can only draw seal randomness from tipsets that have already achieved finality) - preCommitSealRand := tm.waitPreCommitSealRandomness(ctx, sectorNumber) + if !proofType.IsNonInteractive() { + // Submit the Pre-Commit to the network + var uc *cid.Cid + if len(pieces) > 0 { + unsealedCid := tm.unsealedCids[sectorNumber] + uc = &unsealedCid + } + r, err := tm.SubmitMessage(ctx, &miner14.PreCommitSectorBatchParams2{ + Sectors: []miner14.SectorPreCommitInfo{{ + Expiration: 2880 * 300, + SectorNumber: sectorNumber, + SealProof: proofType, + SealedCID: tm.sealedCids[sectorNumber], + SealRandEpoch: preCommitSealRandEpoch, + UnsealedCid: uc, + }}, + }, 1, builtin.MethodsMiner.PreCommitSectorBatch2) + req.NoError(err) + req.True(r.Receipt.ExitCode.IsSuccess()) + } + + // Generate a ProveCommit for the CC sector + var seedRandomness abi.InteractiveSealRandomness + seedEpoch, seedRandomness = tm.proveCommitWaitSeed(ctx, sectorNumber, proofType) + proveCommit = []byte{0xde, 0xad, 0xbe, 0xef} // mock prove commit if !tm.mockProofs { - // Step 2: Write empty bytes that we want to seal i.e. create our CC sector - tm.makeAndSaveCCSector(ctx, sectorNumber) + proveCommit = tm.generateProveCommit(ctx, sectorNumber, proofType, seedRandomness, pieces) + } + + return seedEpoch, proveCommit +} + +func (tm *TestUnmanagedMiner) SubmitProveCommit( + ctx context.Context, + proofType abi.RegisteredSealProof, + sectorNumber abi.SectorNumber, + seedEpoch abi.ChainEpoch, + proveCommit []byte, + pieceManifest []miner14.PieceActivationManifest, +) { + + req := require.New(tm.t) + + if proofType.IsNonInteractive() { + req.Nil(pieceManifest, "piece manifest should be nil for Non-interactive PoRep") + } + + // Step 6: Submit the ProveCommit to the network + if proofType.IsNonInteractive() { + tm.t.Log("Submitting ProveCommitSector ...") + + var provingDeadline uint64 = 7 + if tm.IsUmmutableDeadline(ctx, provingDeadline) { + // avoid immutable deadlines + provingDeadline = 5 + } - // Step 3: Generate a Pre-Commit for the CC sector -> this persists the proof on the `TestUnmanagedMiner` Miner State - tm.generatePreCommit(ctx, sectorNumber, preCommitSealRand, proofType, []abi.PieceInfo{}) + actorIdNum, err := address.IDFromAddress(tm.ActorAddr) + req.NoError(err) + actorId := abi.ActorID(actorIdNum) + + r, err := tm.SubmitMessage(ctx, &miner14.ProveCommitSectorsNIParams{ + Sectors: []miner14.SectorNIActivationInfo{{ + SealingNumber: sectorNumber, + SealerID: actorId, + SealedCID: tm.sealedCids[sectorNumber], + SectorNumber: sectorNumber, + SealRandEpoch: seedEpoch, + Expiration: 2880 * 300, + }}, + AggregateProof: proveCommit, + SealProofType: proofType, + AggregateProofType: abi.RegisteredAggregationProof_SnarkPackV2, + ProvingDeadline: provingDeadline, + RequireActivationSuccess: true, + }, 1, builtin.MethodsMiner.ProveCommitSectorsNI) + req.NoError(err) + req.True(r.Receipt.ExitCode.IsSuccess()) + + // NI-PoRep lets us determine the deadline, so we can check that it's set correctly + sp, err := tm.FullNode.StateSectorPartition(ctx, tm.ActorAddr, sectorNumber, r.TipSet) + req.NoError(err) + req.Equal(provingDeadline, sp.Deadline) } else { - // skip the above steps and use a mock sealed CID - tm.sealedCids[sectorNumber] = cid.MustParse("bagboea4b5abcatlxechwbp7kjpjguna6r6q7ejrhe6mdp3lf34pmswn27pkkiekz") + tm.t.Log("Submitting ProveCommitSector ...") + + r, err := tm.SubmitMessage(ctx, &miner14.ProveCommitSectors3Params{ + SectorActivations: []miner14.SectorActivationManifest{{SectorNumber: sectorNumber, Pieces: pieceManifest}}, + SectorProofs: [][]byte{proveCommit}, + RequireActivationSuccess: true, + }, 0, builtin.MethodsMiner.ProveCommitSectors3) + req.NoError(err) + req.True(r.Receipt.ExitCode.IsSuccess()) } +} - // Step 4 : Submit the Pre-Commit to the network - r, err := tm.submitMessage(ctx, &miner14.PreCommitSectorBatchParams2{ - Sectors: []miner14.SectorPreCommitInfo{{ - Expiration: 2880 * 300, - SectorNumber: sectorNumber, - SealProof: TestSpt, - SealedCID: tm.sealedCids[sectorNumber], - SealRandEpoch: preCommitSealRand, - }}, - }, 1, builtin.MethodsMiner.PreCommitSectorBatch2) - req.NoError(err) - req.True(r.Receipt.ExitCode.IsSuccess()) +func (tm *TestUnmanagedMiner) OnboardCCSector(ctx context.Context, proofType abi.RegisteredSealProof) (abi.SectorNumber, chan WindowPostResp, context.CancelFunc) { + sectorNumber := tm.NextSectorNumber() + + if !tm.mockProofs { + // Write empty bytes that we want to seal i.e. create our CC sector + tm.makeAndSaveCCSector(ctx, sectorNumber) + } + + seedEpoch, proveCommit := tm.PrepareSectorForProveCommit(ctx, proofType, sectorNumber, []abi.PieceInfo{}) + + tm.SubmitProveCommit(ctx, proofType, sectorNumber, seedEpoch, proveCommit, nil) + + tm.proofType[sectorNumber] = proofType + respCh, cancelFn := tm.wdPostLoop(ctx, sectorNumber, tm.sealedCids[sectorNumber], tm.sealedSectorPaths[sectorNumber], tm.cacheDirPaths[sectorNumber]) + + return sectorNumber, respCh, cancelFn +} - // Step 5: Generate a ProveCommit for the CC sector - waitSeedRandomness := tm.proveCommitWaitSeed(ctx, sectorNumber) +func (tm *TestUnmanagedMiner) OnboardSectorWithPieces(ctx context.Context, proofType abi.RegisteredSealProof) (abi.SectorNumber, chan WindowPostResp, context.CancelFunc) { + sectorNumber := tm.NextSectorNumber() - proveCommit := []byte{0xde, 0xad, 0xbe, 0xef} // mock prove commit + // Build a sector with non 0 Pieces that we want to onboard + var pieces []abi.PieceInfo if !tm.mockProofs { - proveCommit = tm.generateProveCommit(ctx, sectorNumber, proofType, waitSeedRandomness, []abi.PieceInfo{}) + pieces = tm.mkAndSavePiecesToOnboard(ctx, sectorNumber, proofType) + } else { + pieces = []abi.PieceInfo{{ + Size: abi.PaddedPieceSize(tm.options.sectorSize), + PieceCID: cid.MustParse("baga6ea4seaqjtovkwk4myyzj56eztkh5pzsk5upksan6f5outesy62bsvl4dsha"), + }} } - // Step 6: Submit the ProveCommit to the network + _, proveCommit := tm.PrepareSectorForProveCommit(ctx, proofType, sectorNumber, pieces) + + // Submit the ProveCommit to the network tm.t.Log("Submitting ProveCommitSector ...") - r, err = tm.submitMessage(ctx, &miner14.ProveCommitSectors3Params{ - SectorActivations: []miner14.SectorActivationManifest{{SectorNumber: sectorNumber}}, - SectorProofs: [][]byte{proveCommit}, - RequireActivationSuccess: true, - }, 0, builtin.MethodsMiner.ProveCommitSectors3) - req.NoError(err) - req.True(r.Receipt.ExitCode.IsSuccess()) + var manifest []miner14.PieceActivationManifest + for _, piece := range pieces { + manifest = append(manifest, miner14.PieceActivationManifest{ + CID: piece.PieceCID, + Size: piece.Size, + }) + } - tm.proofType[sectorNumber] = proofType + tm.SubmitProveCommit(ctx, proofType, sectorNumber, 0, proveCommit, manifest) + tm.proofType[sectorNumber] = proofType respCh, cancelFn := tm.wdPostLoop(ctx, sectorNumber, tm.sealedCids[sectorNumber], tm.sealedSectorPaths[sectorNumber], tm.cacheDirPaths[sectorNumber]) + return sectorNumber, respCh, cancelFn } +// calculateNextPostEpoch calculates the first epoch of the deadline proving window +// that is desired for the given sector for the specified miner. +// This function returns the current epoch and the calculated proving epoch. +func (tm *TestUnmanagedMiner) calculateNextPostEpoch( + ctx context.Context, + sectorNumber abi.SectorNumber, +) (abi.ChainEpoch, abi.ChainEpoch, error) { + // Retrieve the current blockchain head + head, err := tm.FullNode.ChainHead(ctx) + if err != nil { + return 0, 0, fmt.Errorf("failed to get chain head: %w", err) + } + + // Obtain the proving deadline information for the miner + di, err := tm.FullNode.StateMinerProvingDeadline(ctx, tm.ActorAddr, head.Key()) + if err != nil { + return 0, 0, fmt.Errorf("failed to get proving deadline: %w", err) + } + + tm.t.Logf("Miner %s: WindowPoST(%d): ProvingDeadline: %+v", tm.ActorAddr, sectorNumber, di) + + // Fetch the sector partition for the given sector number + sp, err := tm.FullNode.StateSectorPartition(ctx, tm.ActorAddr, sectorNumber, head.Key()) + if err != nil { + return 0, 0, fmt.Errorf("failed to get sector partition: %w", err) + } + + tm.t.Logf("Miner %s: WindowPoST(%d): SectorPartition: %+v", tm.ActorAddr, sectorNumber, sp) + + // Calculate the start of the period, adjusting if the current deadline has passed + periodStart := di.PeriodStart + // calculate current deadline index because it won't be reliable from state until the first + // challenge window cron tick after first sector onboarded + currentDeadlineIdx := uint64(math.Abs(float64((di.CurrentEpoch - di.PeriodStart) / di.WPoStChallengeWindow))) + if di.PeriodStart < di.CurrentEpoch && sp.Deadline <= currentDeadlineIdx { + // If the deadline has passed in the current proving period, calculate for the next period + // Note that di.Open may be > di.CurrentEpoch if the miner has just been enrolled in cron so + // their deadlines haven't started rolling yet + periodStart += di.WPoStProvingPeriod + } + + // Calculate the exact epoch when proving should occur + provingEpoch := periodStart + di.WPoStChallengeWindow*abi.ChainEpoch(sp.Deadline) + + tm.t.Logf("Miner %s: WindowPoST(%d): next ProvingEpoch: %d", tm.ActorAddr, sectorNumber, provingEpoch) + + return di.CurrentEpoch, provingEpoch, nil +} + func (tm *TestUnmanagedMiner) wdPostLoop( pctx context.Context, sectorNumber abi.SectorNumber, @@ -598,7 +699,7 @@ func (tm *TestUnmanagedMiner) SubmitPostDispute(ctx context.Context, sectorNumbe tm.t.Logf("Miner %s: Sector %d - Disputing WindowedPoSt to confirm validity at epoch %d", tm.ActorAddr, sectorNumber, disputeEpoch) - _, err = tm.submitMessage(ctx, &miner14.DisputeWindowedPoStParams{ + _, err = tm.SubmitMessage(ctx, &miner14.DisputeWindowedPoStParams{ Deadline: sp.Deadline, PoStIndex: 0, }, 1, builtin.MethodsMiner.DisputeWindowedPoSt) @@ -651,7 +752,7 @@ func (tm *TestUnmanagedMiner) submitWindowPost(ctx context.Context, sectorNumber return fmt.Errorf("Miner(%s): failed to get miner info for sector %d: %w", tm.ActorAddr, sectorNumber, err) } - r, err := tm.submitMessage(ctx, &miner14.SubmitWindowedPoStParams{ + r, err := tm.SubmitMessage(ctx, &miner14.SubmitWindowedPoStParams{ ChainCommitEpoch: chainRandomnessEpoch, ChainCommitRand: chainRandomness, Deadline: sp.Deadline, @@ -754,12 +855,16 @@ func (tm *TestUnmanagedMiner) generateWindowPost( return proofBytes, nil } -func (tm *TestUnmanagedMiner) waitPreCommitSealRandomness(ctx context.Context, sectorNumber abi.SectorNumber) abi.ChainEpoch { +func (tm *TestUnmanagedMiner) waitPreCommitSealRandomness(ctx context.Context, sectorNumber abi.SectorNumber, proofType abi.RegisteredSealProof) abi.ChainEpoch { // We want to draw seal randomness from a tipset that has already achieved finality as PreCommits are expensive to re-generate. // Check if we already have an epoch that is already final and wait for such an epoch if we don't have one. head, err := tm.FullNode.ChainHead(ctx) require.NoError(tm.t, err) + if proofType.IsNonInteractive() { + return head.Height() - 1 // no need to wait + } + var sealRandEpoch abi.ChainEpoch if head.Height() > policy.SealRandomnessLookback { sealRandEpoch = head.Height() - policy.SealRandomnessLookback @@ -775,53 +880,6 @@ func (tm *TestUnmanagedMiner) waitPreCommitSealRandomness(ctx context.Context, s return sealRandEpoch } -// calculateNextPostEpoch calculates the first epoch of the deadline proving window -// that is desired for the given sector for the specified miner. -// This function returns the current epoch and the calculated proving epoch. -func (tm *TestUnmanagedMiner) calculateNextPostEpoch( - ctx context.Context, - sectorNumber abi.SectorNumber, -) (abi.ChainEpoch, abi.ChainEpoch, error) { - // Retrieve the current blockchain head - head, err := tm.FullNode.ChainHead(ctx) - if err != nil { - return 0, 0, fmt.Errorf("failed to get chain head: %w", err) - } - - // Obtain the proving deadline information for the miner - di, err := tm.FullNode.StateMinerProvingDeadline(ctx, tm.ActorAddr, head.Key()) - if err != nil { - return 0, 0, fmt.Errorf("failed to get proving deadline: %w", err) - } - - tm.t.Logf("Miner %s: WindowPoST(%d): ProvingDeadline: %+v", tm.ActorAddr, sectorNumber, di) - - // Fetch the sector partition for the given sector number - sp, err := tm.FullNode.StateSectorPartition(ctx, tm.ActorAddr, sectorNumber, head.Key()) - if err != nil { - return 0, 0, fmt.Errorf("failed to get sector partition: %w", err) - } - - tm.t.Logf("Miner %s: WindowPoST(%d): SectorPartition: %+v", tm.ActorAddr, sectorNumber, sp) - - // Calculate the start of the period, adjusting if the current deadline has passed - periodStart := di.PeriodStart - // calculate current deadline index because it won't be reliable from state until the first - // challenge window cron tick after first sector onboarded - curIdx := (di.CurrentEpoch - di.PeriodStart) / di.WPoStChallengeWindow - if di.PeriodStart < di.CurrentEpoch && sp.Deadline <= uint64(curIdx) { - // If the deadline has passed in the current proving period, calculate for the next period - periodStart += di.WPoStProvingPeriod - } - - // Calculate the exact epoch when proving should occur - provingEpoch := periodStart + di.WPoStChallengeWindow*abi.ChainEpoch(sp.Deadline) - - tm.t.Logf("Miner %s: WindowPoST(%d): next ProvingEpoch: %d", tm.ActorAddr, sectorNumber, provingEpoch) - - return di.CurrentEpoch, provingEpoch, nil -} - func (tm *TestUnmanagedMiner) generatePreCommit( ctx context.Context, sectorNumber abi.SectorNumber, @@ -829,6 +887,15 @@ func (tm *TestUnmanagedMiner) generatePreCommit( proofType abi.RegisteredSealProof, pieceInfo []abi.PieceInfo, ) { + + if tm.mockProofs { + tm.sealedCids[sectorNumber] = cid.MustParse("bagboea4b5abcatlxechwbp7kjpjguna6r6q7ejrhe6mdp3lf34pmswn27pkkiekz") + if len(pieceInfo) > 0 { + tm.unsealedCids[sectorNumber] = cid.MustParse("baga6ea4seaqjtovkwk4myyzj56eztkh5pzsk5upksan6f5outesy62bsvl4dsha") + } + return + } + req := require.New(tm.t) tm.t.Logf("Miner %s: Generating proof type %d PreCommit for sector %d...", tm.ActorAddr, proofType, sectorNumber) @@ -878,41 +945,48 @@ func (tm *TestUnmanagedMiner) generatePreCommit( tm.unsealedCids[sectorNumber] = unsealedCid } -func (tm *TestUnmanagedMiner) proveCommitWaitSeed(ctx context.Context, sectorNumber abi.SectorNumber) abi.InteractiveSealRandomness { +func (tm *TestUnmanagedMiner) proveCommitWaitSeed(ctx context.Context, sectorNumber abi.SectorNumber, proofType abi.RegisteredSealProof) (abi.ChainEpoch, abi.InteractiveSealRandomness) { req := require.New(tm.t) head, err := tm.FullNode.ChainHead(ctx) req.NoError(err) - tm.t.Logf("Miner %s: Fetching pre-commit info for sector %d...", tm.ActorAddr, sectorNumber) - preCommitInfo, err := tm.FullNode.StateSectorPreCommitInfo(ctx, tm.ActorAddr, sectorNumber, head.Key()) - req.NoError(err) - seedRandomnessHeight := preCommitInfo.PreCommitEpoch + policy.GetPreCommitChallengeDelay() + var seedRandomnessHeight abi.ChainEpoch + + if proofType.IsNonInteractive() { + seedRandomnessHeight = head.Height() - 1 // no need to wait, it just can't be current epoch + } else { + tm.t.Logf("Miner %s: Fetching pre-commit info for sector %d...", tm.ActorAddr, sectorNumber) + preCommitInfo, err := tm.FullNode.StateSectorPreCommitInfo(ctx, tm.ActorAddr, sectorNumber, head.Key()) + req.NoError(err) + seedRandomnessHeight = preCommitInfo.PreCommitEpoch + policy.GetPreCommitChallengeDelay() + + tm.t.Logf("Miner %s: Waiting %d epochs for seed randomness at epoch %d (current epoch %d) for sector %d...", tm.ActorAddr, seedRandomnessHeight-head.Height(), seedRandomnessHeight, head.Height(), sectorNumber) + tm.FullNode.WaitTillChain(ctx, HeightAtLeast(seedRandomnessHeight+5)) - tm.t.Logf("Miner %s: Waiting %d epochs for seed randomness at epoch %d (current epoch %d) for sector %d...", tm.ActorAddr, seedRandomnessHeight-head.Height(), seedRandomnessHeight, head.Height(), sectorNumber) - tm.FullNode.WaitTillChain(ctx, HeightAtLeast(seedRandomnessHeight+5)) + head, err = tm.FullNode.ChainHead(ctx) + req.NoError(err) + } minerAddrBytes := new(bytes.Buffer) req.NoError(tm.ActorAddr.MarshalCBOR(minerAddrBytes)) - head, err = tm.FullNode.ChainHead(ctx) - req.NoError(err) - tm.t.Logf("Miner %s: Fetching seed randomness for sector %d...", tm.ActorAddr, sectorNumber) rand, err := tm.FullNode.StateGetRandomnessFromBeacon(ctx, crypto.DomainSeparationTag_InteractiveSealChallengeSeed, seedRandomnessHeight, minerAddrBytes.Bytes(), head.Key()) req.NoError(err) seedRandomness := abi.InteractiveSealRandomness(rand) tm.t.Logf("Miner %s: Obtained seed randomness for sector %d: %x", tm.ActorAddr, sectorNumber, seedRandomness) - return seedRandomness + return seedRandomnessHeight, seedRandomness } func (tm *TestUnmanagedMiner) generateProveCommit( - ctx context.Context, + _ context.Context, sectorNumber abi.SectorNumber, proofType abi.RegisteredSealProof, seedRandomness abi.InteractiveSealRandomness, pieces []abi.PieceInfo, ) []byte { + tm.t.Logf("Miner %s: Generating proof type %d Sector Proof for sector %d...", tm.ActorAddr, proofType, sectorNumber) req := require.New(tm.t) @@ -938,20 +1012,43 @@ func (tm *TestUnmanagedMiner) generateProveCommit( tm.t.Logf("Miner %s: Running proof type %d SealCommitPhase2 for sector %d...", tm.ActorAddr, proofType, sectorNumber) - sectorProof, err := ffi.SealCommitPhase2(scp1, sectorNumber, actorId) - req.NoError(err) + var sectorProof []byte + + if proofType.IsNonInteractive() { + circuitProofs, err := ffi.SealCommitPhase2CircuitProofs(scp1, sectorNumber) + req.NoError(err) + asvpai := proof.AggregateSealVerifyProofAndInfos{ + Miner: actorId, + SealProof: proofType, + AggregateProof: abi.RegisteredAggregationProof_SnarkPackV2, + Infos: []proof.AggregateSealVerifyInfo{{ + Number: sectorNumber, + Randomness: tm.sealTickets[sectorNumber], + InteractiveRandomness: make([]byte, 32), + SealedCID: tm.sealedCids[sectorNumber], + UnsealedCID: tm.unsealedCids[sectorNumber], + }}, + } + tm.t.Logf("Miner %s: Aggregating circuit proofs for sector %d: %+v", tm.ActorAddr, sectorNumber, asvpai) + sectorProof, err = ffi.AggregateSealProofs(asvpai, [][]byte{circuitProofs}) + req.NoError(err) + } else { + sectorProof, err = ffi.SealCommitPhase2(scp1, sectorNumber, actorId) + req.NoError(err) + } tm.t.Logf("Miner %s: Got proof type %d sector proof of length %d for sector %d", tm.ActorAddr, proofType, len(sectorProof), sectorNumber) return sectorProof } -func (tm *TestUnmanagedMiner) submitMessage( +func (tm *TestUnmanagedMiner) SubmitMessage( ctx context.Context, params cbg.CBORMarshaler, value uint64, method abi.MethodNum, ) (*api.MsgLookup, error) { + enc, aerr := actors.SerializeParams(params) if aerr != nil { return nil, aerr @@ -1034,14 +1131,10 @@ func (tm *TestUnmanagedMiner) WaitTillActivatedAndAssertPower( // Miner B should now have power tm.AssertPower(ctx, uint64(tm.options.sectorSize), uint64(tm.options.sectorSize)) - if tm.mockProofs { - // WindowPost Dispute should succeed as we are using mock proofs - err := tm.SubmitPostDispute(ctx, sector) - require.NoError(tm.t, err) - } else { + if !tm.mockProofs { // WindowPost Dispute should fail tm.AssertDisputeFails(ctx, sector) - } + } // else it would pass, which we don't want } func (tm *TestUnmanagedMiner) AssertDisputeFails(ctx context.Context, sector abi.SectorNumber) { @@ -1050,3 +1143,11 @@ func (tm *TestUnmanagedMiner) AssertDisputeFails(ctx context.Context, sector abi require.Contains(tm.t, err.Error(), "failed to dispute valid post") require.Contains(tm.t, err.Error(), "(RetCode=16)") } + +func (tm *TestUnmanagedMiner) IsUmmutableDeadline(ctx context.Context, deadlineIndex uint64) bool { + di, err := tm.FullNode.StateMinerProvingDeadline(ctx, tm.ActorAddr, types.EmptyTSK) + require.NoError(tm.t, err) + // don't rely on di.Index because if we haven't enrolled in cron it won't be ticking + currentDeadline := uint64((di.CurrentEpoch - di.PeriodStart) / di.WPoStChallengeWindow) + return currentDeadline == deadlineIndex || currentDeadline == deadlineIndex-1 +} diff --git a/itests/manual_onboarding_test.go b/itests/manual_onboarding_test.go index 08e4119acf1..4e976b030b3 100644 --- a/itests/manual_onboarding_test.go +++ b/itests/manual_onboarding_test.go @@ -8,24 +8,28 @@ import ( "github.com/stretchr/testify/require" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/itests/kit" ) // Manually onboard CC sectors, bypassing lotus-miner onboarding pathways func TestManualSectorOnboarding(t *testing.T) { - const defaultSectorSize = abi.SectorSize(2 << 10) // 2KiB - req := require.New(t) + const defaultSectorSize = abi.SectorSize(2 << 10) // 2KiB + sealProofType, err := miner.SealProofTypeFromSectorSize(defaultSectorSize, network.Version23, miner.SealProofVariant_Standard) + req.NoError(err) + for _, withMockProofs := range []bool{true, false} { testName := "WithRealProofs" if withMockProofs { testName = "WithMockProofs" } t.Run(testName, func(t *testing.T) { - if testName == "WithRealProofs" { + if !withMockProofs { kit.Expensive(t) } kit.QuietMiningLogs() @@ -85,7 +89,7 @@ func TestManualSectorOnboarding(t *testing.T) { var bRespCh chan kit.WindowPostResp var bWdPostCancelF context.CancelFunc - bSectorNum, bRespCh, bWdPostCancelF = minerB.OnboardCCSector(ctx, kit.TestSpt) + bSectorNum, bRespCh, bWdPostCancelF = minerB.OnboardCCSector(ctx, sealProofType) // Miner B should still not have power as power can only be gained after sector is activated i.e. the first WindowPost is submitted for it minerB.AssertNoPower(ctx) // Ensure that the block miner checks for and waits for posts during the appropriate proving window from our new miner with a sector @@ -106,13 +110,9 @@ func TestManualSectorOnboarding(t *testing.T) { minerC.WaitTillActivatedAndAssertPower(ctx, cRespCh, cSectorNum) // Miner B has activated the CC sector -> upgrade it with snapdeals - // Note: We can't activate a sector with mock proofs as the WdPost is successfully disputed and so no point - // in snapping it as snapping is only for activated sectors - if !withMockProofs { - minerB.SnapDeal(ctx, kit.TestSpt, bSectorNum) - // cancel the WdPost for the CC sector as the corresponding CommR is no longer valid - bWdPostCancelF() - } + _ = minerB.SnapDeal(ctx, kit.TestSpt, bSectorNum) + // cancel the WdPost for the CC sector as the corresponding CommR is no longer valid + bWdPostCancelF() }) } }