chore: test: refactor, fix and improve unmanaged miner (#12143)

from niporep work, including some necessary bits from there but are useful in
general
This commit is contained in:
Rod Vagg 2024-06-25 20:47:43 +10:00 committed by GitHub
parent 40651abc0a
commit a60a8c7f8a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 333 additions and 232 deletions

View File

@ -6,6 +6,7 @@ import (
"crypto/rand" "crypto/rand"
"fmt" "fmt"
"io" "io"
"math"
"os" "os"
"path/filepath" "path/filepath"
"testing" "testing"
@ -224,82 +225,6 @@ func (tm *TestUnmanagedMiner) makeAndSaveCCSector(_ context.Context, sectorNumbe
tm.cacheDirPaths[sectorNumber] = cacheDirPath 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) { func (tm *TestUnmanagedMiner) mkStagedFileWithPieces(pt abi.RegisteredSealProof) ([]abi.PieceInfo, string) {
paddedPieceSize := abi.PaddedPieceSize(tm.options.sectorSize) paddedPieceSize := abi.PaddedPieceSize(tm.options.sectorSize)
unpaddedPieceSize := paddedPieceSize.Unpadded() unpaddedPieceSize := paddedPieceSize.Unpadded()
@ -340,39 +265,51 @@ func (tm *TestUnmanagedMiner) mkStagedFileWithPieces(pt abi.RegisteredSealProof)
return publicPieces, unsealedSectorFile.Name() return publicPieces, unsealedSectorFile.Name()
} }
func (tm *TestUnmanagedMiner) SnapDeal(ctx context.Context, proofType abi.RegisteredSealProof, sectorNumber abi.SectorNumber) { func (tm *TestUnmanagedMiner) SnapDeal(ctx context.Context, proofType abi.RegisteredSealProof, sectorNumber abi.SectorNumber) []abi.PieceInfo {
if tm.mockProofs { updateProofType := abi.SealProofInfos[proofType].UpdateProof
tm.t.Fatal("snap deal with mock proofs currently not supported") var pieces []abi.PieceInfo
var snapProof []byte
var newSealedCid cid.Cid
if !tm.mockProofs {
// generate sector key
var unsealedPath string
pieces, unsealedPath = tm.mkStagedFileWithPieces(proofType)
s, err := os.Stat(tm.sealedSectorPaths[sectorNumber])
require.NoError(tm.t, err)
randomBytes := make([]byte, s.Size())
_, err = io.ReadFull(rand.Reader, randomBytes)
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))
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")
} }
// generate sector key
pieces, unsealedPath := tm.mkStagedFileWithPieces(proofType)
updateProofType := abi.SealProofInfos[proofType].UpdateProof
s, err := os.Stat(tm.sealedSectorPaths[sectorNumber])
require.NoError(tm.t, err)
randomBytes := make([]byte, s.Size())
_, err = io.ReadFull(rand.Reader, randomBytes)
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))
newSealed, newUnsealed, 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],
newSealed, newUnsealed, updatePath.Name(), updateDir, tm.sealedSectorPaths[sectorNumber],
tm.cacheDirPaths[sectorNumber])
require.NoError(tm.t, err)
snapProof, err := ffi.SectorUpdate.GenerateUpdateProofWithVanilla(updateProofType, tm.sealedCids[sectorNumber],
newSealed, newUnsealed, vp)
require.NoError(tm.t, err)
tm.waitForMutableDeadline(ctx, sectorNumber) tm.waitForMutableDeadline(ctx, sectorNumber)
// submit proof // submit proof
@ -396,7 +333,7 @@ func (tm *TestUnmanagedMiner) SnapDeal(ctx context.Context, proofType abi.Regist
Sector: sectorNumber, Sector: sectorNumber,
Deadline: sl.Deadline, Deadline: sl.Deadline,
Partition: sl.Partition, Partition: sl.Partition,
NewSealedCID: newSealed, NewSealedCID: newSealedCid,
Pieces: manifest, Pieces: manifest,
}, },
}, },
@ -405,9 +342,11 @@ func (tm *TestUnmanagedMiner) SnapDeal(ctx context.Context, proofType abi.Regist
RequireActivationSuccess: true, RequireActivationSuccess: true,
RequireNotificationSuccess: false, 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.NoError(tm.t, err)
require.True(tm.t, r.Receipt.ExitCode.IsSuccess()) require.True(tm.t, r.Receipt.ExitCode.IsSuccess())
return pieces
} }
func (tm *TestUnmanagedMiner) waitForMutableDeadline(ctx context.Context, sectorNum abi.SectorNumber) { 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, func (tm *TestUnmanagedMiner) NextSectorNumber() abi.SectorNumber {
context.CancelFunc) {
req := require.New(tm.t)
sectorNumber := tm.currentSectorNum sectorNumber := tm.currentSectorNum
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 // --------------------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) if !proofType.IsNonInteractive() {
preCommitSealRand := tm.waitPreCommitSealRandomness(ctx, sectorNumber) // Submit the Pre-Commit to the network
var uc *cid.Cid
if !tm.mockProofs { if len(pieces) > 0 {
// Step 2: Write empty bytes that we want to seal i.e. create our CC sector unsealedCid := tm.unsealedCids[sectorNumber]
tm.makeAndSaveCCSector(ctx, sectorNumber) uc = &unsealedCid
}
// Step 3: Generate a Pre-Commit for the CC sector -> this persists the proof on the `TestUnmanagedMiner` Miner State r, err := tm.SubmitMessage(ctx, &miner14.PreCommitSectorBatchParams2{
tm.generatePreCommit(ctx, sectorNumber, preCommitSealRand, proofType, []abi.PieceInfo{}) Sectors: []miner14.SectorPreCommitInfo{{
} else { Expiration: 2880 * 300,
// skip the above steps and use a mock sealed CID SectorNumber: sectorNumber,
tm.sealedCids[sectorNumber] = cid.MustParse("bagboea4b5abcatlxechwbp7kjpjguna6r6q7ejrhe6mdp3lf34pmswn27pkkiekz") SealProof: proofType,
SealedCID: tm.sealedCids[sectorNumber],
SealRandEpoch: preCommitSealRandEpoch,
UnsealedCid: uc,
}},
}, 1, builtin.MethodsMiner.PreCommitSectorBatch2)
req.NoError(err)
req.True(r.Receipt.ExitCode.IsSuccess())
} }
// Step 4 : Submit the Pre-Commit to the network // Generate a ProveCommit for the CC sector
r, err := tm.submitMessage(ctx, &miner14.PreCommitSectorBatchParams2{ var seedRandomness abi.InteractiveSealRandomness
Sectors: []miner14.SectorPreCommitInfo{{ seedEpoch, seedRandomness = tm.proveCommitWaitSeed(ctx, sectorNumber, proofType)
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())
// Step 5: Generate a ProveCommit for the CC sector proveCommit = []byte{0xde, 0xad, 0xbe, 0xef} // mock prove commit
waitSeedRandomness := tm.proveCommitWaitSeed(ctx, sectorNumber)
proveCommit := []byte{0xde, 0xad, 0xbe, 0xef} // mock prove commit
if !tm.mockProofs { if !tm.mockProofs {
proveCommit = tm.generateProveCommit(ctx, sectorNumber, proofType, waitSeedRandomness, []abi.PieceInfo{}) 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 // Step 6: Submit the ProveCommit to the network
tm.t.Log("Submitting ProveCommitSector ...") if proofType.IsNonInteractive() {
tm.t.Log("Submitting ProveCommitSector ...")
r, err = tm.submitMessage(ctx, &miner14.ProveCommitSectors3Params{ var provingDeadline uint64 = 7
SectorActivations: []miner14.SectorActivationManifest{{SectorNumber: sectorNumber}}, if tm.IsUmmutableDeadline(ctx, provingDeadline) {
SectorProofs: [][]byte{proveCommit}, // avoid immutable deadlines
RequireActivationSuccess: true, provingDeadline = 5
}, 0, builtin.MethodsMiner.ProveCommitSectors3) }
req.NoError(err)
req.True(r.Receipt.ExitCode.IsSuccess()) 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 {
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())
}
}
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 tm.proofType[sectorNumber] = proofType
respCh, cancelFn := tm.wdPostLoop(ctx, sectorNumber, tm.sealedCids[sectorNumber], tm.sealedSectorPaths[sectorNumber], tm.cacheDirPaths[sectorNumber]) respCh, cancelFn := tm.wdPostLoop(ctx, sectorNumber, tm.sealedCids[sectorNumber], tm.sealedSectorPaths[sectorNumber], tm.cacheDirPaths[sectorNumber])
return sectorNumber, respCh, cancelFn return sectorNumber, respCh, cancelFn
} }
func (tm *TestUnmanagedMiner) OnboardSectorWithPieces(ctx context.Context, proofType abi.RegisteredSealProof) (abi.SectorNumber, chan WindowPostResp, context.CancelFunc) {
sectorNumber := tm.NextSectorNumber()
// 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"),
}}
}
_, proveCommit := tm.PrepareSectorForProveCommit(ctx, proofType, sectorNumber, pieces)
// 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,
})
}
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( func (tm *TestUnmanagedMiner) wdPostLoop(
pctx context.Context, pctx context.Context,
sectorNumber abi.SectorNumber, 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) 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, Deadline: sp.Deadline,
PoStIndex: 0, PoStIndex: 0,
}, 1, builtin.MethodsMiner.DisputeWindowedPoSt) }, 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) 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, ChainCommitEpoch: chainRandomnessEpoch,
ChainCommitRand: chainRandomness, ChainCommitRand: chainRandomness,
Deadline: sp.Deadline, Deadline: sp.Deadline,
@ -754,12 +855,16 @@ func (tm *TestUnmanagedMiner) generateWindowPost(
return proofBytes, nil 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. // 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. // 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) head, err := tm.FullNode.ChainHead(ctx)
require.NoError(tm.t, err) require.NoError(tm.t, err)
if proofType.IsNonInteractive() {
return head.Height() - 1 // no need to wait
}
var sealRandEpoch abi.ChainEpoch var sealRandEpoch abi.ChainEpoch
if head.Height() > policy.SealRandomnessLookback { if head.Height() > policy.SealRandomnessLookback {
sealRandEpoch = head.Height() - policy.SealRandomnessLookback sealRandEpoch = head.Height() - policy.SealRandomnessLookback
@ -775,53 +880,6 @@ func (tm *TestUnmanagedMiner) waitPreCommitSealRandomness(ctx context.Context, s
return sealRandEpoch 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( func (tm *TestUnmanagedMiner) generatePreCommit(
ctx context.Context, ctx context.Context,
sectorNumber abi.SectorNumber, sectorNumber abi.SectorNumber,
@ -829,6 +887,15 @@ func (tm *TestUnmanagedMiner) generatePreCommit(
proofType abi.RegisteredSealProof, proofType abi.RegisteredSealProof,
pieceInfo []abi.PieceInfo, 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) req := require.New(tm.t)
tm.t.Logf("Miner %s: Generating proof type %d PreCommit for sector %d...", tm.ActorAddr, proofType, sectorNumber) 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 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) req := require.New(tm.t)
head, err := tm.FullNode.ChainHead(ctx) head, err := tm.FullNode.ChainHead(ctx)
req.NoError(err) req.NoError(err)
tm.t.Logf("Miner %s: Fetching pre-commit info for sector %d...", tm.ActorAddr, sectorNumber) var seedRandomnessHeight abi.ChainEpoch
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) if proofType.IsNonInteractive() {
tm.FullNode.WaitTillChain(ctx, HeightAtLeast(seedRandomnessHeight+5)) 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))
head, err = tm.FullNode.ChainHead(ctx)
req.NoError(err)
}
minerAddrBytes := new(bytes.Buffer) minerAddrBytes := new(bytes.Buffer)
req.NoError(tm.ActorAddr.MarshalCBOR(minerAddrBytes)) 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) 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()) rand, err := tm.FullNode.StateGetRandomnessFromBeacon(ctx, crypto.DomainSeparationTag_InteractiveSealChallengeSeed, seedRandomnessHeight, minerAddrBytes.Bytes(), head.Key())
req.NoError(err) req.NoError(err)
seedRandomness := abi.InteractiveSealRandomness(rand) seedRandomness := abi.InteractiveSealRandomness(rand)
tm.t.Logf("Miner %s: Obtained seed randomness for sector %d: %x", tm.ActorAddr, sectorNumber, seedRandomness) 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( func (tm *TestUnmanagedMiner) generateProveCommit(
ctx context.Context, _ context.Context,
sectorNumber abi.SectorNumber, sectorNumber abi.SectorNumber,
proofType abi.RegisteredSealProof, proofType abi.RegisteredSealProof,
seedRandomness abi.InteractiveSealRandomness, seedRandomness abi.InteractiveSealRandomness,
pieces []abi.PieceInfo, pieces []abi.PieceInfo,
) []byte { ) []byte {
tm.t.Logf("Miner %s: Generating proof type %d Sector Proof for sector %d...", tm.ActorAddr, proofType, sectorNumber) tm.t.Logf("Miner %s: Generating proof type %d Sector Proof for sector %d...", tm.ActorAddr, proofType, sectorNumber)
req := require.New(tm.t) 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) tm.t.Logf("Miner %s: Running proof type %d SealCommitPhase2 for sector %d...", tm.ActorAddr, proofType, sectorNumber)
sectorProof, err := ffi.SealCommitPhase2(scp1, sectorNumber, actorId) var sectorProof []byte
req.NoError(err)
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) 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 return sectorProof
} }
func (tm *TestUnmanagedMiner) submitMessage( func (tm *TestUnmanagedMiner) SubmitMessage(
ctx context.Context, ctx context.Context,
params cbg.CBORMarshaler, params cbg.CBORMarshaler,
value uint64, value uint64,
method abi.MethodNum, method abi.MethodNum,
) (*api.MsgLookup, error) { ) (*api.MsgLookup, error) {
enc, aerr := actors.SerializeParams(params) enc, aerr := actors.SerializeParams(params)
if aerr != nil { if aerr != nil {
return nil, aerr return nil, aerr
@ -1034,14 +1131,10 @@ func (tm *TestUnmanagedMiner) WaitTillActivatedAndAssertPower(
// Miner B should now have power // Miner B should now have power
tm.AssertPower(ctx, uint64(tm.options.sectorSize), uint64(tm.options.sectorSize)) tm.AssertPower(ctx, uint64(tm.options.sectorSize), uint64(tm.options.sectorSize))
if tm.mockProofs { if !tm.mockProofs {
// WindowPost Dispute should succeed as we are using mock proofs
err := tm.SubmitPostDispute(ctx, sector)
require.NoError(tm.t, err)
} else {
// WindowPost Dispute should fail // WindowPost Dispute should fail
tm.AssertDisputeFails(ctx, sector) tm.AssertDisputeFails(ctx, sector)
} } // else it would pass, which we don't want
} }
func (tm *TestUnmanagedMiner) AssertDisputeFails(ctx context.Context, sector abi.SectorNumber) { 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(), "failed to dispute valid post")
require.Contains(tm.t, err.Error(), "(RetCode=16)") 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
}

View File

@ -8,24 +8,28 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/filecoin-project/go-state-types/abi" "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/build"
"github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/itests/kit" "github.com/filecoin-project/lotus/itests/kit"
) )
// Manually onboard CC sectors, bypassing lotus-miner onboarding pathways // Manually onboard CC sectors, bypassing lotus-miner onboarding pathways
func TestManualSectorOnboarding(t *testing.T) { func TestManualSectorOnboarding(t *testing.T) {
const defaultSectorSize = abi.SectorSize(2 << 10) // 2KiB
req := require.New(t) 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} { for _, withMockProofs := range []bool{true, false} {
testName := "WithRealProofs" testName := "WithRealProofs"
if withMockProofs { if withMockProofs {
testName = "WithMockProofs" testName = "WithMockProofs"
} }
t.Run(testName, func(t *testing.T) { t.Run(testName, func(t *testing.T) {
if testName == "WithRealProofs" { if !withMockProofs {
kit.Expensive(t) kit.Expensive(t)
} }
kit.QuietMiningLogs() kit.QuietMiningLogs()
@ -85,7 +89,7 @@ func TestManualSectorOnboarding(t *testing.T) {
var bRespCh chan kit.WindowPostResp var bRespCh chan kit.WindowPostResp
var bWdPostCancelF context.CancelFunc 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 // 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) 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 // 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) minerC.WaitTillActivatedAndAssertPower(ctx, cRespCh, cSectorNum)
// Miner B has activated the CC sector -> upgrade it with snapdeals // 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 _ = minerB.SnapDeal(ctx, kit.TestSpt, bSectorNum)
// in snapping it as snapping is only for activated sectors // cancel the WdPost for the CC sector as the corresponding CommR is no longer valid
if !withMockProofs { bWdPostCancelF()
minerB.SnapDeal(ctx, kit.TestSpt, bSectorNum)
// cancel the WdPost for the CC sector as the corresponding CommR is no longer valid
bWdPostCancelF()
}
}) })
} }
} }