test: niporep: initial niporep e2e test
This commit is contained in:
parent
96d3062341
commit
f4e99f6bd1
2
.github/workflows/test.yml
vendored
2
.github/workflows/test.yml
vendored
@ -61,6 +61,7 @@ jobs:
|
|||||||
# to support resource intensive jobs.
|
# to support resource intensive jobs.
|
||||||
runners: |
|
runners: |
|
||||||
{
|
{
|
||||||
|
"itest-niporep_manual": ["self-hosted", "linux", "x64", "4xlarge"],
|
||||||
"itest-sector_pledge": ["self-hosted", "linux", "x64", "4xlarge"],
|
"itest-sector_pledge": ["self-hosted", "linux", "x64", "4xlarge"],
|
||||||
"itest-worker": ["self-hosted", "linux", "x64", "4xlarge"],
|
"itest-worker": ["self-hosted", "linux", "x64", "4xlarge"],
|
||||||
"itest-manual_onboarding": ["self-hosted", "linux", "x64", "4xlarge"],
|
"itest-manual_onboarding": ["self-hosted", "linux", "x64", "4xlarge"],
|
||||||
@ -118,6 +119,7 @@ jobs:
|
|||||||
"itest-direct_data_onboard_verified",
|
"itest-direct_data_onboard_verified",
|
||||||
"itest-direct_data_onboard",
|
"itest-direct_data_onboard",
|
||||||
"itest-manual_onboarding",
|
"itest-manual_onboarding",
|
||||||
|
"itest-niporep_manual",
|
||||||
"itest-net",
|
"itest-net",
|
||||||
"itest-path_detach_redeclare",
|
"itest-path_detach_redeclare",
|
||||||
"itest-sealing_resources",
|
"itest-sealing_resources",
|
||||||
|
@ -455,7 +455,7 @@ func (tm *TestUnmanagedMiner) SubmitProveCommit(
|
|||||||
tm.t.Log("Submitting ProveCommitSector ...")
|
tm.t.Log("Submitting ProveCommitSector ...")
|
||||||
|
|
||||||
var provingDeadline uint64 = 7
|
var provingDeadline uint64 = 7
|
||||||
if tm.IsUmmutableDeadline(ctx, provingDeadline) {
|
if tm.IsImmutableDeadline(ctx, provingDeadline) {
|
||||||
// avoid immutable deadlines
|
// avoid immutable deadlines
|
||||||
provingDeadline = 5
|
provingDeadline = 5
|
||||||
}
|
}
|
||||||
@ -1144,7 +1144,7 @@ func (tm *TestUnmanagedMiner) AssertDisputeFails(ctx context.Context, sector abi
|
|||||||
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 {
|
func (tm *TestUnmanagedMiner) IsImmutableDeadline(ctx context.Context, deadlineIndex uint64) bool {
|
||||||
di, err := tm.FullNode.StateMinerProvingDeadline(ctx, tm.ActorAddr, types.EmptyTSK)
|
di, err := tm.FullNode.StateMinerProvingDeadline(ctx, tm.ActorAddr, types.EmptyTSK)
|
||||||
require.NoError(tm.t, err)
|
require.NoError(tm.t, err)
|
||||||
// don't rely on di.Index because if we haven't enrolled in cron it won't be ticking
|
// don't rely on di.Index because if we haven't enrolled in cron it won't be ticking
|
||||||
|
260
itests/niporep_manual_test.go
Normal file
260
itests/niporep_manual_test.go
Normal file
@ -0,0 +1,260 @@
|
|||||||
|
package itests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/ipld/go-ipld-prime"
|
||||||
|
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||||
|
"github.com/ipld/go-ipld-prime/datamodel"
|
||||||
|
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
||||||
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
|
"github.com/multiformats/go-multicodec"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
|
"github.com/filecoin-project/go-state-types/builtin"
|
||||||
|
miner14 "github.com/filecoin-project/go-state-types/builtin/v14/miner"
|
||||||
|
"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/chain/types"
|
||||||
|
"github.com/filecoin-project/lotus/itests/kit"
|
||||||
|
"github.com/filecoin-project/lotus/lib/must"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestManualNISectorOnboarding(t *testing.T) {
|
||||||
|
req := require.New(t)
|
||||||
|
|
||||||
|
const defaultSectorSize = abi.SectorSize(2 << 10) // 2KiB
|
||||||
|
sealProofType, err := miner.SealProofTypeFromSectorSize(defaultSectorSize, network.Version23, miner.SealProofVariant_NonInteractive)
|
||||||
|
req.NoError(err)
|
||||||
|
|
||||||
|
for _, withMockProofs := range []bool{true, false} {
|
||||||
|
testName := "WithRealProofs"
|
||||||
|
if withMockProofs {
|
||||||
|
testName = "WithMockProofs"
|
||||||
|
}
|
||||||
|
t.Run(testName, func(t *testing.T) {
|
||||||
|
if !withMockProofs {
|
||||||
|
kit.Expensive(t)
|
||||||
|
}
|
||||||
|
kit.QuietMiningLogs()
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
var (
|
||||||
|
// need to pick a balance value so that the test is not racy on CI by running through it's WindowPostDeadlines too fast
|
||||||
|
blocktime = 2 * time.Millisecond
|
||||||
|
client kit.TestFullNode
|
||||||
|
minerA kit.TestMiner // A is a standard genesis miner
|
||||||
|
)
|
||||||
|
|
||||||
|
// Setup and begin mining with a single miner (A)
|
||||||
|
// Miner A will only be a genesis Miner with power allocated in the genesis block and will not onboard any sectors from here on
|
||||||
|
ens := kit.NewEnsemble(t, kit.MockProofs(withMockProofs)).
|
||||||
|
FullNode(&client, kit.SectorSize(defaultSectorSize)).
|
||||||
|
// preseal more than the default number of sectors to ensure that the genesis miner has power
|
||||||
|
// because our unmanaged miners won't produce blocks so we may get null rounds
|
||||||
|
Miner(&minerA, &client, kit.PresealSectors(5), kit.SectorSize(defaultSectorSize), kit.WithAllSubsystems()).
|
||||||
|
Start().
|
||||||
|
InterconnectAll()
|
||||||
|
blockMiners := ens.BeginMiningMustPost(blocktime)
|
||||||
|
req.Len(blockMiners, 1)
|
||||||
|
blockMiner := blockMiners[0]
|
||||||
|
|
||||||
|
// Instantiate MinerB to manually handle sector onboarding and power acquisition through sector activation.
|
||||||
|
// Unlike other miners managed by the Lotus Miner storage infrastructure, MinerB operates independently,
|
||||||
|
// performing all related tasks manually. Managed by the TestKit, MinerB has the capability to utilize actual proofs
|
||||||
|
// for the processes of sector onboarding and activation.
|
||||||
|
nodeOpts := []kit.NodeOpt{kit.SectorSize(defaultSectorSize), kit.OwnerAddr(client.DefaultKey)}
|
||||||
|
minerB, ens := ens.UnmanagedMiner(&client, nodeOpts...)
|
||||||
|
|
||||||
|
ens.Start()
|
||||||
|
|
||||||
|
build.Clock.Sleep(time.Second)
|
||||||
|
|
||||||
|
t.Log("Checking initial power ...")
|
||||||
|
|
||||||
|
// Miner A should have power as it has already onboarded sectors in the genesis block
|
||||||
|
head, err := client.ChainHead(ctx)
|
||||||
|
req.NoError(err)
|
||||||
|
p, err := client.StateMinerPower(ctx, minerA.ActorAddr, head.Key())
|
||||||
|
req.NoError(err)
|
||||||
|
t.Logf("MinerA RBP: %v, QaP: %v", p.MinerPower.QualityAdjPower.String(), p.MinerPower.RawBytePower.String())
|
||||||
|
|
||||||
|
// Miner B should have no power as it has yet to onboard and activate any sectors
|
||||||
|
minerB.AssertNoPower(ctx)
|
||||||
|
|
||||||
|
// Verify that ProveCommitSectorsNI rejects messages with invalid parameters
|
||||||
|
verifyProveCommitSectorsNIErrorConditions(ctx, t, minerB, sealProofType)
|
||||||
|
|
||||||
|
// ---- Miner B onboards a CC sector
|
||||||
|
var bSectorNum abi.SectorNumber
|
||||||
|
var bRespCh chan kit.WindowPostResp
|
||||||
|
var bWdPostCancelF context.CancelFunc
|
||||||
|
|
||||||
|
// Onboard a CC sector with Miner B using NI-PoRep
|
||||||
|
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)
|
||||||
|
|
||||||
|
// Check that the sector-activated event was emitted
|
||||||
|
{
|
||||||
|
expectedEntries := []types.EventEntry{
|
||||||
|
{Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "$type", Value: must.One(ipld.Encode(basicnode.NewString("sector-activated"), dagcbor.Encode))},
|
||||||
|
{Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "sector", Value: must.One(ipld.Encode(basicnode.NewInt(int64(bSectorNum)), dagcbor.Encode))},
|
||||||
|
{Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "unsealed-cid", Value: must.One(ipld.Encode(datamodel.Null, dagcbor.Encode))},
|
||||||
|
}
|
||||||
|
from := head.Height()
|
||||||
|
recentEvents, err := client.FullNode.GetActorEventsRaw(ctx, &types.ActorEventFilter{FromHeight: &from})
|
||||||
|
req.NoError(err)
|
||||||
|
req.Len(recentEvents, 1)
|
||||||
|
req.Equal(expectedEntries, recentEvents[0].Entries)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure that the block miner checks for and waits for posts during the appropriate proving window from our new miner with a sector
|
||||||
|
blockMiner.WatchMinerForPost(minerB.ActorAddr)
|
||||||
|
|
||||||
|
// Wait till both miners' sectors have had their first post and are activated and check that this is reflected in miner power
|
||||||
|
minerB.WaitTillActivatedAndAssertPower(ctx, bRespCh, bSectorNum)
|
||||||
|
|
||||||
|
head, err = client.ChainHead(ctx)
|
||||||
|
req.NoError(err)
|
||||||
|
|
||||||
|
// Miner B has activated the CC sector -> upgrade it with snapdeals
|
||||||
|
snapPieces := minerB.SnapDeal(ctx, kit.TestSpt, bSectorNum)
|
||||||
|
// cancel the WdPost for the CC sector as the corresponding CommR is no longer valid
|
||||||
|
bWdPostCancelF()
|
||||||
|
|
||||||
|
// Check "sector-updated" event happned after snap
|
||||||
|
{
|
||||||
|
expectedEntries := []types.EventEntry{
|
||||||
|
{Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "$type", Value: must.One(ipld.Encode(basicnode.NewString("sector-updated"), dagcbor.Encode))},
|
||||||
|
{Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "sector", Value: must.One(ipld.Encode(basicnode.NewInt(int64(bSectorNum)), dagcbor.Encode))},
|
||||||
|
{Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "unsealed-cid", Value: must.One(ipld.Encode(basicnode.NewLink(cidlink.Link{Cid: snapPieces[0].PieceCID}), dagcbor.Encode))},
|
||||||
|
{Flags: 0x03, Codec: uint64(multicodec.Cbor), Key: "piece-cid", Value: must.One(ipld.Encode(basicnode.NewLink(cidlink.Link{Cid: snapPieces[0].PieceCID}), dagcbor.Encode))},
|
||||||
|
{Flags: 0x01, Codec: uint64(multicodec.Cbor), Key: "piece-size", Value: must.One(ipld.Encode(basicnode.NewInt(int64(snapPieces[0].Size)), dagcbor.Encode))},
|
||||||
|
}
|
||||||
|
from := head.Height()
|
||||||
|
recentEvents, err := client.FullNode.GetActorEventsRaw(ctx, &types.ActorEventFilter{FromHeight: &from})
|
||||||
|
req.NoError(err)
|
||||||
|
req.Len(recentEvents, 1)
|
||||||
|
req.Equal(expectedEntries, recentEvents[0].Entries)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func verifyProveCommitSectorsNIErrorConditions(ctx context.Context, t *testing.T, miner *kit.TestUnmanagedMiner, sealProofType abi.RegisteredSealProof) {
|
||||||
|
req := require.New(t)
|
||||||
|
|
||||||
|
head, err := miner.FullNode.ChainHead(ctx)
|
||||||
|
req.NoError(err)
|
||||||
|
|
||||||
|
actorIdNum, err := address.IDFromAddress(miner.ActorAddr)
|
||||||
|
req.NoError(err)
|
||||||
|
actorId := abi.ActorID(actorIdNum)
|
||||||
|
|
||||||
|
var provingDeadline uint64 = 7
|
||||||
|
if miner.IsImmutableDeadline(ctx, provingDeadline) {
|
||||||
|
// avoid immutable deadlines
|
||||||
|
provingDeadline = 5
|
||||||
|
}
|
||||||
|
|
||||||
|
submitAndFail := func(params *miner14.ProveCommitSectorsNIParams, errMsg string, errCode int) {
|
||||||
|
t.Helper()
|
||||||
|
r, err := miner.SubmitMessage(ctx, params, 1, builtin.MethodsMiner.ProveCommitSectorsNI)
|
||||||
|
req.Error(err)
|
||||||
|
req.Contains(err.Error(), errMsg)
|
||||||
|
if errCode > 0 {
|
||||||
|
req.Contains(err.Error(), fmt.Sprintf("(RetCode=%d)", errCode))
|
||||||
|
}
|
||||||
|
req.Nil(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
sn := abi.SectorNumber(5000)
|
||||||
|
mkSai := func() miner14.SectorNIActivationInfo {
|
||||||
|
sn++
|
||||||
|
return miner14.SectorNIActivationInfo{
|
||||||
|
SealingNumber: sn,
|
||||||
|
SealerID: actorId,
|
||||||
|
SealedCID: cid.MustParse("bagboea4b5abcatlxechwbp7kjpjguna6r6q7ejrhe6mdp3lf34pmswn27pkkiekz"),
|
||||||
|
SectorNumber: sn,
|
||||||
|
SealRandEpoch: head.Height() - 10,
|
||||||
|
Expiration: 2880 * 300,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mkParams := func() miner14.ProveCommitSectorsNIParams {
|
||||||
|
return miner14.ProveCommitSectorsNIParams{
|
||||||
|
Sectors: []miner14.SectorNIActivationInfo{mkSai(), mkSai()},
|
||||||
|
AggregateProof: []byte{0xca, 0xfe, 0xbe, 0xef},
|
||||||
|
SealProofType: sealProofType,
|
||||||
|
AggregateProofType: abi.RegisteredAggregationProof_SnarkPackV2,
|
||||||
|
ProvingDeadline: provingDeadline,
|
||||||
|
RequireActivationSuccess: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test message rejection on no sectors
|
||||||
|
params := mkParams()
|
||||||
|
params.Sectors = []miner14.SectorNIActivationInfo{}
|
||||||
|
submitAndFail(¶ms, "too few sectors", 16)
|
||||||
|
|
||||||
|
// Test message rejection on too many sectors
|
||||||
|
sectorInfos := make([]miner14.SectorNIActivationInfo, 66)
|
||||||
|
for i := range sectorInfos {
|
||||||
|
sectorInfos[i] = mkSai()
|
||||||
|
}
|
||||||
|
params = mkParams()
|
||||||
|
params.Sectors = sectorInfos
|
||||||
|
submitAndFail(¶ms, "too many sectors", 16)
|
||||||
|
|
||||||
|
// Test bad aggregation proof type
|
||||||
|
params = mkParams()
|
||||||
|
params.AggregateProofType = abi.RegisteredAggregationProof_SnarkPackV1
|
||||||
|
submitAndFail(¶ms, "aggregate proof type", 16)
|
||||||
|
|
||||||
|
// Test bad SealerID
|
||||||
|
params = mkParams()
|
||||||
|
params.Sectors[1].SealerID = 1234
|
||||||
|
submitAndFail(¶ms, "invalid NI commit 1 while requiring activation success", 16)
|
||||||
|
|
||||||
|
// Test bad SealingNumber
|
||||||
|
params = mkParams()
|
||||||
|
params.Sectors[1].SealingNumber = 1234
|
||||||
|
submitAndFail(¶ms, "invalid NI commit 1 while requiring activation success", 16)
|
||||||
|
|
||||||
|
// Test bad SealedCID
|
||||||
|
params = mkParams()
|
||||||
|
params.Sectors[1].SealedCID = cid.MustParse("baga6ea4seaqjtovkwk4myyzj56eztkh5pzsk5upksan6f5outesy62bsvl4dsha")
|
||||||
|
submitAndFail(¶ms, "invalid NI commit 1 while requiring activation success", 16)
|
||||||
|
|
||||||
|
// Test bad SealRandEpoch
|
||||||
|
head, err = miner.FullNode.ChainHead(ctx)
|
||||||
|
req.NoError(err)
|
||||||
|
params = mkParams()
|
||||||
|
params.Sectors[1].SealRandEpoch = head.Height() + builtin.EpochsInDay
|
||||||
|
submitAndFail(¶ms, fmt.Sprintf("seal challenge epoch %d must be before now", params.Sectors[1].SealRandEpoch), 16)
|
||||||
|
params.Sectors[1].SealRandEpoch = head.Height() - 190*builtin.EpochsInDay
|
||||||
|
submitAndFail(¶ms, "invalid NI commit 1 while requiring activation success", 16)
|
||||||
|
|
||||||
|
// Immutable/bad deadlines
|
||||||
|
di, err := miner.FullNode.StateMinerProvingDeadline(ctx, miner.ActorAddr, head.Key())
|
||||||
|
req.NoError(err)
|
||||||
|
currentDeadlineIdx := uint64(math.Abs(float64((di.CurrentEpoch - di.PeriodStart) / di.WPoStChallengeWindow)))
|
||||||
|
req.Less(currentDeadlineIdx, di.WPoStPeriodDeadlines)
|
||||||
|
params = mkParams()
|
||||||
|
params.ProvingDeadline = currentDeadlineIdx
|
||||||
|
submitAndFail(¶ms, fmt.Sprintf("proving deadline %d must not be the current or next deadline", currentDeadlineIdx), 18)
|
||||||
|
params.ProvingDeadline = currentDeadlineIdx + 1
|
||||||
|
submitAndFail(¶ms, fmt.Sprintf("proving deadline %d must not be the current or next deadline", currentDeadlineIdx+1), 18)
|
||||||
|
params.ProvingDeadline = di.WPoStPeriodDeadlines // too big
|
||||||
|
submitAndFail(¶ms, fmt.Sprintf("proving deadline index %d invalid", di.WPoStPeriodDeadlines), 16)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user