From 231528409bac775b2ec340de99586ba2a114bd90 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Mon, 10 Mar 2025 11:25:20 +1100 Subject: [PATCH 1/2] chore(fees): add initial fees & migration testing (FIP-0100) --- CHANGELOG.md | 1 + api/api_full.go | 1 + build/openrpc/full.json | 7 +- build/openrpc/gateway.json | 7 +- chain/actors/builtin/miner/actor.go.template | 1 + chain/actors/builtin/miner/miner.go | 1 + chain/actors/builtin/miner/state.go.template | 17 +- chain/actors/builtin/miner/v0.go | 4 + chain/actors/builtin/miner/v10.go | 4 + chain/actors/builtin/miner/v11.go | 4 + chain/actors/builtin/miner/v12.go | 4 + chain/actors/builtin/miner/v13.go | 4 + chain/actors/builtin/miner/v14.go | 4 + chain/actors/builtin/miner/v15.go | 4 + chain/actors/builtin/miner/v16.go | 14 +- chain/actors/builtin/miner/v2.go | 4 + chain/actors/builtin/miner/v3.go | 4 + chain/actors/builtin/miner/v4.go | 4 + chain/actors/builtin/miner/v5.go | 4 + chain/actors/builtin/miner/v6.go | 4 + chain/actors/builtin/miner/v7.go | 4 + chain/actors/builtin/miner/v8.go | 4 + chain/actors/builtin/miner/v9.go | 4 + documentation/en/api-v0-methods.md | 3 +- documentation/en/api-v1-unstable-methods.md | 3 +- itests/daily_fees_test.go | 525 +++++++++++++++++++ itests/direct_data_onboard_test.go | 10 +- itests/direct_data_onboard_verified_test.go | 250 ++------- itests/kit/funds.go | 151 ++++++ itests/kit/node_unmanaged.go | 95 ++-- itests/manual_onboarding_test.go | 7 +- itests/niporep_manual_test.go | 13 +- node/impl/full/state.go | 1 + 33 files changed, 911 insertions(+), 256 deletions(-) create mode 100644 itests/daily_fees_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cca0ce2782..884066aede1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - chore: upgrade drand client. ([filecoin-project/lotus#12830](https://github.com/filecoin-project/lotus/pull/12830)) - chore: upgrade go-state-types with big.Int{} change that means an empty big.Int is now treated as zero for all operations ([filecoin-project/lotus#12936](https://github.com/filecoin-project/lotus/pull/12936)) - fix!: change circulating supply calculation for calibnet, butterflynet and 2k for nv25 upgrade. ([filecoin-project/lotus#12938](https://github.com/filecoin-project/lotus/pull/12938)) +- feat: add DailyFee integration tests ([filecoin-project/lotus#12942](https://github.com/filecoin-project/lotus/pull/12942)) # UNRELEASED v.1.32.0 diff --git a/api/api_full.go b/api/api_full.go index 9d39606ba7b..cf492db1469 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -1398,6 +1398,7 @@ const ( type Deadline struct { PostSubmissions bitfield.BitField DisputableProofCount uint64 + DailyFee abi.TokenAmount } type Partition struct { diff --git a/build/openrpc/full.json b/build/openrpc/full.json index 79f6b6522b6..5b18fea86cf 100644 --- a/build/openrpc/full.json +++ b/build/openrpc/full.json @@ -21786,7 +21786,8 @@ 5, 1 ], - "DisputableProofCount": 42 + "DisputableProofCount": 42, + "DailyFee": "0" } ] ], @@ -21794,6 +21795,10 @@ { "additionalProperties": false, "properties": { + "DailyFee": { + "additionalProperties": false, + "type": "object" + }, "DisputableProofCount": { "title": "number", "type": "number" diff --git a/build/openrpc/gateway.json b/build/openrpc/gateway.json index 3bde230b1f4..c371420cdf7 100644 --- a/build/openrpc/gateway.json +++ b/build/openrpc/gateway.json @@ -10185,7 +10185,8 @@ 5, 1 ], - "DisputableProofCount": 42 + "DisputableProofCount": 42, + "DailyFee": "0" } ] ], @@ -10193,6 +10194,10 @@ { "additionalProperties": false, "properties": { + "DailyFee": { + "additionalProperties": false, + "type": "object" + }, "DisputableProofCount": { "title": "number", "type": "number" diff --git a/chain/actors/builtin/miner/actor.go.template b/chain/actors/builtin/miner/actor.go.template index 44b13925b81..d81a10c8fdd 100644 --- a/chain/actors/builtin/miner/actor.go.template +++ b/chain/actors/builtin/miner/actor.go.template @@ -127,6 +127,7 @@ type Deadline interface { PartitionsChanged(Deadline) (bool, error) DisputableProofCount() (uint64, error) + DailyFee() abi.TokenAmount } type Partition interface { diff --git a/chain/actors/builtin/miner/miner.go b/chain/actors/builtin/miner/miner.go index 95fc61a6e2e..0d40fccc112 100644 --- a/chain/actors/builtin/miner/miner.go +++ b/chain/actors/builtin/miner/miner.go @@ -211,6 +211,7 @@ type Deadline interface { PartitionsChanged(Deadline) (bool, error) DisputableProofCount() (uint64, error) + DailyFee() abi.TokenAmount } type Partition interface { diff --git a/chain/actors/builtin/miner/state.go.template b/chain/actors/builtin/miner/state.go.template index b4c91095457..d3c29ed37f2 100644 --- a/chain/actors/builtin/miner/state.go.template +++ b/chain/actors/builtin/miner/state.go.template @@ -552,6 +552,15 @@ func (d *deadline{{.v}}) DisputableProofCount() (uint64, error) { {{end}} } +func (d *deadline{{.v}}) DailyFee() abi.TokenAmount { +{{- if (ge .v 16)}} + if d.Deadline.DailyFee.Int != nil { + return d.Deadline.DailyFee + } +{{- end}} + return big.Zero() +} + func (p *partition{{.v}}) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } @@ -569,6 +578,12 @@ func (p *partition{{.v}}) UnprovenSectors() (bitfield.BitField, error) { } func fromV{{.v}}SectorOnChainInfo(v{{.v}} miner{{.v}}.SectorOnChainInfo) SectorOnChainInfo { +{{- if (ge .v 16)}} + dailyFee := v{{.v}}.DailyFee + if dailyFee.Int == nil { + dailyFee = big.Zero() + } +{{- end}} info := SectorOnChainInfo{ SectorNumber: v{{.v}}.SectorNumber, SealProof: v{{.v}}.SealProof, @@ -590,7 +605,7 @@ func fromV{{.v}}SectorOnChainInfo(v{{.v}} miner{{.v}}.SectorOnChainInfo) SectorO Flags: SectorOnChainInfoFlags(v{{.v}}.Flags), {{- end}} {{- if (ge .v 16)}} - DailyFee: v{{.v}}.DailyFee, + DailyFee: dailyFee, {{- else}} DailyFee: big.Zero(), {{- end}} diff --git a/chain/actors/builtin/miner/v0.go b/chain/actors/builtin/miner/v0.go index f8123fe27cd..fc59eaea69e 100644 --- a/chain/actors/builtin/miner/v0.go +++ b/chain/actors/builtin/miner/v0.go @@ -480,6 +480,10 @@ func (d *deadline0) DisputableProofCount() (uint64, error) { } +func (d *deadline0) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition0) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v10.go b/chain/actors/builtin/miner/v10.go index 8f111476951..e9f6724fe8b 100644 --- a/chain/actors/builtin/miner/v10.go +++ b/chain/actors/builtin/miner/v10.go @@ -515,6 +515,10 @@ func (d *deadline10) DisputableProofCount() (uint64, error) { } +func (d *deadline10) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition10) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v11.go b/chain/actors/builtin/miner/v11.go index 14204cf9509..cbd72a64164 100644 --- a/chain/actors/builtin/miner/v11.go +++ b/chain/actors/builtin/miner/v11.go @@ -515,6 +515,10 @@ func (d *deadline11) DisputableProofCount() (uint64, error) { } +func (d *deadline11) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition11) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v12.go b/chain/actors/builtin/miner/v12.go index e497429a2d2..5acb0367307 100644 --- a/chain/actors/builtin/miner/v12.go +++ b/chain/actors/builtin/miner/v12.go @@ -515,6 +515,10 @@ func (d *deadline12) DisputableProofCount() (uint64, error) { } +func (d *deadline12) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition12) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v13.go b/chain/actors/builtin/miner/v13.go index e1ae8dff2de..6f8d698556b 100644 --- a/chain/actors/builtin/miner/v13.go +++ b/chain/actors/builtin/miner/v13.go @@ -515,6 +515,10 @@ func (d *deadline13) DisputableProofCount() (uint64, error) { } +func (d *deadline13) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition13) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v14.go b/chain/actors/builtin/miner/v14.go index e7aaaa0c38e..c1f95dd12cc 100644 --- a/chain/actors/builtin/miner/v14.go +++ b/chain/actors/builtin/miner/v14.go @@ -515,6 +515,10 @@ func (d *deadline14) DisputableProofCount() (uint64, error) { } +func (d *deadline14) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition14) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v15.go b/chain/actors/builtin/miner/v15.go index f9babc92077..46865e66596 100644 --- a/chain/actors/builtin/miner/v15.go +++ b/chain/actors/builtin/miner/v15.go @@ -515,6 +515,10 @@ func (d *deadline15) DisputableProofCount() (uint64, error) { } +func (d *deadline15) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition15) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v16.go b/chain/actors/builtin/miner/v16.go index 3ba21adeb96..4267248033f 100644 --- a/chain/actors/builtin/miner/v16.go +++ b/chain/actors/builtin/miner/v16.go @@ -13,6 +13,7 @@ import ( rle "github.com/filecoin-project/go-bitfield/rle" "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/big" builtin16 "github.com/filecoin-project/go-state-types/builtin" miner16 "github.com/filecoin-project/go-state-types/builtin/v16/miner" adt16 "github.com/filecoin-project/go-state-types/builtin/v16/util/adt" @@ -514,6 +515,13 @@ func (d *deadline16) DisputableProofCount() (uint64, error) { } +func (d *deadline16) DailyFee() abi.TokenAmount { + if d.Deadline.DailyFee.Int != nil { + return d.Deadline.DailyFee + } + return big.Zero() +} + func (p *partition16) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } @@ -531,6 +539,10 @@ func (p *partition16) UnprovenSectors() (bitfield.BitField, error) { } func fromV16SectorOnChainInfo(v16 miner16.SectorOnChainInfo) SectorOnChainInfo { + dailyFee := v16.DailyFee + if dailyFee.Int == nil { + dailyFee = big.Zero() + } info := SectorOnChainInfo{ SectorNumber: v16.SectorNumber, SealProof: v16.SealProof, @@ -547,7 +559,7 @@ func fromV16SectorOnChainInfo(v16 miner16.SectorOnChainInfo) SectorOnChainInfo { PowerBaseEpoch: v16.PowerBaseEpoch, ReplacedDayReward: v16.ReplacedDayReward, Flags: SectorOnChainInfoFlags(v16.Flags), - DailyFee: v16.DailyFee, + DailyFee: dailyFee, } return info } diff --git a/chain/actors/builtin/miner/v2.go b/chain/actors/builtin/miner/v2.go index 04c59f76d03..f8d02093db4 100644 --- a/chain/actors/builtin/miner/v2.go +++ b/chain/actors/builtin/miner/v2.go @@ -512,6 +512,10 @@ func (d *deadline2) DisputableProofCount() (uint64, error) { } +func (d *deadline2) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition2) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v3.go b/chain/actors/builtin/miner/v3.go index 5ee11c7efa3..ac062fef8a3 100644 --- a/chain/actors/builtin/miner/v3.go +++ b/chain/actors/builtin/miner/v3.go @@ -512,6 +512,10 @@ func (d *deadline3) DisputableProofCount() (uint64, error) { } +func (d *deadline3) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition3) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v4.go b/chain/actors/builtin/miner/v4.go index cb20dc70820..7532244adbf 100644 --- a/chain/actors/builtin/miner/v4.go +++ b/chain/actors/builtin/miner/v4.go @@ -512,6 +512,10 @@ func (d *deadline4) DisputableProofCount() (uint64, error) { } +func (d *deadline4) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition4) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v5.go b/chain/actors/builtin/miner/v5.go index 08b7bfad947..a720cf38f20 100644 --- a/chain/actors/builtin/miner/v5.go +++ b/chain/actors/builtin/miner/v5.go @@ -512,6 +512,10 @@ func (d *deadline5) DisputableProofCount() (uint64, error) { } +func (d *deadline5) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition5) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v6.go b/chain/actors/builtin/miner/v6.go index cd49ed513c4..6195c8b6d60 100644 --- a/chain/actors/builtin/miner/v6.go +++ b/chain/actors/builtin/miner/v6.go @@ -512,6 +512,10 @@ func (d *deadline6) DisputableProofCount() (uint64, error) { } +func (d *deadline6) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition6) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v7.go b/chain/actors/builtin/miner/v7.go index 2929f23fa18..97638c0299e 100644 --- a/chain/actors/builtin/miner/v7.go +++ b/chain/actors/builtin/miner/v7.go @@ -511,6 +511,10 @@ func (d *deadline7) DisputableProofCount() (uint64, error) { } +func (d *deadline7) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition7) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v8.go b/chain/actors/builtin/miner/v8.go index a0abde0b394..7e7bc549a80 100644 --- a/chain/actors/builtin/miner/v8.go +++ b/chain/actors/builtin/miner/v8.go @@ -511,6 +511,10 @@ func (d *deadline8) DisputableProofCount() (uint64, error) { } +func (d *deadline8) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition8) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/chain/actors/builtin/miner/v9.go b/chain/actors/builtin/miner/v9.go index d4c7dea5d57..2773feffaa3 100644 --- a/chain/actors/builtin/miner/v9.go +++ b/chain/actors/builtin/miner/v9.go @@ -515,6 +515,10 @@ func (d *deadline9) DisputableProofCount() (uint64, error) { } +func (d *deadline9) DailyFee() abi.TokenAmount { + return big.Zero() +} + func (p *partition9) AllSectors() (bitfield.BitField, error) { return p.Partition.Sectors, nil } diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index 1a5fbf17555..55cf29dfee4 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -4952,7 +4952,8 @@ Response: 5, 1 ], - "DisputableProofCount": 42 + "DisputableProofCount": 42, + "DailyFee": "0" } ] ``` diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index e0637e9c178..1933053e1ce 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -7383,7 +7383,8 @@ Response: 5, 1 ], - "DisputableProofCount": 42 + "DisputableProofCount": 42, + "DailyFee": "0" } ] ``` diff --git a/itests/daily_fees_test.go b/itests/daily_fees_test.go new file mode 100644 index 00000000000..424f0c14d5a --- /dev/null +++ b/itests/daily_fees_test.go @@ -0,0 +1,525 @@ +package itests + +import ( + "context" + "sync" + "testing" + "time" + + "github.com/ipfs/go-cid" + "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/big" + "github.com/filecoin-project/go-state-types/builtin" + miner14 "github.com/filecoin-project/go-state-types/builtin/v14/miner" + verifreg14 "github.com/filecoin-project/go-state-types/builtin/v14/verifreg" + miner15 "github.com/filecoin-project/go-state-types/builtin/v15/miner" + miner16 "github.com/filecoin-project/go-state-types/builtin/v16/miner" + "github.com/filecoin-project/go-state-types/builtin/v8/util/adt" + "github.com/filecoin-project/go-state-types/network" + gstStore "github.com/filecoin-project/go-state-types/store" + + "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/chain/actors/builtin/miner" + "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/state" + "github.com/filecoin-project/lotus/chain/stmgr" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/wallet/key" + "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/lib/must" +) + +func TestDailyFees(t *testing.T) { + req := require.New(t) + + kit.QuietMiningLogs() + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + const defaultSectorSize = abi.SectorSize(2 << 10) // 2KiB + var ( + blocktime = 2 * time.Millisecond + client kit.TestFullNode + genminer kit.TestMiner + // don't upgrade until our original sectors are fully proven and power updated, to keep the test simple + nv25epoch abi.ChainEpoch = builtin.EpochsInDay + 200 + feePostWg sync.WaitGroup + ) + + initialBigBalance := types.MustParseFIL("100fil").Int64() + sealProofType := must.One(miner.SealProofTypeFromSectorSize(defaultSectorSize, network.Version23, miner.SealProofVariant_Standard)) + rootKey := must.One(key.GenerateKey(types.KTSecp256k1)) + verifierKey := must.One(key.GenerateKey(types.KTSecp256k1)) + verifiedClientKey := must.One(key.GenerateKey(types.KTBLS)) + unverifiedClient := must.One(key.GenerateKey(types.KTBLS)) + + t.Log("*** Setting up network with genesis miner and clients") + + // 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(true), + kit.RootVerifier(rootKey, abi.NewTokenAmount(initialBigBalance)), + kit.Account(verifierKey, abi.NewTokenAmount(initialBigBalance)), + kit.Account(verifiedClientKey, abi.NewTokenAmount(initialBigBalance)), + kit.Account(unverifiedClient, abi.NewTokenAmount(initialBigBalance)), + kit.UpgradeSchedule(stmgr.Upgrade{ + Network: network.Version24, + Height: -1, + }, stmgr.Upgrade{ + Network: network.Version25, + Height: nv25epoch, + Migration: filcns.UpgradeActorsV16, + }, + )). + FullNode(&client, kit.SectorSize(defaultSectorSize)). + Miner(&genminer, &client, kit.PresealSectors(5), kit.SectorSize(defaultSectorSize), kit.WithAllSubsystems()). + Start(). + InterconnectAll() + + store := gstStore.WrapBlockStore(ctx, blockstore.NewAPIBlockstore(client)) + + blockMiners := ens.BeginMiningMustPost(blocktime) + req.Len(blockMiners, 1) + blockMiner := blockMiners[0] + + nodeOpts := []kit.NodeOpt{kit.SectorSize(defaultSectorSize), kit.OwnerAddr(client.DefaultKey)} + mminer, ens := ens.UnmanagedMiner(ctx, &client, nodeOpts...) + defer mminer.Stop() + + // circulatingSupplyBefore gets the circulating supply just before the given tipset key; + // for calculating the fee we need to know the circulating supply that was given to builtin + // actors at the time it was originally calculated, so we need the CS from the tipset before + // the one where the message was executed. + circulatingSupplyBefore := func(tsk types.TipSetKey) abi.TokenAmount { + ts, err := client.ChainGetTipSet(ctx, tsk) + req.NoError(err) + cs, err := client.StateVMCirculatingSupplyInternal(ctx, ts.Parents()) + req.NoError(err) + return cs.FilCirculating + } + + // checkDailyFee checks the daily fee for a sector, returning true if the sector is in the v16 + // format and false if it is in the v15 format. It also returns the daily fee. + checkDailyFee := func(sn abi.SectorNumber) (bool, abi.TokenAmount) { + head, err := client.ChainHead(ctx) + req.NoError(err) + + st, err := state.LoadStateTree(store, head.ParentState()) + req.NoError(err) + + act, err := st.GetActor(mminer.ActorAddr) + req.NoError(err) + + var sectorsArr *adt.Array + { + nv, err := client.StateNetworkVersion(ctx, head.Key()) + req.NoError(err) + switch nv { + case network.Version24: + var miner miner15.State + err = store.Get(ctx, act.Head, &miner) + req.NoError(err) + sectorsArr, err = adt.AsArray(store, miner.Sectors, miner15.SectorsAmtBitwidth) + req.NoError(err) + t.Logf("Got miner15.State: %+v", miner) + case network.Version25: + var miner miner16.State + err = store.Get(ctx, act.Head, &miner) + req.NoError(err) + sectorsArr, err = adt.AsArray(store, miner.Sectors, miner16.SectorsAmtBitwidth) + req.NoError(err) + t.Logf("Got miner16.State: %+v", miner) + default: + t.Fatalf("unexpected network version: %d", nv) + } + } + + // SectorOnChainInfo has a lazy migration for v16, it could take either a 15 field format or a + // 16 field format with a DailyFee field on the end. We want to determine whether its a 15 or a + // 16 field version by first trying to decode it as a 15 field version. + + dailyFee := abi.NewTokenAmount(0) + var v16 bool + + var soci15 miner15.SectorOnChainInfo + ok, err := sectorsArr.Get(uint64(sn), &soci15) + if err == nil { + req.True(ok) + } else { + // try for v16 sector format, the unmarshaller can also handle the 15 field variety so we do + // this second + var soci16 miner16.SectorOnChainInfo + ok, err = sectorsArr.Get(uint64(sn), &soci16) + req.NoError(err) + req.True(ok) + req.NotNil(soci16.DailyFee) + req.NotNil(soci16.DailyFee.Int) + dailyFee = soci16.DailyFee + v16 = true + } + + // call the public API and check that it shows what we know + s, err := client.StateSectorGetInfo(ctx, mminer.ActorAddr, sn, head.Key()) + req.NoError(err) + req.NotNil(s.DailyFee) + req.NotNil(s.DailyFee.Int) + req.Equal(0, big.Cmp(dailyFee, s.DailyFee), + "daily fees not equal: expected %s, got %s", dailyFee, s.DailyFee) + + return v16, dailyFee + } + + // expectMinerBurn asserts that the miner actor has spent the given amount of funds. + // UnmanagedMiner generously gives a large Value in its message submissions to the miner actor, so + // we need to check the miner's balance to see what it currently is, then find all messages sent + // to the miner actor and sum their values to see how much should be in there. Then the remainder + // is the amount that has been burned. + // + // This is not quite how we expect the daily fee mechanism to work in practice: UnmanagedMiner + // doesn't (currently) win any block rewards, so it has no vesting funds to pull from. So instead, + // our daily fee is extracted from the balance. If we had vesting funds, we would need to check + // that as well. + expectMinerBurn := func(expectBurn abi.TokenAmount) { + ts, err := client.ChainHead(ctx) + req.NoError(err) + + // current balance of the actor + act, err := client.StateGetActor(ctx, mminer.ActorAddr, types.EmptyTSK) + req.NoError(err) + minerBalance := act.Balance + + // hunt through the chain and look for messages sent to our miner and extract their value + totalSend := abi.NewTokenAmount(0) + parentTs := ts.Parents() + req.NoError(err) + for ts.Height() > 1 { + msgs, err := client.ChainGetMessagesInTipset(ctx, parentTs) + req.NoError(err) + for _, msg := range msgs { + if msg.Message.To == mminer.ActorAddr { + totalSend = big.Add(totalSend, msg.Message.Value) + } + } + ts, err = client.ChainGetTipSet(ctx, parentTs) + req.NoError(err) + parentTs = ts.Parents() + } + + require.Equal( + t, + expectBurn.Int64(), + big.Sub(totalSend, minerBalance).Int64(), + "expected miner to have burned %s, but it's burned %s", expectBurn, big.Sub(minerBalance, totalSend), + ) + } + + // checkFeeRecords checks that the DailyFee values in the miner's Deadlines and the FeeDeduction + // totals in the ExpirationQueues for each partition are consistent with what the public API + // reports for sector locations and their daily fees. + checkFeeRecords := func(expectTotalFees abi.TokenAmount) { + t.Log("*** Check consistency of deadline fee records and expiration queue fee deduction records") + + // deadline DailyFee values should sum up the live sectors in that deadline + sectors, err := client.StateMinerSectors(ctx, mminer.ActorAddr, nil, types.EmptyTSK) + req.NoError(err) + deadlines, err := client.StateMinerDeadlines(ctx, mminer.ActorAddr, types.EmptyTSK) + req.NoError(err) + expectedDeadlineFees := make([]abi.TokenAmount, len(deadlines)) + expectedExpirationQueueFees := make(map[miner.SectorLocation]abi.TokenAmount, len(deadlines)) + var actualTotalFees abi.TokenAmount + for _, sector := range sectors { + loc, err := client.StateSectorPartition(ctx, mminer.ActorAddr, sector.SectorNumber, types.EmptyTSK) + req.NoError(err) + expectedDeadlineFees[loc.Deadline] = big.Add(expectedDeadlineFees[loc.Deadline], sector.DailyFee) + expectedExpirationQueueFees[*loc] = big.Add(expectedExpirationQueueFees[*loc], sector.DailyFee) + actualTotalFees = big.Add(actualTotalFees, sector.DailyFee) + } + + req.Equal(0, big.Cmp(expectTotalFees, actualTotalFees), "expected total fees %s, got %s", expectTotalFees, actualTotalFees) + + // public API has DailyFee on each Deadline + for i, deadline := range deadlines { + fee := expectedDeadlineFees[i] + req.Equal(0, big.Cmp(fee, deadline.DailyFee), "expected %s, got %s for deadline %d", fee, deadline.DailyFee, i) + } + + nv, err := client.StateNetworkVersion(ctx, types.EmptyTSK) + req.NoError(err) + if nv < network.Version25 { + // nothing to see here, we're done + return + } + + // we need to go deeper for the queues + act, err := client.StateGetActor(ctx, mminer.ActorAddr, types.EmptyTSK) + req.NoError(err) + var minerState miner16.State + err = store.Get(ctx, act.Head, &minerState) + req.NoError(err) + dls, err := minerState.LoadDeadlines(store) + req.NoError(err) + var checkedExpirationQueueFees int + + err = dls.ForEach(store, func(dlIdx uint64, dl *miner16.Deadline) error { + // check the daily fee again (sanity check) + fee := expectedDeadlineFees[dlIdx] + req.Equal(0, big.Cmp(fee, dl.DailyFee), "expected %s, got %s for deadline %d", fee, dl.DailyFee, dlIdx) + if !fee.IsZero() { + t.Logf("checked deadline %d daily fee: expected %s, got %s", dlIdx, fee, dl.DailyFee) + } + + // dive into the partitions + partitions, err := dl.PartitionsArray(store) + req.NoError(err) + quant := minerState.QuantSpecForDeadline(dlIdx) + var partition miner16.Partition + err = partitions.ForEach(&partition, func(i int64) error { + expectedFee, ok := expectedExpirationQueueFees[miner.SectorLocation{Deadline: dlIdx, Partition: uint64(i)}] + if ok { + checkedExpirationQueueFees++ + } else { + // we don't have record of a sector in here, there shouldn't be a reason to go deeper but just in case ... + expectedFee = abi.NewTokenAmount(0) + } + var actualFee abi.TokenAmount + + // hunt through the expiration queue and find all the fees that are set to be deducted for this deadline+partition + queue, err := miner16.LoadExpirationQueue(store, partition.ExpirationsEpochs, quant, miner16.PartitionExpirationAmtBitwidth) + req.NoError(err) + var expSet miner16.ExpirationSet + err = queue.ForEach(&expSet, func(i int64) error { + actualFee = big.Add(actualFee, expSet.FeeDeduction) + return nil + }) + req.NoError(err) + + req.Equal(0, big.Cmp(expectedFee, actualFee), "expected %s, got %s for deadline %d partition %d expiration fee deduction", expectedFee, actualFee, dlIdx, i) + t.Logf("checked deadline %d partition %d expiration fee deduction: expected %s, got %s", dlIdx, i, expectedFee, actualFee) + return nil + }) + req.NoError(err) + + return nil + }) + req.NoError(err) + + req.Equal(len(expectedExpirationQueueFees), checkedExpirationQueueFees, "checked %d expiration queue fees, expected %d", checkedExpirationQueueFees, len(expectedExpirationQueueFees)) + } + + ens.Start() + + t.Log("*** Onboarding sectors before the network upgrade") + expectMinerBurn(abi.NewTokenAmount(0)) + checkFeeRecords(abi.NewTokenAmount(0)) + + _, verifiedClientAddresses := kit.SetupVerifiedClients(ctx, t, &client, rootKey, verifierKey, []*key.Key{verifiedClientKey}) + verifiedClientAddr := verifiedClientAddresses[0] + minerId := must.One(address.IDFromAddress(mminer.ActorAddr)) + + piece := abi.PieceInfo{ + Size: abi.PaddedPieceSize(defaultSectorSize), + PieceCID: cid.MustParse("baga6ea4seaqlhznlutptgfwhffupyer6txswamerq5fc2jlwf2lys2mm5jtiaeq"), + } + clientId, allocationId := kit.SetupAllocation(ctx, t, &client, minerId, piece, verifiedClientAddr, 0, 0) + + var allSectors []abi.SectorNumber + ccSectors24, _ := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{{}, {}}) // 2 CC sectors + allSectors = append(allSectors, ccSectors24...) + dealSector24, _ := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{kit.SectorWithRandPiece()}) + allSectors = append(allSectors, dealSector24...) + verifiedSector24, _ := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{ + {Piece: piece.PieceCID, Verified: &miner14.VerifiedAllocationKey{Client: clientId, ID: verifreg14.AllocationId(allocationId)}}}) + allSectors = append(allSectors, verifiedSector24...) + + blockMiner.WatchMinerForPost(mminer.ActorAddr) + + t.Log("*** Checking daily fees on sectors onboarded before the network upgrade, before their first PoST") + + // No fees, no fee information at all in these sectors (sanity check) + for _, sn := range allSectors { + has, fee := checkDailyFee(sn) + req.False(has) // v15 + req.Equal(abi.NewTokenAmount(0), fee) + } + expectMinerBurn(abi.NewTokenAmount(0)) + checkFeeRecords(abi.NewTokenAmount(0)) + + t.Log("*** Waiting for PoST for sectors onboarded before the network upgrade") + + expectedRaw := uint64(defaultSectorSize * 4) // 4 sectors onboarded + expectedQap := uint64(defaultSectorSize * 13) // 3 sectors + 1 verified sector + mminer.WaitTillActivatedAndAssertPower(allSectors, expectedRaw, expectedQap) + + t.Log("*** Checking daily fees on sectors onboarded before the network upgrade, after their first PoST") + + // PoST shouldn't have changed anything + for _, sn := range allSectors { + has, fee := checkDailyFee(sn) + req.False(has) // v15 + req.Equal(abi.NewTokenAmount(0), fee) + } + expectMinerBurn(abi.NewTokenAmount(0)) + checkFeeRecords(abi.NewTokenAmount(0)) + + // Move past the upgrade + client.WaitTillChain(ctx, kit.HeightAtLeast(nv25epoch+5)) + + t.Log("*** Re-checking daily fees on sectors onboarded before the network upgrade") + + // Still no fees, sectors shouldn't have been touched + for _, sn := range allSectors { + has, fee := checkDailyFee(sn) + req.False(has) // v15 + req.Equal(abi.NewTokenAmount(0), fee) + } + expectMinerBurn(abi.NewTokenAmount(0)) + checkFeeRecords(abi.NewTokenAmount(0)) + + t.Log("*** Snapping deals into sectors after the network upgrade") + + // Snap both CC sectors, one with an unverified piece, one with a verified piece, capture the + // CS value at each snap so we can accurately predict the expected daily fee + _, snap0Tsk := mminer.SnapDeal(ccSectors24[0], kit.SectorManifest{Piece: piece.PieceCID}) + ccSectors240ExpectedFee := miner16.DailyProofFee(circulatingSupplyBefore(snap0Tsk), abi.NewStoragePower(int64(defaultSectorSize))) + clientId, allocationId = kit.SetupAllocation(ctx, t, &client, minerId, piece, verifiedClientAddr, 0, 0) + _, snap1Tsk := mminer.SnapDeal(ccSectors24[1], kit.SectorManifest{Piece: piece.PieceCID, Verified: &miner14.VerifiedAllocationKey{Client: clientId, ID: verifreg14.AllocationId(allocationId)}}) + ccSectors241ExpectedFee := miner16.DailyProofFee(circulatingSupplyBefore(snap1Tsk), abi.NewStoragePower(int64(defaultSectorSize*10))) + + feePostWg.Add(1) + go func() { + mminer.WaitTillPost(ccSectors24[0]) // both onboarded together, so they should post together + feePostWg.Done() + }() + + t.Log("*** Checking daily fees on old and snapped sectors") + + // No fees on untouched sectors, but because we expect all our sectors to be stored in the root + // of the HAMT together and we've modified at least one of them, they would have all been + // rewritten in the new v16 format, so we shouldn't see v15's here anymore. + noFeeSectors := append(append([]abi.SectorNumber{}, dealSector24...), verifiedSector24...) + for _, sn := range noFeeSectors { + has, fee := checkDailyFee(sn) + req.True(has) // v16 + req.Equal(abi.NewTokenAmount(0), fee) + } + + // fees on snapped sectors, first our non-verified sector, then our verified sector, with 10x qap + has, fee := checkDailyFee(ccSectors24[0]) + req.True(has) + req.Equal(ccSectors240ExpectedFee, fee) + has, fee = checkDailyFee(ccSectors24[1]) + req.True(has) + req.Equal(ccSectors241ExpectedFee, fee) + + expectMinerBurn(abi.NewTokenAmount(0)) // fees are registered, but not yet paid + checkFeeRecords(big.Add(ccSectors240ExpectedFee, ccSectors241ExpectedFee)) + + t.Log("*** Onboarding sectors after the network upgrade") + + clientId, allocationId = kit.SetupAllocation(ctx, t, &client, minerId, piece, verifiedClientAddr, 0, 0) + + ccSectors25, ccTsk := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{{}, {}}) // 2 CC sectors + allSectors = append(allSectors, ccSectors25...) + ccSectors25ExpectedFee := miner16.DailyProofFee(circulatingSupplyBefore(ccTsk), abi.NewStoragePower(int64(defaultSectorSize))) + feePostWg.Add(1) + go func() { + mminer.WaitTillPost(ccSectors25[0]) // both onboarded together, so they should post together + feePostWg.Done() + }() + + dealSector25, dealTsk := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{kit.SectorWithRandPiece()}) + allSectors = append(allSectors, dealSector25...) + dealSector25ExpectedFee := miner16.DailyProofFee(circulatingSupplyBefore(dealTsk), abi.NewStoragePower(int64(defaultSectorSize))) + feePostWg.Add(1) + go func() { + mminer.WaitTillPost(dealSector25[0]) + feePostWg.Done() + }() + + verifiedSector25, verifiedTsk := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{ + {Piece: piece.PieceCID, Verified: &miner14.VerifiedAllocationKey{Client: clientId, ID: verifreg14.AllocationId(allocationId)}}}) + allSectors = append(allSectors, verifiedSector25...) + verified25ExpectedFee := miner16.DailyProofFee(circulatingSupplyBefore(verifiedTsk), abi.NewStoragePower(int64(defaultSectorSize*10))) + feePostWg.Add(1) + go func() { + mminer.WaitTillPost(verifiedSector25[0]) + feePostWg.Done() + }() + + checkAllFees := func() { + t.Log("*** Checking daily fees on old sectors") + + for _, sn := range noFeeSectors { + has, fee := checkDailyFee(sn) + req.True(has) // v16 + req.Equal(abi.NewTokenAmount(0), fee) + } + + t.Log("*** Re-checking daily fees on snapped sectors") + + has, fee = checkDailyFee(ccSectors24[0]) + req.True(has) + req.Equal(ccSectors240ExpectedFee, fee) + has, fee = checkDailyFee(ccSectors24[1]) + req.True(has) + req.Equal(ccSectors241ExpectedFee, fee) + + t.Log("*** Checking daily fees on new sectors") + + has, fee = checkDailyFee(ccSectors25[0]) + req.True(has) + req.Equal(ccSectors25ExpectedFee, fee) + has, fee = checkDailyFee(ccSectors25[1]) + req.True(has) + req.Equal(ccSectors25ExpectedFee, fee) + has, fee = checkDailyFee(dealSector25[0]) + req.True(has) + req.Equal(dealSector25ExpectedFee, fee) + has, fee = checkDailyFee(verifiedSector25[0]) + req.True(has) + req.Equal(verified25ExpectedFee, fee) + } + + checkAllFees() // before PoST + + t.Log("*** Waiting for PoST for sectors onboarded after the network upgrade") + + expectedRaw = uint64(defaultSectorSize * 8) // 8 sectors onboarded + expectedQap = uint64(defaultSectorSize * 35) // 5 sectors + 3 (incl 1 snap) verified sectors + mminer.WaitTillActivatedAndAssertPower(allSectors, expectedRaw, expectedQap) + + checkAllFees() // after PoST + + // wait for all fees to be paid—we need each one to have reached its first deadline and they are + // likely spread out over multiple deadlines + feePostWg.Wait() + head, err := client.ChainHead(ctx) + req.NoError(err) + // wait one deadline to make sure we get to the end of the current deadline where we've done a + // PoST. + // NOTE: we are crossing our fingers a little here, it is possible that our snapped sectors end + // up hitting two proving deadlines if we didn't compress our nv25 onboarding enough and get + // allocated to nicely aligned deadlines. + client.WaitTillChain(ctx, kit.HeightAtLeast(head.Height()+miner.WPoStChallengeWindow())) + + var expectTotalBurn abi.TokenAmount + for _, fee := range []abi.TokenAmount{ + ccSectors240ExpectedFee, + ccSectors241ExpectedFee, + ccSectors25ExpectedFee, // x2 + ccSectors25ExpectedFee, + dealSector25ExpectedFee, + verified25ExpectedFee, + } { + expectTotalBurn = big.Add(expectTotalBurn, fee) + } + // we've passed our first deadline where fees were payable, both for the snapped nv24 sectors + // and the nv25 sectors + expectMinerBurn(expectTotalBurn) + // with only one fee payment so far for all sectors, the total in the records should be the same as the burn + checkFeeRecords(expectTotalBurn) +} diff --git a/itests/direct_data_onboard_test.go b/itests/direct_data_onboard_test.go index 071b441cc0e..bf83e0f1bdf 100644 --- a/itests/direct_data_onboard_test.go +++ b/itests/direct_data_onboard_test.go @@ -21,6 +21,7 @@ import ( "github.com/filecoin-project/go-commp-utils/v2" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" + miner16 "github.com/filecoin-project/go-state-types/builtin/v16/miner" market2 "github.com/filecoin-project/go-state-types/builtin/v9/market" "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/network" @@ -252,9 +253,16 @@ func TestOnboardMixedMarketDDO(t *testing.T) { ds, err := client.StateMarketStorageDeal(ctx, dealID, types.EmptyTSK) require.NoError(t, err) - require.NotEqual(t, -1, ds.State.SectorStartEpoch) + // verify that the daily fee was set up correctly + soci, err := client.StateSectorGetInfo(ctx, miner.ActorAddr, si.SectorID, types.EmptyTSK) + require.NoError(t, err) + cs, err := client.StateVMCirculatingSupplyInternal(ctx, types.EmptyTSK) + require.NoError(t, err) + expectedDailyFee := miner16.DailyProofFee(cs.FilCirculating, abi.NewStoragePower(2048)) + require.Equal(t, expectedDailyFee, soci.DailyFee) + { deals, err := client.StateMarketDeals(ctx, types.EmptyTSK) require.NoError(t, err) diff --git a/itests/direct_data_onboard_verified_test.go b/itests/direct_data_onboard_verified_test.go index 53e3b3bade0..48bd0f05296 100644 --- a/itests/direct_data_onboard_verified_test.go +++ b/itests/direct_data_onboard_verified_test.go @@ -25,12 +25,11 @@ import ( "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/builtin" verifregtypes13 "github.com/filecoin-project/go-state-types/builtin/v13/verifreg" - datacap2 "github.com/filecoin-project/go-state-types/builtin/v9/datacap" + miner16 "github.com/filecoin-project/go-state-types/builtin/v16/miner" lapi "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api/v1api" "github.com/filecoin-project/lotus/chain/actors" - "github.com/filecoin-project/lotus/chain/actors/builtin/datacap" minertypes "github.com/filecoin-project/lotus/chain/actors/builtin/miner" "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" "github.com/filecoin-project/lotus/chain/types" @@ -51,22 +50,15 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { ctx = context.Background() ) - rootKey, err := key.GenerateKey(types.KTSecp256k1) - require.NoError(t, err) - - verifierKey, err := key.GenerateKey(types.KTSecp256k1) - require.NoError(t, err) - - verifiedClientKey, err := key.GenerateKey(types.KTBLS) - require.NoError(t, err) - - bal, err := types.ParseFIL("100fil") - require.NoError(t, err) + initialBigBalance := types.MustParseFIL("100fil").Int64() + rootKey := must.One(key.GenerateKey(types.KTSecp256k1)) + verifierKey := must.One(key.GenerateKey(types.KTSecp256k1)) + verifiedClientKey := must.One(key.GenerateKey(types.KTBLS)) client, miner, ens := kit.EnsembleMinimal(t, kit.ThroughRPC(), - kit.RootVerifier(rootKey, abi.NewTokenAmount(bal.Int64())), - kit.Account(verifierKey, abi.NewTokenAmount(bal.Int64())), - kit.Account(verifiedClientKey, abi.NewTokenAmount(bal.Int64())), + kit.RootVerifier(rootKey, abi.NewTokenAmount(initialBigBalance)), + kit.Account(verifierKey, abi.NewTokenAmount(initialBigBalance)), + kit.Account(verifiedClientKey, abi.NewTokenAmount(initialBigBalance)), ) /* --- Setup subscription channels for ActorEvents --- */ @@ -109,8 +101,8 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { /* --- Setup verified registry and client and allocate datacap to client */ - verifierAddr, verifiedClientAddrses := ddoVerifiedSetupVerifiedClient(ctx, t, client, rootKey, verifierKey, []*key.Key{verifiedClientKey}) - verifiedClientAddr := verifiedClientAddrses[0] + verifierAddr, verifiedClientAddresses := kit.SetupVerifiedClients(ctx, t, client, rootKey, verifierKey, []*key.Key{verifiedClientKey}) + verifiedClientAddr := verifiedClientAddresses[0] /* --- Prepare piece for onboarding --- */ @@ -124,8 +116,15 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { /* --- Allocate datacap for the piece by the verified client --- */ head, err := client.ChainHead(ctx) require.NoError(t, err) + // but first, a detour to an expiring claim so we can observe the events bogusAllocationExpiry := head.Height() + 100 - clientId, allocationId := ddoVerifiedSetupAllocations(ctx, t, client, minerId, dc, verifiedClientAddr, bogusAllocationExpiry, 0) + bogusPiece := abi.PieceInfo{ + Size: dc.Size, + PieceCID: bogusPieceCid, + } + _, _ = kit.SetupAllocation(ctx, t, client, minerId, bogusPiece, verifiedClientAddr, bogusAllocationExpiry, 0) + // actual claim + clientId, allocationId := kit.SetupAllocation(ctx, t, client, minerId, dc, verifiedClientAddr, 0, 0) head, err = client.ChainHead(ctx) require.NoError(t, err) @@ -141,6 +140,19 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { so, si := ddoVerifiedOnboardPiece(ctx, t, miner, clientId, allocationId, dc, pieceData) + /* --- Verify that the daily fee was set up correctly --- */ + + soci, err := client.StateSectorGetInfo(ctx, miner.ActorAddr, si.SectorID, types.EmptyTSK) + require.NoError(t, err) + // get CS @ the activation epoch + activationTs, err := client.ChainGetTipSetByHeight(ctx, soci.Activation, types.EmptyTSK) + require.NoError(t, err) + cs, err := client.StateVMCirculatingSupplyInternal(ctx, activationTs.Key()) + require.NoError(t, err) + qap := abi.NewStoragePower(int64(pieceSize.Padded()) * 10) // 10x power + expectedDailyFee := miner16.DailyProofFee(cs.FilCirculating, qap) + require.Equal(t, expectedDailyFee, soci.DailyFee) + // check that we have one allocation because the real allocation has been claimed by the miner for the piece allocations, err := client.StateGetAllocations(ctx, verifiedClientAddr, types.EmptyTSK) require.NoError(t, err) @@ -416,100 +428,6 @@ func TestOnboardRawPieceVerified_WithActorEvents(t *testing.T) { // speed than the ActorEventHandler is aware of. } -func ddoVerifiedSetupAllocations( - ctx context.Context, - t *testing.T, - node v1api.FullNode, - minerId uint64, - dc abi.PieceInfo, - verifiedClientAddr address.Address, - bogusAllocExpiration abi.ChainEpoch, // zero if we don't want to make one - tmax abi.ChainEpoch, -) (clientID abi.ActorID, allocationID verifregtypes13.AllocationId) { - if tmax == 0 { - tmax = verifreg.MaximumVerifiedAllocationTerm - } - - var requests []verifreg.AllocationRequest - - if bogusAllocExpiration != 0 { - // design this one to expire so we can observe allocation-removed - allocationRequestBogus := verifreg.AllocationRequest{ - Provider: abi.ActorID(minerId), - Data: bogusPieceCid, - Size: dc.Size, - TermMin: verifreg.MinimumVerifiedAllocationTerm, - TermMax: tmax, - Expiration: bogusAllocExpiration, - } - requests = append(requests, allocationRequestBogus) - } - - allocationRequest := verifreg.AllocationRequest{ - Provider: abi.ActorID(minerId), - Data: dc.PieceCID, - Size: dc.Size, - TermMin: verifreg.MinimumVerifiedAllocationTerm, - TermMax: tmax, - Expiration: verifreg.MaximumVerifiedAllocationExpiration, - } - requests = append(requests, allocationRequest) - - allocationRequests := verifreg.AllocationRequests{ - Allocations: requests, - } - - receiverParams, aerr := actors.SerializeParams(&allocationRequests) - require.NoError(t, aerr) - - var amt abi.TokenAmount - amt = big.Mul(big.NewInt(int64(dc.Size)), builtin.TokenPrecision) - if bogusAllocExpiration != 0 { - amt = big.Mul(big.NewInt(int64(dc.Size*2)), builtin.TokenPrecision) - } - - transferParams, aerr := actors.SerializeParams(&datacap2.TransferParams{ - To: builtin.VerifiedRegistryActorAddr, - Amount: amt, - OperatorData: receiverParams, - }) - require.NoError(t, aerr) - - msg := &types.Message{ - To: builtin.DatacapActorAddr, - From: verifiedClientAddr, - Method: datacap.Methods.TransferExported, - Params: transferParams, - Value: big.Zero(), - } - - sm, err := node.MpoolPushMessage(ctx, msg, nil) - require.NoError(t, err) - - res, err := node.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) - require.NoError(t, err) - require.EqualValues(t, 0, res.Receipt.ExitCode) - - // check that we have an allocation - allocations, err := node.StateGetAllocations(ctx, verifiedClientAddr, types.EmptyTSK) - require.NoError(t, err) - if bogusAllocExpiration != 0 { - require.Len(t, allocations, 2) // allocation waiting to be claimed - } else { - require.Len(t, allocations, 1) // allocation waiting to be claimed - } - - for key, value := range allocations { - if value.Data == dc.PieceCID { - allocationID = verifregtypes13.AllocationId(key) - clientID = value.Client - break - } - } - require.NotEqual(t, verifreg.AllocationId(0), allocationID) // found it in there - return clientID, allocationID -} - func ddoVerifiedOnboardPiece(ctx context.Context, t *testing.T, miner *kit.TestMiner, clientId abi.ActorID, allocationId verifregtypes13.AllocationId, dc abi.PieceInfo, pieceData []byte) (lapi.SectorOffset, lapi.SectorInfo) { head, err := miner.FullNode.ChainHead(ctx) require.NoError(t, err) @@ -648,71 +566,6 @@ func ddoVerifiedBuildActorEventsFromMessages(ctx context.Context, t *testing.T, return actorEvents } -func ddoVerifiedSetupVerifiedClient(ctx context.Context, t *testing.T, client *kit.TestFullNode, rootKey *key.Key, verifierKey *key.Key, verifiedClientKeys []*key.Key) (verifierAddr address.Address, ret []address.Address) { - // import the root key. - rootAddr, err := client.WalletImport(ctx, &rootKey.KeyInfo) - require.NoError(t, err) - - // import the verifiers' keys. - verifierAddr, err = client.WalletImport(ctx, &verifierKey.KeyInfo) - require.NoError(t, err) - - // import the verified client's key. - for _, k := range verifiedClientKeys { - verifiedClientAddr, err := client.WalletImport(ctx, &k.KeyInfo) - require.NoError(t, err) - ret = append(ret, verifiedClientAddr) - } - - allowance := big.NewInt(100000000000) - params, aerr := actors.SerializeParams(&verifreg.AddVerifierParams{Address: verifierAddr, Allowance: allowance}) - require.NoError(t, aerr) - - msg := &types.Message{ - From: rootAddr, - To: verifreg.Address, - Method: verifreg.Methods.AddVerifier, - Params: params, - Value: big.Zero(), - } - - sm, err := client.MpoolPushMessage(ctx, msg, nil) - require.NoError(t, err, "AddVerifier failed") - - res, err := client.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) - require.NoError(t, err) - require.EqualValues(t, 0, res.Receipt.ExitCode) - - verifierAllowance, err := client.StateVerifierStatus(ctx, verifierAddr, types.EmptyTSK) - require.NoError(t, err) - require.Equal(t, allowance, *verifierAllowance) - - // assign datacap to clients - for _, ad := range ret { - initialDatacap := big.NewInt(10000) - - params, aerr = actors.SerializeParams(&verifreg.AddVerifiedClientParams{Address: ad, Allowance: initialDatacap}) - require.NoError(t, aerr) - - msg = &types.Message{ - From: verifierAddr, - To: verifreg.Address, - Method: verifreg.Methods.AddVerifiedClient, - Params: params, - Value: big.Zero(), - } - - sm, err = client.MpoolPushMessage(ctx, msg, nil) - require.NoError(t, err) - - res, err = client.StateWaitMsg(ctx, sm.Cid(), 1, lapi.LookbackNoLimit, true) - require.NoError(t, err) - require.EqualValues(t, 0, res.Receipt.ExitCode) - } - - return -} - func filterEvents(events []*types.ActorEvent, key string) []*types.ActorEvent { keyBytes := must.One(ipld.Encode(basicnode.NewString(key), dagcbor.Encode)) filtered := make([]*types.ActorEvent, 0) @@ -770,30 +623,19 @@ func TestVerifiedDDOExtendClaim(t *testing.T) { ctx = context.Background() ) - rootKey, err := key.GenerateKey(types.KTSecp256k1) - require.NoError(t, err) - - verifierKey, err := key.GenerateKey(types.KTSecp256k1) - require.NoError(t, err) - - verifiedClientKey1, err := key.GenerateKey(types.KTBLS) - require.NoError(t, err) - - verifiedClientKey2, err := key.GenerateKey(types.KTBLS) - require.NoError(t, err) - - unverifiedClient, err := key.GenerateKey(types.KTBLS) - require.NoError(t, err) - - bal, err := types.ParseFIL("100fil") - require.NoError(t, err) + initialBigBalance := types.MustParseFIL("100fil").Int64() + rootKey := must.One(key.GenerateKey(types.KTSecp256k1)) + verifierKey := must.One(key.GenerateKey(types.KTSecp256k1)) + verifiedClientKey1 := must.One(key.GenerateKey(types.KTBLS)) + verifiedClientKey2 := must.One(key.GenerateKey(types.KTBLS)) + unverifiedClient := must.One(key.GenerateKey(types.KTBLS)) client, miner, ens := kit.EnsembleMinimal(t, kit.ThroughRPC(), - kit.RootVerifier(rootKey, abi.NewTokenAmount(bal.Int64())), - kit.Account(verifierKey, abi.NewTokenAmount(bal.Int64())), - kit.Account(verifiedClientKey1, abi.NewTokenAmount(bal.Int64())), - kit.Account(verifiedClientKey2, abi.NewTokenAmount(bal.Int64())), - kit.Account(unverifiedClient, abi.NewTokenAmount(bal.Int64())), + kit.RootVerifier(rootKey, abi.NewTokenAmount(initialBigBalance)), + kit.Account(verifierKey, abi.NewTokenAmount(initialBigBalance)), + kit.Account(verifiedClientKey1, abi.NewTokenAmount(initialBigBalance)), + kit.Account(verifiedClientKey2, abi.NewTokenAmount(initialBigBalance)), + kit.Account(unverifiedClient, abi.NewTokenAmount(initialBigBalance)), ) /* --- Start mining --- */ @@ -805,9 +647,9 @@ func TestVerifiedDDOExtendClaim(t *testing.T) { /* --- Setup verified registry and clients and allocate datacap to client */ - _, verifiedClientAddrses := ddoVerifiedSetupVerifiedClient(ctx, t, client, rootKey, verifierKey, []*key.Key{verifiedClientKey1, verifiedClientKey2}) - verifiedClientAddr1 := verifiedClientAddrses[0] - verifiedClientAddr2 := verifiedClientAddrses[1] + _, verifiedClientAddresses := kit.SetupVerifiedClients(ctx, t, client, rootKey, verifierKey, []*key.Key{verifiedClientKey1, verifiedClientKey2}) + verifiedClientAddr1 := verifiedClientAddresses[0] + verifiedClientAddr2 := verifiedClientAddresses[1] /* --- Prepare piece for onboarding --- */ @@ -819,7 +661,7 @@ func TestVerifiedDDOExtendClaim(t *testing.T) { require.NoError(t, err) /* --- Allocate datacap for the piece by the verified client --- */ - clientId, allocationId := ddoVerifiedSetupAllocations(ctx, t, client, minerId, dc, verifiedClientAddr1, 0, builtin.EpochsInYear*3) + clientId, allocationId := kit.SetupAllocation(ctx, t, client, minerId, dc, verifiedClientAddr1, 0, builtin.EpochsInYear*3) /* --- Onboard the piece --- */ diff --git a/itests/kit/funds.go b/itests/kit/funds.go index 1918d9125ff..9a92348016f 100644 --- a/itests/kit/funds.go +++ b/itests/kit/funds.go @@ -9,9 +9,17 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" + "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/builtin" + verifregtypes13 "github.com/filecoin-project/go-state-types/builtin/v13/verifreg" + datacap2 "github.com/filecoin-project/go-state-types/builtin/v9/datacap" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/builtin/datacap" + "github.com/filecoin-project/lotus/chain/actors/builtin/verifreg" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/wallet/key" ) // SendFunds sends funds from the default wallet of the specified sender node @@ -38,3 +46,146 @@ func (f *TestFullNode) WaitMsg(ctx context.Context, msg cid.Cid) { require.EqualValues(f.t, 0, res.Receipt.ExitCode, "message did not successfully execute") } + +// SetupVerifiedClients assumes that rootKey has been set in the ensemble's genesis and that +// verifierKey and verifiedClientKeys exist as accounts. It first sets up the verifier with datacap +// to allocate, and then allocates datacap to each of the verified clients. +// It returns the address of the verifier and the addresses of the verified clients. +func SetupVerifiedClients(ctx context.Context, t *testing.T, client *TestFullNode, rootKey *key.Key, verifierKey *key.Key, verifiedClientKeys []*key.Key) (verifierAddr address.Address, ret []address.Address) { + // import the root key. + rootAddr, err := client.WalletImport(ctx, &rootKey.KeyInfo) + require.NoError(t, err) + + // import the verifiers' keys. + verifierAddr, err = client.WalletImport(ctx, &verifierKey.KeyInfo) + require.NoError(t, err) + + // import the verified client's key. + for _, k := range verifiedClientKeys { + verifiedClientAddr, err := client.WalletImport(ctx, &k.KeyInfo) + require.NoError(t, err) + ret = append(ret, verifiedClientAddr) + } + + allowance := big.NewInt(100000000000) + params, aerr := actors.SerializeParams(&verifreg.AddVerifierParams{Address: verifierAddr, Allowance: allowance}) + require.NoError(t, aerr) + + msg := &types.Message{ + From: rootAddr, + To: verifreg.Address, + Method: verifreg.Methods.AddVerifier, + Params: params, + Value: big.Zero(), + } + + sm, err := client.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err, "AddVerifier failed") + + res, err := client.StateWaitMsg(ctx, sm.Cid(), 1, api.LookbackNoLimit, true) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) + + verifierAllowance, err := client.StateVerifierStatus(ctx, verifierAddr, types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, allowance, *verifierAllowance) + + // assign datacap to clients + for _, ad := range ret { + initialDatacap := big.NewInt(10000) + + params, aerr = actors.SerializeParams(&verifreg.AddVerifiedClientParams{Address: ad, Allowance: initialDatacap}) + require.NoError(t, aerr) + + msg = &types.Message{ + From: verifierAddr, + To: verifreg.Address, + Method: verifreg.Methods.AddVerifiedClient, + Params: params, + Value: big.Zero(), + } + + sm, err = client.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + + res, err = client.StateWaitMsg(ctx, sm.Cid(), 1, api.LookbackNoLimit, true) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) + } + + return +} + +func SetupAllocation( + ctx context.Context, + t *testing.T, + node api.FullNode, + minerId uint64, + dc abi.PieceInfo, + verifiedClientAddr address.Address, + expiration abi.ChainEpoch, // set to zero if we want to use maximum + termMax abi.ChainEpoch, // set to zero if we want to use maximum +) (clientID abi.ActorID, allocationID verifregtypes13.AllocationId) { + if termMax == 0 { + termMax = verifreg.MaximumVerifiedAllocationTerm + } + if expiration == 0 { + expiration = verifreg.MaximumVerifiedAllocationExpiration + } + + var requests []verifreg.AllocationRequest + + allocationRequest := verifreg.AllocationRequest{ + Provider: abi.ActorID(minerId), + Data: dc.PieceCID, + Size: dc.Size, + TermMin: verifreg.MinimumVerifiedAllocationTerm, + TermMax: termMax, + Expiration: expiration, + } + requests = append(requests, allocationRequest) + + allocationRequests := verifreg.AllocationRequests{ + Allocations: requests, + } + + receiverParams, aerr := actors.SerializeParams(&allocationRequests) + require.NoError(t, aerr) + + transferParams, aerr := actors.SerializeParams(&datacap2.TransferParams{ + To: builtin.VerifiedRegistryActorAddr, + Amount: big.Mul(big.NewInt(int64(dc.Size)), builtin.TokenPrecision), + OperatorData: receiverParams, + }) + require.NoError(t, aerr) + + msg := &types.Message{ + To: builtin.DatacapActorAddr, + From: verifiedClientAddr, + Method: datacap.Methods.TransferExported, + Params: transferParams, + Value: big.Zero(), + } + + sm, err := node.MpoolPushMessage(ctx, msg, nil) + require.NoError(t, err) + + res, err := node.StateWaitMsg(ctx, sm.Cid(), 1, api.LookbackNoLimit, true) + require.NoError(t, err) + require.EqualValues(t, 0, res.Receipt.ExitCode) + + // check that we have an allocation + allocations, err := node.StateGetAllocations(ctx, verifiedClientAddr, types.EmptyTSK) + require.NoError(t, err) + + for key, value := range allocations { + vkey := verifregtypes13.AllocationId(key) + if dc.PieceCID.Equals(value.Data) && vkey >= allocationID { + allocationID = verifregtypes13.AllocationId(key) + clientID = value.Client + } + } + + require.NotEqual(t, verifreg.AllocationId(0), allocationID) // found it in there + return clientID, allocationID +} diff --git a/itests/kit/node_unmanaged.go b/itests/kit/node_unmanaged.go index 1771bea3ff2..7b557d36c4d 100644 --- a/itests/kit/node_unmanaged.go +++ b/itests/kit/node_unmanaged.go @@ -38,8 +38,8 @@ import ( "github.com/filecoin-project/lotus/chain/wallet/key" ) -// TODO: this shouldn't be fixed, we should make a new one for each sector/piece -var fixedPieceCid = cid.MustParse("baga6ea4seaqjtovkwk4myyzj56eztkh5pzsk5upksan6f5outesy62bsvl4dsha") +// TODO: make a randomiser for this +var rndPieceCid = cid.MustParse("baga6ea4seaqjtovkwk4myyzj56eztkh5pzsk5upksan6f5outesy62bsvl4dsha") // 32 bytes of 1's: this value essentially ignored in NI-PoRep proofs, but all zeros is not recommended. // Regardless of what we submit to the chain, actors will replace it with 32 1's anyway but we are @@ -88,11 +88,22 @@ type sectorInfo struct { sealedCid cid.Cid unsealedCid cid.Cid sectorProof []byte - pieces []abi.PieceInfo + pieces []miner14.PieceActivationManifest sealTickets abi.SealRandomness sealRandomnessEpoch abi.ChainEpoch } +func (si sectorInfo) piecesToPieceInfos() []abi.PieceInfo { + pcPieces := make([]abi.PieceInfo, len(si.pieces)) + for i, piece := range si.pieces { + pcPieces[i] = abi.PieceInfo{ + Size: piece.Size, + PieceCID: piece.CID, + } + } + return pcPieces +} + type windowPost struct { Posted []abi.SectorNumber Epoch abi.ChainEpoch @@ -188,6 +199,20 @@ func WithExpectedExitCodes(exitCodes []exitcode.ExitCode) OnboardOpt { } } +// SectorManifest is (currently) a simplified SectorAllocationManifest, allowing zero or one pieces +// to be built into a sector, where that one piece may be verified. Leave blank to onboard a CC +// sector. +type SectorManifest struct { + Piece cid.Cid + Verified *miner14.VerifiedAllocationKey +} + +func SectorWithRandPiece() SectorManifest { + return SectorManifest{ + Piece: rndPieceCid, + } +} + // OnboardSectors onboards the specified number of sectors to the miner using the specified proof // type. If `withPieces` is true and the proof type supports it, the sectors will be onboarded with // pieces, otherwise they will be CC. @@ -196,10 +221,9 @@ func WithExpectedExitCodes(exitCodes []exitcode.ExitCode) OnboardOpt { // process with real proofs. func (tm *TestUnmanagedMiner) OnboardSectors( proofType abi.RegisteredSealProof, - withPieces bool, - count int, + manifest []SectorManifest, opts ...OnboardOpt, -) []abi.SectorNumber { +) ([]abi.SectorNumber, types.TipSetKey) { req := require.New(tm.t) @@ -208,7 +232,7 @@ func (tm *TestUnmanagedMiner) OnboardSectors( req.NoError(o(&options)) } - sectors := make([]sectorInfo, count) + sectors := make([]sectorInfo, len(manifest)) // Wait for the seal randomness to be available (we can only draw seal randomness from // tipsets that have already achieved finality) @@ -219,8 +243,7 @@ func (tm *TestUnmanagedMiner) OnboardSectors( // For each sector, run PC1, PC2, C1 an C2, preparing for ProveCommit. If the proof needs it we // will also submit a precommit for the sector. - for i := 0; i < count; i++ { - idx := i + for idx, sm := range manifest { // We hold on to `sector`, adding new properties to it as we go along until we're finished with // this phase, then add it to `sectors` @@ -228,12 +251,13 @@ func (tm *TestUnmanagedMiner) OnboardSectors( sector.sealRandomnessEpoch = sealRandEpoch eg.Go(func() error { - if withPieces { + if sm.Piece.Defined() { // Build a sector with non-zero pieces to onboard if tm.mockProofs { - sector.pieces = []abi.PieceInfo{{ - Size: abi.PaddedPieceSize(tm.options.sectorSize), - PieceCID: fixedPieceCid, + sector.pieces = []miner14.PieceActivationManifest{{ + Size: abi.PaddedPieceSize(tm.options.sectorSize), + CID: sm.Piece, + VerifiedAllocationKey: sm.Verified, }} } else { var err error @@ -282,7 +306,7 @@ func (tm *TestUnmanagedMiner) OnboardSectors( } // Submit ProveCommit for all sectors - exitCodes := tm.submitProveCommit(proofType, sectors, options.requireActivationSuccess, options.modifyNIActivationsBeforeSubmit) + exitCodes, tsk := tm.submitProveCommit(proofType, sectors, options.requireActivationSuccess, options.modifyNIActivationsBeforeSubmit) // ProveCommit may have succeeded overall, but some sectors may have failed if RequireActivationSuccess // was set to false. We need to return the exit codes for each sector so the caller can determine @@ -305,12 +329,13 @@ func (tm *TestUnmanagedMiner) OnboardSectors( tm.wdPostLoop() - return onboarded + return onboarded, tsk } // SnapDeal snaps a deal into a sector, generating a new sealed sector and updating the sector's state. // WindowPoSt should continue to operate after this operation if required. -func (tm *TestUnmanagedMiner) SnapDeal(sectorNumber abi.SectorNumber) []abi.PieceInfo { +// The SectorManifest argument (currently) only impacts mock proofs, and is ignored otherwise. +func (tm *TestUnmanagedMiner) SnapDeal(sectorNumber abi.SectorNumber, sm SectorManifest) ([]abi.PieceInfo, types.TipSetKey) { req := require.New(tm.t) tm.log("Snapping a deal into sector %d ...", sectorNumber) @@ -355,7 +380,7 @@ func (tm *TestUnmanagedMiner) SnapDeal(sectorNumber abi.SectorNumber) []abi.Piec } else { pieces = []abi.PieceInfo{{ Size: abi.PaddedPieceSize(tm.options.sectorSize), - PieceCID: cid.MustParse("baga6ea4seaqlhznlutptgfwhffupyer6txswamerq5fc2jlwf2lys2mm5jtiaeq"), + PieceCID: sm.Piece, }} snapProof = []byte{0xde, 0xad, 0xbe, 0xef} newSealedCid = cid.MustParse("bagboea4b5abcatlxechwbp7kjpjguna6r6q7ejrhe6mdp3lf34pmswn27pkkieka") @@ -365,10 +390,14 @@ func (tm *TestUnmanagedMiner) SnapDeal(sectorNumber abi.SectorNumber) []abi.Piec var manifest []miner14.PieceActivationManifest for _, piece := range pieces { - manifest = append(manifest, miner14.PieceActivationManifest{ + pm := miner14.PieceActivationManifest{ CID: piece.PieceCID, Size: piece.Size, - }) + } + if tm.mockProofs { + pm.VerifiedAllocationKey = sm.Verified + } + manifest = append(manifest, pm) } head, err := tm.FullNode.ChainHead(tm.ctx) @@ -398,12 +427,12 @@ func (tm *TestUnmanagedMiner) SnapDeal(sectorNumber abi.SectorNumber) []abi.Piec req.NoError(err) req.True(r.Receipt.ExitCode.IsSuccess()) - si.pieces = pieces + si.pieces = manifest si.sealedCid = newSealedCid si.unsealedCid = newUnsealedCid tm.setCommittedSector(si) - return pieces + return pieces, r.TipSet } func (tm *TestUnmanagedMiner) log(msg string, args ...interface{}) { @@ -487,9 +516,9 @@ func (tm *TestUnmanagedMiner) mkAndSavePiecesToOnboard(sector sectorInfo) (secto } // Create a struct for the piece info - sector.pieces = []abi.PieceInfo{{ - Size: paddedPieceSize, - PieceCID: pieceCIDA, + sector.pieces = []miner14.PieceActivationManifest{{ + Size: paddedPieceSize, + CID: pieceCIDA, }} // Create a temporary file for the sealed sector @@ -679,7 +708,7 @@ func (tm *TestUnmanagedMiner) submitProveCommit( sectors []sectorInfo, requireActivationSuccess bool, modifyNIActivationsBeforeSubmit func([]miner14.SectorNIActivationInfo) []miner14.SectorNIActivationInfo, -) []exitcode.ExitCode { +) ([]exitcode.ExitCode, types.TipSetKey) { req := require.New(tm.t) @@ -767,13 +796,7 @@ func (tm *TestUnmanagedMiner) submitProveCommit( for i, sector := range sectors { activations[i] = miner14.SectorActivationManifest{SectorNumber: sector.sectorNumber} if len(sector.pieces) > 0 { - activations[i].Pieces = make([]miner14.PieceActivationManifest, len(sector.pieces)) - for j, piece := range sector.pieces { - activations[i].Pieces[j] = miner14.PieceActivationManifest{ - CID: piece.PieceCID, - Size: piece.Size, - } - } + activations[i].Pieces = sector.pieces } } @@ -819,7 +842,7 @@ func (tm *TestUnmanagedMiner) submitProveCommit( } } - return exitCodes + return exitCodes, msgReturn.TipSet } func (tm *TestUnmanagedMiner) wdPostLoop() { @@ -1230,7 +1253,7 @@ func (tm *TestUnmanagedMiner) generatePreCommit(sector sectorInfo, sealRandEpoch if tm.mockProofs { sector.sealedCid = cid.MustParse("bagboea4b5abcatlxechwbp7kjpjguna6r6q7ejrhe6mdp3lf34pmswn27pkkiekz") if len(sector.pieces) > 0 { - sector.unsealedCid = fixedPieceCid + sector.unsealedCid = sector.pieces[0].CID // TODO: handle multiple pieces? } return sector, nil } @@ -1269,7 +1292,7 @@ func (tm *TestUnmanagedMiner) generatePreCommit(sector sectorInfo, sealRandEpoch sector.sectorNumber, actorId, sealTickets, - sector.pieces, + sector.piecesToPieceInfos(), ) if err != nil { return sectorInfo{}, fmt.Errorf("failed to run SealPreCommitPhase1 for sector %d: %w", sector.sectorNumber, err) @@ -1362,7 +1385,7 @@ func (tm *TestUnmanagedMiner) generateSectorProof(sector sectorInfo) ([]byte, er actorId, sector.sealTickets, interactiveRandomness, - sector.pieces, + sector.piecesToPieceInfos(), ) if err != nil { return nil, fmt.Errorf("failed to run SealCommitPhase1 for sector %d: %w", sector.sectorNumber, err) diff --git a/itests/manual_onboarding_test.go b/itests/manual_onboarding_test.go index c03ddd9b1d7..3c3988254c1 100644 --- a/itests/manual_onboarding_test.go +++ b/itests/manual_onboarding_test.go @@ -5,6 +5,7 @@ import ( "testing" "time" + "github.com/ipfs/go-cid" "github.com/stretchr/testify/require" "github.com/filecoin-project/go-state-types/abi" @@ -87,7 +88,7 @@ func TestManualSectorOnboarding(t *testing.T) { minerC.AssertNoPower() // ---- Miner B onboards a CC sector - bSectors := minerB.OnboardSectors(sealProofType, false, 1) + bSectors, _ := minerB.OnboardSectors(sealProofType, []kit.SectorManifest{{}}) req.Len(bSectors, 1) // 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() @@ -95,7 +96,7 @@ func TestManualSectorOnboarding(t *testing.T) { blockMiner.WatchMinerForPost(minerB.ActorAddr) // --- Miner C onboards sector with data/pieces - cSectors := minerC.OnboardSectors(sealProofType, true, 1) + cSectors, _ := minerC.OnboardSectors(sealProofType, []kit.SectorManifest{kit.SectorWithRandPiece()}) // Miner C should still not have power as power can only be gained after sector is activated i.e. the first WindowPost is submitted for it minerC.AssertNoPower() // Ensure that the block miner checks for and waits for posts during the appropriate proving window from our new miner with a sector @@ -106,7 +107,7 @@ func TestManualSectorOnboarding(t *testing.T) { minerC.WaitTillActivatedAndAssertPower(cSectors, uint64(defaultSectorSize), uint64(defaultSectorSize)) // Miner B has activated the CC sector -> upgrade it with snapdeals - _ = minerB.SnapDeal(bSectors[0]) + _, _ = minerB.SnapDeal(bSectors[0], kit.SectorManifest{Piece: cid.MustParse("baga6ea4seaqlhznlutptgfwhffupyer6txswamerq5fc2jlwf2lys2mm5jtiaeq")}) }) } } diff --git a/itests/niporep_manual_test.go b/itests/niporep_manual_test.go index a2fa949082a..8b0e0261bb1 100644 --- a/itests/niporep_manual_test.go +++ b/itests/niporep_manual_test.go @@ -263,21 +263,22 @@ func TestManualNISectorOnboarding(t *testing.T) { for i, tcMiner := range tc.miners { miner := miners[i] + sm := make([]kit.SectorManifest, len(tcMiner.sectorsToOnboard)) var expectSuccesses int - for _, ec := range tcMiner.sectorsToOnboard { + for i, ec := range tcMiner.sectorsToOnboard { if ec.IsSuccess() { expectSuccesses++ } + sm[i] = kit.SectorManifest{} } head, err = client.ChainHead(ctx) req.NoError(err) // Onboard CC sectors to this test miner using NI-PoRep - sectors[i] = miner.OnboardSectors( + sectors[i], _ = miner.OnboardSectors( sealProofType, - false, - len(tcMiner.sectorsToOnboard), + sm, kit.WithExpectedExitCodes(tcMiner.sectorsToOnboard), kit.WithRequireActivationSuccess(tcMiner.allOrNothing), kit.WithModifyNIActivationsBeforeSubmit(func(activations []miner14.SectorNIActivationInfo) []miner14.SectorNIActivationInfo { @@ -337,7 +338,7 @@ func TestManualNISectorOnboarding(t *testing.T) { req.NoError(err) // Snap a deal into the first of the successfully onboarded CC sectors for this miner - snapPieces := miner.SnapDeal(sectors[i][0]) + snapPieces, _ := miner.SnapDeal(sectors[i][0], kit.SectorWithRandPiece()) // Check "sector-updated" event happned after snap { @@ -390,7 +391,7 @@ func TestNISectorFailureCases(t *testing.T) { build.Clock.Sleep(time.Second) // We have to onboard a sector first to get the miner enrolled in cron; although we don't need to wait for it to prove - _ = miner.OnboardSectors(sealProofType, false, 1) + _, _ = miner.OnboardSectors(sealProofType, []kit.SectorManifest{{}}) // Utility functions and variables for our failure cases diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 23788c4bd02..5fcd4d5b5a8 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -244,6 +244,7 @@ func (a *StateAPI) StateMinerDeadlines(ctx context.Context, m address.Address, t out[i] = api.Deadline{ PostSubmissions: ps, DisputableProofCount: l, + DailyFee: dl.DailyFee(), } return nil }); err != nil { From 15d46550fea10949491748476df27260ffdd7e19 Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 14 Mar 2025 13:24:53 +1100 Subject: [PATCH 2/2] fixup! chore(fees): add initial fees & migration testing (FIP-0100) --- chain/actors/builtin/miner/actor.go.template | 2 +- chain/actors/builtin/miner/miner.go | 2 +- chain/actors/builtin/miner/state.go.template | 6 +- chain/actors/builtin/miner/v0.go | 4 +- chain/actors/builtin/miner/v10.go | 4 +- chain/actors/builtin/miner/v11.go | 4 +- chain/actors/builtin/miner/v12.go | 4 +- chain/actors/builtin/miner/v13.go | 4 +- chain/actors/builtin/miner/v14.go | 4 +- chain/actors/builtin/miner/v15.go | 4 +- chain/actors/builtin/miner/v16.go | 6 +- chain/actors/builtin/miner/v2.go | 4 +- chain/actors/builtin/miner/v3.go | 4 +- chain/actors/builtin/miner/v4.go | 4 +- chain/actors/builtin/miner/v5.go | 4 +- chain/actors/builtin/miner/v6.go | 4 +- chain/actors/builtin/miner/v7.go | 4 +- chain/actors/builtin/miner/v8.go | 4 +- chain/actors/builtin/miner/v9.go | 4 +- itests/daily_fees_test.go | 39 ++++++++--- itests/kit/node_unmanaged.go | 69 +++++++++++++++++--- itests/manual_onboarding_test.go | 7 +- itests/niporep_manual_test.go | 12 ++-- node/impl/full/state.go | 6 +- 24 files changed, 141 insertions(+), 68 deletions(-) diff --git a/chain/actors/builtin/miner/actor.go.template b/chain/actors/builtin/miner/actor.go.template index d81a10c8fdd..058de451514 100644 --- a/chain/actors/builtin/miner/actor.go.template +++ b/chain/actors/builtin/miner/actor.go.template @@ -127,7 +127,7 @@ type Deadline interface { PartitionsChanged(Deadline) (bool, error) DisputableProofCount() (uint64, error) - DailyFee() abi.TokenAmount + DailyFee() (abi.TokenAmount, error) } type Partition interface { diff --git a/chain/actors/builtin/miner/miner.go b/chain/actors/builtin/miner/miner.go index 0d40fccc112..0fd65e8d399 100644 --- a/chain/actors/builtin/miner/miner.go +++ b/chain/actors/builtin/miner/miner.go @@ -211,7 +211,7 @@ type Deadline interface { PartitionsChanged(Deadline) (bool, error) DisputableProofCount() (uint64, error) - DailyFee() abi.TokenAmount + DailyFee() (abi.TokenAmount, error) } type Partition interface { diff --git a/chain/actors/builtin/miner/state.go.template b/chain/actors/builtin/miner/state.go.template index d3c29ed37f2..4b97c767840 100644 --- a/chain/actors/builtin/miner/state.go.template +++ b/chain/actors/builtin/miner/state.go.template @@ -552,13 +552,13 @@ func (d *deadline{{.v}}) DisputableProofCount() (uint64, error) { {{end}} } -func (d *deadline{{.v}}) DailyFee() abi.TokenAmount { +func (d *deadline{{.v}}) DailyFee() (abi.TokenAmount, error) { {{- if (ge .v 16)}} if d.Deadline.DailyFee.Int != nil { - return d.Deadline.DailyFee + return d.Deadline.DailyFee, nil } {{- end}} - return big.Zero() + return big.Zero(), nil } func (p *partition{{.v}}) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v0.go b/chain/actors/builtin/miner/v0.go index fc59eaea69e..1182bcf5b39 100644 --- a/chain/actors/builtin/miner/v0.go +++ b/chain/actors/builtin/miner/v0.go @@ -480,8 +480,8 @@ func (d *deadline0) DisputableProofCount() (uint64, error) { } -func (d *deadline0) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline0) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition0) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v10.go b/chain/actors/builtin/miner/v10.go index e9f6724fe8b..a8e05e25819 100644 --- a/chain/actors/builtin/miner/v10.go +++ b/chain/actors/builtin/miner/v10.go @@ -515,8 +515,8 @@ func (d *deadline10) DisputableProofCount() (uint64, error) { } -func (d *deadline10) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline10) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition10) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v11.go b/chain/actors/builtin/miner/v11.go index cbd72a64164..8a20cf81070 100644 --- a/chain/actors/builtin/miner/v11.go +++ b/chain/actors/builtin/miner/v11.go @@ -515,8 +515,8 @@ func (d *deadline11) DisputableProofCount() (uint64, error) { } -func (d *deadline11) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline11) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition11) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v12.go b/chain/actors/builtin/miner/v12.go index 5acb0367307..adc2ab1c99a 100644 --- a/chain/actors/builtin/miner/v12.go +++ b/chain/actors/builtin/miner/v12.go @@ -515,8 +515,8 @@ func (d *deadline12) DisputableProofCount() (uint64, error) { } -func (d *deadline12) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline12) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition12) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v13.go b/chain/actors/builtin/miner/v13.go index 6f8d698556b..478892eef07 100644 --- a/chain/actors/builtin/miner/v13.go +++ b/chain/actors/builtin/miner/v13.go @@ -515,8 +515,8 @@ func (d *deadline13) DisputableProofCount() (uint64, error) { } -func (d *deadline13) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline13) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition13) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v14.go b/chain/actors/builtin/miner/v14.go index c1f95dd12cc..e5578ce5257 100644 --- a/chain/actors/builtin/miner/v14.go +++ b/chain/actors/builtin/miner/v14.go @@ -515,8 +515,8 @@ func (d *deadline14) DisputableProofCount() (uint64, error) { } -func (d *deadline14) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline14) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition14) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v15.go b/chain/actors/builtin/miner/v15.go index 46865e66596..c40ba27de18 100644 --- a/chain/actors/builtin/miner/v15.go +++ b/chain/actors/builtin/miner/v15.go @@ -515,8 +515,8 @@ func (d *deadline15) DisputableProofCount() (uint64, error) { } -func (d *deadline15) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline15) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition15) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v16.go b/chain/actors/builtin/miner/v16.go index 4267248033f..4453332e16e 100644 --- a/chain/actors/builtin/miner/v16.go +++ b/chain/actors/builtin/miner/v16.go @@ -515,11 +515,11 @@ func (d *deadline16) DisputableProofCount() (uint64, error) { } -func (d *deadline16) DailyFee() abi.TokenAmount { +func (d *deadline16) DailyFee() (abi.TokenAmount, error) { if d.Deadline.DailyFee.Int != nil { - return d.Deadline.DailyFee + return d.Deadline.DailyFee, nil } - return big.Zero() + return big.Zero(), nil } func (p *partition16) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v2.go b/chain/actors/builtin/miner/v2.go index f8d02093db4..c7732069154 100644 --- a/chain/actors/builtin/miner/v2.go +++ b/chain/actors/builtin/miner/v2.go @@ -512,8 +512,8 @@ func (d *deadline2) DisputableProofCount() (uint64, error) { } -func (d *deadline2) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline2) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition2) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v3.go b/chain/actors/builtin/miner/v3.go index ac062fef8a3..81491205dec 100644 --- a/chain/actors/builtin/miner/v3.go +++ b/chain/actors/builtin/miner/v3.go @@ -512,8 +512,8 @@ func (d *deadline3) DisputableProofCount() (uint64, error) { } -func (d *deadline3) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline3) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition3) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v4.go b/chain/actors/builtin/miner/v4.go index 7532244adbf..02063ecd855 100644 --- a/chain/actors/builtin/miner/v4.go +++ b/chain/actors/builtin/miner/v4.go @@ -512,8 +512,8 @@ func (d *deadline4) DisputableProofCount() (uint64, error) { } -func (d *deadline4) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline4) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition4) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v5.go b/chain/actors/builtin/miner/v5.go index a720cf38f20..33dc986fc97 100644 --- a/chain/actors/builtin/miner/v5.go +++ b/chain/actors/builtin/miner/v5.go @@ -512,8 +512,8 @@ func (d *deadline5) DisputableProofCount() (uint64, error) { } -func (d *deadline5) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline5) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition5) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v6.go b/chain/actors/builtin/miner/v6.go index 6195c8b6d60..02824a65249 100644 --- a/chain/actors/builtin/miner/v6.go +++ b/chain/actors/builtin/miner/v6.go @@ -512,8 +512,8 @@ func (d *deadline6) DisputableProofCount() (uint64, error) { } -func (d *deadline6) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline6) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition6) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v7.go b/chain/actors/builtin/miner/v7.go index 97638c0299e..e16756af6f2 100644 --- a/chain/actors/builtin/miner/v7.go +++ b/chain/actors/builtin/miner/v7.go @@ -511,8 +511,8 @@ func (d *deadline7) DisputableProofCount() (uint64, error) { } -func (d *deadline7) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline7) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition7) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v8.go b/chain/actors/builtin/miner/v8.go index 7e7bc549a80..a30dcf72916 100644 --- a/chain/actors/builtin/miner/v8.go +++ b/chain/actors/builtin/miner/v8.go @@ -511,8 +511,8 @@ func (d *deadline8) DisputableProofCount() (uint64, error) { } -func (d *deadline8) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline8) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition8) AllSectors() (bitfield.BitField, error) { diff --git a/chain/actors/builtin/miner/v9.go b/chain/actors/builtin/miner/v9.go index 2773feffaa3..2450936b9e7 100644 --- a/chain/actors/builtin/miner/v9.go +++ b/chain/actors/builtin/miner/v9.go @@ -515,8 +515,8 @@ func (d *deadline9) DisputableProofCount() (uint64, error) { } -func (d *deadline9) DailyFee() abi.TokenAmount { - return big.Zero() +func (d *deadline9) DailyFee() (abi.TokenAmount, error) { + return big.Zero(), nil } func (p *partition9) AllSectors() (bitfield.BitField, error) { diff --git a/itests/daily_fees_test.go b/itests/daily_fees_test.go index 424f0c14d5a..842b35fbf32 100644 --- a/itests/daily_fees_test.go +++ b/itests/daily_fees_test.go @@ -6,7 +6,6 @@ import ( "testing" "time" - "github.com/ipfs/go-cid" "github.com/stretchr/testify/require" "github.com/filecoin-project/go-address" @@ -102,6 +101,15 @@ func TestDailyFees(t *testing.T) { return cs.FilCirculating } + checkMiner16Invariants := func() { + act, err := client.StateGetActor(ctx, mminer.ActorAddr, types.EmptyTSK) + req.NoError(err) + var st miner16.State + req.NoError(store.Get(ctx, act.Head, &st)) + _, msgs := miner16.CheckStateInvariants(&st, store, act.Balance) + req.Len(msgs.Messages(), 0) + } + // checkDailyFee checks the daily fee for a sector, returning true if the sector is in the v16 // format and false if it is in the v15 format. It also returns the daily fee. checkDailyFee := func(sn abi.SectorNumber) (bool, abi.TokenAmount) { @@ -322,17 +330,19 @@ func TestDailyFees(t *testing.T) { piece := abi.PieceInfo{ Size: abi.PaddedPieceSize(defaultSectorSize), - PieceCID: cid.MustParse("baga6ea4seaqlhznlutptgfwhffupyer6txswamerq5fc2jlwf2lys2mm5jtiaeq"), + PieceCID: kit.BogusPieceCid2, } clientId, allocationId := kit.SetupAllocation(ctx, t, &client, minerId, piece, verifiedClientAddr, 0, 0) var allSectors []abi.SectorNumber - ccSectors24, _ := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{{}, {}}) // 2 CC sectors + ccSectors24, _ := mminer.OnboardSectors(sealProofType, kit.NewSectorBatch().AddEmptySectors(2)) // 2 CC sectors allSectors = append(allSectors, ccSectors24...) - dealSector24, _ := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{kit.SectorWithRandPiece()}) + dealSector24, _ := mminer.OnboardSectors(sealProofType, kit.NewSectorBatch().AddSectorsWithRandomPieces(1)) allSectors = append(allSectors, dealSector24...) - verifiedSector24, _ := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{ - {Piece: piece.PieceCID, Verified: &miner14.VerifiedAllocationKey{Client: clientId, ID: verifreg14.AllocationId(allocationId)}}}) + verifiedSector24, _ := mminer.OnboardSectors( + sealProofType, + kit.NewSectorBatch().AddSector( + kit.SectorWithVerifiedPiece(piece.PieceCID, &miner14.VerifiedAllocationKey{Client: clientId, ID: verifreg14.AllocationId(allocationId)}))) allSectors = append(allSectors, verifiedSector24...) blockMiner.WatchMinerForPost(mminer.ActorAddr) @@ -368,6 +378,8 @@ func TestDailyFees(t *testing.T) { // Move past the upgrade client.WaitTillChain(ctx, kit.HeightAtLeast(nv25epoch+5)) + checkMiner16Invariants() + t.Log("*** Re-checking daily fees on sectors onboarded before the network upgrade") // Still no fees, sectors shouldn't have been touched @@ -395,6 +407,8 @@ func TestDailyFees(t *testing.T) { feePostWg.Done() }() + checkMiner16Invariants() + t.Log("*** Checking daily fees on old and snapped sectors") // No fees on untouched sectors, but because we expect all our sectors to be stored in the root @@ -422,7 +436,7 @@ func TestDailyFees(t *testing.T) { clientId, allocationId = kit.SetupAllocation(ctx, t, &client, minerId, piece, verifiedClientAddr, 0, 0) - ccSectors25, ccTsk := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{{}, {}}) // 2 CC sectors + ccSectors25, ccTsk := mminer.OnboardSectors(sealProofType, kit.NewSectorBatch().AddEmptySectors(2)) // 2 CC sectors allSectors = append(allSectors, ccSectors25...) ccSectors25ExpectedFee := miner16.DailyProofFee(circulatingSupplyBefore(ccTsk), abi.NewStoragePower(int64(defaultSectorSize))) feePostWg.Add(1) @@ -431,7 +445,7 @@ func TestDailyFees(t *testing.T) { feePostWg.Done() }() - dealSector25, dealTsk := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{kit.SectorWithRandPiece()}) + dealSector25, dealTsk := mminer.OnboardSectors(sealProofType, kit.NewSectorBatch().AddSectorsWithRandomPieces(1)) allSectors = append(allSectors, dealSector25...) dealSector25ExpectedFee := miner16.DailyProofFee(circulatingSupplyBefore(dealTsk), abi.NewStoragePower(int64(defaultSectorSize))) feePostWg.Add(1) @@ -440,8 +454,10 @@ func TestDailyFees(t *testing.T) { feePostWg.Done() }() - verifiedSector25, verifiedTsk := mminer.OnboardSectors(sealProofType, []kit.SectorManifest{ - {Piece: piece.PieceCID, Verified: &miner14.VerifiedAllocationKey{Client: clientId, ID: verifreg14.AllocationId(allocationId)}}}) + verifiedSector25, verifiedTsk := mminer.OnboardSectors( + sealProofType, + kit.NewSectorBatch().AddSector( + kit.SectorWithVerifiedPiece(piece.PieceCID, &miner14.VerifiedAllocationKey{Client: clientId, ID: verifreg14.AllocationId(allocationId)}))) allSectors = append(allSectors, verifiedSector25...) verified25ExpectedFee := miner16.DailyProofFee(circulatingSupplyBefore(verifiedTsk), abi.NewStoragePower(int64(defaultSectorSize*10))) feePostWg.Add(1) @@ -485,6 +501,7 @@ func TestDailyFees(t *testing.T) { } checkAllFees() // before PoST + checkMiner16Invariants() t.Log("*** Waiting for PoST for sectors onboarded after the network upgrade") @@ -493,6 +510,7 @@ func TestDailyFees(t *testing.T) { mminer.WaitTillActivatedAndAssertPower(allSectors, expectedRaw, expectedQap) checkAllFees() // after PoST + checkMiner16Invariants() // wait for all fees to be paid—we need each one to have reached its first deadline and they are // likely spread out over multiple deadlines @@ -522,4 +540,5 @@ func TestDailyFees(t *testing.T) { expectMinerBurn(expectTotalBurn) // with only one fee payment so far for all sectors, the total in the records should be the same as the burn checkFeeRecords(expectTotalBurn) + checkMiner16Invariants() } diff --git a/itests/kit/node_unmanaged.go b/itests/kit/node_unmanaged.go index 7b557d36c4d..04006f4379b 100644 --- a/itests/kit/node_unmanaged.go +++ b/itests/kit/node_unmanaged.go @@ -39,7 +39,10 @@ import ( ) // TODO: make a randomiser for this -var rndPieceCid = cid.MustParse("baga6ea4seaqjtovkwk4myyzj56eztkh5pzsk5upksan6f5outesy62bsvl4dsha") +var ( + BogusPieceCid1 = cid.MustParse("baga6ea4seaqjtovkwk4myyzj56eztkh5pzsk5upksan6f5outesy62bsvl4dsha") + BogusPieceCid2 = cid.MustParse("baga6ea4seaqlhznlutptgfwhffupyer6txswamerq5fc2jlwf2lys2mm5jtiaeq") +) // 32 bytes of 1's: this value essentially ignored in NI-PoRep proofs, but all zeros is not recommended. // Regardless of what we submit to the chain, actors will replace it with 32 1's anyway but we are @@ -207,9 +210,55 @@ type SectorManifest struct { Verified *miner14.VerifiedAllocationKey } -func SectorWithRandPiece() SectorManifest { +// SectorBatch is a builder for creating sector manifests +type SectorBatch struct { + manifests []SectorManifest +} + +// NewSectorBatch creates an empty sector batch +func NewSectorBatch() *SectorBatch { + return &SectorBatch{manifests: []SectorManifest{}} +} + +// AddEmptySectors adds the specified number of CC sectors to the batch +func (sb *SectorBatch) AddEmptySectors(count int) *SectorBatch { + for i := 0; i < count; i++ { + sb.manifests = append(sb.manifests, EmptySector()) + } + return sb +} + +// AddSectorsWithRandomPieces adds sectors with random pieces +func (sb *SectorBatch) AddSectorsWithRandomPieces(count int) *SectorBatch { + for i := 0; i < count; i++ { + sb.manifests = append(sb.manifests, SectorWithPiece(BogusPieceCid1)) + } + return sb +} + +// AddSector adds a custom sector manifest +func (sb *SectorBatch) AddSector(manifest SectorManifest) *SectorBatch { + sb.manifests = append(sb.manifests, manifest) + return sb +} + +// EmptySector creates an empty (CC) sector with no pieces +func EmptySector() SectorManifest { + return SectorManifest{} +} + +// SectorWithPiece creates a sector with the specified piece CID +func SectorWithPiece(piece cid.Cid) SectorManifest { + return SectorManifest{ + Piece: piece, + } +} + +// SectorWithVerifiedPiece creates a sector with a verified allocation +func SectorWithVerifiedPiece(piece cid.Cid, key *miner14.VerifiedAllocationKey) SectorManifest { return SectorManifest{ - Piece: rndPieceCid, + Piece: piece, + Verified: key, } } @@ -221,7 +270,7 @@ func SectorWithRandPiece() SectorManifest { // process with real proofs. func (tm *TestUnmanagedMiner) OnboardSectors( proofType abi.RegisteredSealProof, - manifest []SectorManifest, + sectorBatch *SectorBatch, opts ...OnboardOpt, ) ([]abi.SectorNumber, types.TipSetKey) { @@ -232,7 +281,7 @@ func (tm *TestUnmanagedMiner) OnboardSectors( req.NoError(o(&options)) } - sectors := make([]sectorInfo, len(manifest)) + sectors := make([]sectorInfo, len(sectorBatch.manifests)) // Wait for the seal randomness to be available (we can only draw seal randomness from // tipsets that have already achieved finality) @@ -243,7 +292,7 @@ func (tm *TestUnmanagedMiner) OnboardSectors( // For each sector, run PC1, PC2, C1 an C2, preparing for ProveCommit. If the proof needs it we // will also submit a precommit for the sector. - for idx, sm := range manifest { + for idx, sm := range sectorBatch.manifests { // We hold on to `sector`, adding new properties to it as we go along until we're finished with // this phase, then add it to `sectors` @@ -1252,8 +1301,12 @@ func (tm *TestUnmanagedMiner) waitPreCommitSealRandomness(proofType abi.Register func (tm *TestUnmanagedMiner) generatePreCommit(sector sectorInfo, sealRandEpoch abi.ChainEpoch) (sectorInfo, error) { if tm.mockProofs { sector.sealedCid = cid.MustParse("bagboea4b5abcatlxechwbp7kjpjguna6r6q7ejrhe6mdp3lf34pmswn27pkkiekz") - if len(sector.pieces) > 0 { - sector.unsealedCid = sector.pieces[0].CID // TODO: handle multiple pieces? + switch len(sector.pieces) { + case 0: + case 1: + sector.unsealedCid = sector.pieces[0].CID + default: + require.FailNow(tm.t, "generatePreCommit: multiple pieces not supported") // yet } return sector, nil } diff --git a/itests/manual_onboarding_test.go b/itests/manual_onboarding_test.go index 3c3988254c1..bc4771ff407 100644 --- a/itests/manual_onboarding_test.go +++ b/itests/manual_onboarding_test.go @@ -5,7 +5,6 @@ import ( "testing" "time" - "github.com/ipfs/go-cid" "github.com/stretchr/testify/require" "github.com/filecoin-project/go-state-types/abi" @@ -88,7 +87,7 @@ func TestManualSectorOnboarding(t *testing.T) { minerC.AssertNoPower() // ---- Miner B onboards a CC sector - bSectors, _ := minerB.OnboardSectors(sealProofType, []kit.SectorManifest{{}}) + bSectors, _ := minerB.OnboardSectors(sealProofType, kit.NewSectorBatch().AddEmptySectors(1)) req.Len(bSectors, 1) // 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() @@ -96,7 +95,7 @@ func TestManualSectorOnboarding(t *testing.T) { blockMiner.WatchMinerForPost(minerB.ActorAddr) // --- Miner C onboards sector with data/pieces - cSectors, _ := minerC.OnboardSectors(sealProofType, []kit.SectorManifest{kit.SectorWithRandPiece()}) + cSectors, _ := minerC.OnboardSectors(sealProofType, kit.NewSectorBatch().AddSectorsWithRandomPieces(1)) // Miner C should still not have power as power can only be gained after sector is activated i.e. the first WindowPost is submitted for it minerC.AssertNoPower() // Ensure that the block miner checks for and waits for posts during the appropriate proving window from our new miner with a sector @@ -107,7 +106,7 @@ func TestManualSectorOnboarding(t *testing.T) { minerC.WaitTillActivatedAndAssertPower(cSectors, uint64(defaultSectorSize), uint64(defaultSectorSize)) // Miner B has activated the CC sector -> upgrade it with snapdeals - _, _ = minerB.SnapDeal(bSectors[0], kit.SectorManifest{Piece: cid.MustParse("baga6ea4seaqlhznlutptgfwhffupyer6txswamerq5fc2jlwf2lys2mm5jtiaeq")}) + _, _ = minerB.SnapDeal(bSectors[0], kit.SectorManifest{Piece: kit.BogusPieceCid2}) }) } } diff --git a/itests/niporep_manual_test.go b/itests/niporep_manual_test.go index 8b0e0261bb1..ac65876da58 100644 --- a/itests/niporep_manual_test.go +++ b/itests/niporep_manual_test.go @@ -263,13 +263,11 @@ func TestManualNISectorOnboarding(t *testing.T) { for i, tcMiner := range tc.miners { miner := miners[i] - sm := make([]kit.SectorManifest, len(tcMiner.sectorsToOnboard)) var expectSuccesses int - for i, ec := range tcMiner.sectorsToOnboard { + for _, ec := range tcMiner.sectorsToOnboard { if ec.IsSuccess() { expectSuccesses++ } - sm[i] = kit.SectorManifest{} } head, err = client.ChainHead(ctx) @@ -278,7 +276,7 @@ func TestManualNISectorOnboarding(t *testing.T) { // Onboard CC sectors to this test miner using NI-PoRep sectors[i], _ = miner.OnboardSectors( sealProofType, - sm, + kit.NewSectorBatch().AddEmptySectors(len(tcMiner.sectorsToOnboard)), kit.WithExpectedExitCodes(tcMiner.sectorsToOnboard), kit.WithRequireActivationSuccess(tcMiner.allOrNothing), kit.WithModifyNIActivationsBeforeSubmit(func(activations []miner14.SectorNIActivationInfo) []miner14.SectorNIActivationInfo { @@ -338,7 +336,7 @@ func TestManualNISectorOnboarding(t *testing.T) { req.NoError(err) // Snap a deal into the first of the successfully onboarded CC sectors for this miner - snapPieces, _ := miner.SnapDeal(sectors[i][0], kit.SectorWithRandPiece()) + snapPieces, _ := miner.SnapDeal(sectors[i][0], kit.SectorWithPiece(kit.BogusPieceCid2)) // Check "sector-updated" event happned after snap { @@ -391,7 +389,7 @@ func TestNISectorFailureCases(t *testing.T) { build.Clock.Sleep(time.Second) // We have to onboard a sector first to get the miner enrolled in cron; although we don't need to wait for it to prove - _, _ = miner.OnboardSectors(sealProofType, []kit.SectorManifest{{}}) + _, _ = miner.OnboardSectors(sealProofType, kit.NewSectorBatch().AddEmptySectors(1)) // Utility functions and variables for our failure cases @@ -483,7 +481,7 @@ func TestNISectorFailureCases(t *testing.T) { t.Run("bad SealedCID", func(t *testing.T) { params := mkParams() - params.Sectors[1].SealedCID = cid.MustParse("baga6ea4seaqjtovkwk4myyzj56eztkh5pzsk5upksan6f5outesy62bsvl4dsha") + params.Sectors[1].SealedCID = kit.BogusPieceCid1 submitAndFail(¶ms, "invalid NI commit 1 while requiring activation success", 16) }) diff --git a/node/impl/full/state.go b/node/impl/full/state.go index 5fcd4d5b5a8..7e7d0e842cb 100644 --- a/node/impl/full/state.go +++ b/node/impl/full/state.go @@ -240,11 +240,15 @@ func (a *StateAPI) StateMinerDeadlines(ctx context.Context, m address.Address, t if err != nil { return err } + dailyFee, err := dl.DailyFee() + if err != nil { + return err + } out[i] = api.Deadline{ PostSubmissions: ps, DisputableProofCount: l, - DailyFee: dl.DailyFee(), + DailyFee: dailyFee, } return nil }); err != nil {