From a9f9f95f5e93bb7a27fd9fe8c0217e347fa1bef9 Mon Sep 17 00:00:00 2001 From: ZenGround0 <5515260+ZenGround0@users.noreply.github.com> Date: Fri, 28 May 2021 16:29:14 -0400 Subject: [PATCH] Feat/param finalization (#1438) * Add aggregate network fee * Fix test * Size limit failure scenario tests * Test network fee for aggregate * Fix after rebase * Review response Co-authored-by: ZenGround0 --- actors/builtin/miner/miner_actor.go | 5 ++ actors/builtin/miner/miner_commitment_test.go | 7 +- actors/builtin/miner/miner_test.go | 10 ++- actors/builtin/miner/monies.go | 14 ++++ actors/builtin/miner/monies_test.go | 35 +++++++++ actors/builtin/miner/policy.go | 16 ++--- actors/builtin/network.go | 3 + actors/runtime/runtime.go | 3 + actors/test/commit_post_test.go | 72 ++++++++++++++++++- support/mock/builder.go | 1 + support/mock/mockrt.go | 6 ++ support/vm/invocation_context.go | 5 ++ 12 files changed, 163 insertions(+), 14 deletions(-) diff --git a/actors/builtin/miner/miner_actor.go b/actors/builtin/miner/miner_actor.go index 0eb77d117..aa7ae647a 100644 --- a/actors/builtin/miner/miner_actor.go +++ b/actors/builtin/miner/miner_actor.go @@ -955,6 +955,11 @@ func (a Actor) ProveCommitAggregate(rt Runtime, params *ProveCommitAggregatePara builtin.RequireNoErr(rt, err, exitcode.ErrIllegalArgument, "aggregate seal verify failed") confirmSectorProofsValid(rt, precommitsToConfirm) + burnFunds(rt, AggregateNetworkFee(len(precommitsToConfirm), rt.BaseFee())) + rt.StateReadonly(&st) + err = st.CheckBalanceInvariants(rt.CurrentBalance()) + builtin.RequireNoErr(rt, err, ErrBalanceInvariantBroken, "balance invariants broken") + return nil } diff --git a/actors/builtin/miner/miner_commitment_test.go b/actors/builtin/miner/miner_commitment_test.go index 86dc959b0..f5036180c 100644 --- a/actors/builtin/miner/miner_commitment_test.go +++ b/actors/builtin/miner/miner_commitment_test.go @@ -237,7 +237,8 @@ func TestCommitments(t *testing.T) { // Expires too early rt.ExpectAbortContainsMessage(exitcode.ErrIllegalArgument, "must exceed", func() { - actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, expiration-20*builtin.EpochsInDay, nil), preCommitConf{}, false) + earlyExpiration := abi.ChainEpoch(miner.MinSectorExpiration - builtin.EpochsInDay) + actor.preCommitSector(rt, actor.makePreCommit(102, challengeEpoch, earlyExpiration, nil), preCommitConf{}, false) }) rt.Reset() @@ -484,10 +485,10 @@ func TestPreCommitBatch(t *testing.T) { error: "batch empty", }, { name: "too many sectors", - batchSize: 33, + batchSize: 257, balanceSurplus: big.Zero(), exit: exitcode.ErrIllegalArgument, - error: "batch of 33 too large", + error: "batch of 257 too large", }, { name: "insufficient balance", batchSize: 10, diff --git a/actors/builtin/miner/miner_test.go b/actors/builtin/miner/miner_test.go index 1408c5021..4fe0a9681 100644 --- a/actors/builtin/miner/miner_test.go +++ b/actors/builtin/miner/miner_test.go @@ -43,8 +43,8 @@ var bigBalance = big.Mul(big.NewInt(1_000_000), big.NewInt(1e18)) // A reward amount for use in tests where the vesting amount wants to be large enough to cover penalties. var bigRewards = big.Mul(big.NewInt(1_000), big.NewInt(1e18)) -// an expriration 1 greater than min expiration -const defaultSectorExpiration = 190 +// an expriration ~10 days greater than effective min expiration taking into account 30 days max between pre and prove commit +const defaultSectorExpiration = 220 func init() { testPid = abi.PeerID("peerID") @@ -5076,6 +5076,12 @@ func (h *actorHarness) proveCommitAggregateSector(rt *mock.Runtime, conf proveCo h.confirmSectorProofsValidInternal(rt, conf, precommits...) } + // burn networkFee + { + expectedFee := miner.AggregateNetworkFee(len(precommits), big.Zero()) + rt.ExpectSend(builtin.BurntFundsActorAddr, builtin.MethodSend, nil, expectedFee, nil, exitcode.Ok) + } + rt.SetCaller(h.worker, builtin.AccountActorCodeID) rt.ExpectValidateCallerAny() rt.Call(h.a.ProveCommitAggregate, params) diff --git a/actors/builtin/miner/monies.go b/actors/builtin/miner/monies.go index 987d2af8b..e79a2130c 100644 --- a/actors/builtin/miner/monies.go +++ b/actors/builtin/miner/monies.go @@ -208,3 +208,17 @@ func LockedRewardFromReward(reward abi.TokenAmount) (abi.TokenAmount, *VestSpec) lockAmount := big.Div(big.Mul(reward, LockedRewardFactorNum), LockedRewardFactorDenom) return lockAmount, &RewardVestingSpec } + +var EstimatedSingleProofGasUsage = big.NewInt(65733297) // PARAM_SPEC +var BatchDiscount = builtin.BigFrac{ // PARAM_SPEC + Numerator: big.NewInt(1), + Denominator: big.NewInt(20), +} +var BatchBalancer = big.Mul(big.NewInt(2), builtin.OneNanoFIL) // PARAM_SPEC + +func AggregateNetworkFee(aggregateSize int, baseFee abi.TokenAmount) abi.TokenAmount { + effectiveGasFee := big.Max(baseFee, BatchBalancer) + networkFeeNum := big.Product(effectiveGasFee, EstimatedSingleProofGasUsage, big.NewInt(int64(aggregateSize)), BatchDiscount.Numerator) + networkFee := big.Div(networkFeeNum, BatchDiscount.Denominator) + return networkFee +} diff --git a/actors/builtin/miner/monies_test.go b/actors/builtin/miner/monies_test.go index 4a73d958c..720026cac 100644 --- a/actors/builtin/miner/monies_test.go +++ b/actors/builtin/miner/monies_test.go @@ -214,3 +214,38 @@ func TestPrecommitDepositAndInitialPledgePostiive(t *testing.T) { assert.Equal(t, abi.NewTokenAmount(1), pcd) }) } + +func TestAggregateNetworkFee(t *testing.T) { + + t.Run("Constant fee per sector when base fee is below 2 nFIL", func(t *testing.T) { + oneSectorFee := miner.AggregateNetworkFee(1, big.Zero()) + tenSectorFee := miner.AggregateNetworkFee(10, big.Zero()) + assert.Equal(t, big.Mul(oneSectorFee, big.NewInt(10)), tenSectorFee) + fortySectorFee := miner.AggregateNetworkFee(40, builtin.OneNanoFIL) + assert.Equal(t, big.Mul(oneSectorFee, big.NewInt(40)), fortySectorFee) + }) + + t.Run("Fee increases iff basefee crosses threshold", func(t *testing.T) { + atNoBaseFee := miner.AggregateNetworkFee(10, big.Zero()) + atBalanceMinusOneBaseFee := miner.AggregateNetworkFee(10, big.Sub(miner.BatchBalancer, builtin.OneNanoFIL)) + atBalanceBaseFee := miner.AggregateNetworkFee(10, miner.BatchBalancer) + atBalancePlusOneBaseFee := miner.AggregateNetworkFee(10, big.Sum(miner.BatchBalancer, builtin.OneNanoFIL)) + atBalancePlusTwoBaseFee := miner.AggregateNetworkFee(10, big.Sum(miner.BatchBalancer, builtin.OneNanoFIL, builtin.OneNanoFIL)) + atBalanceTimesTwoBaseFee := miner.AggregateNetworkFee(10, big.Mul(miner.BatchBalancer, big.NewInt(2))) + + assert.True(t, atNoBaseFee.Equals(atBalanceMinusOneBaseFee)) + assert.True(t, atNoBaseFee.Equals(atBalanceBaseFee)) + assert.True(t, atBalanceBaseFee.LessThan(atBalancePlusOneBaseFee)) + assert.True(t, atBalancePlusOneBaseFee.LessThan(atBalancePlusTwoBaseFee)) + assert.True(t, atBalanceTimesTwoBaseFee.Equals(big.Mul(big.NewInt(2), atBalanceBaseFee))) + }) + + t.Run("Regression tests", func(t *testing.T) { + tenAtNoBaseFee := miner.AggregateNetworkFee(10, big.Zero()) + assert.Equal(t, big.Mul(builtin.OneNanoFIL, big.NewInt(65733297)), tenAtNoBaseFee) + tenAtOneNanoBaseFee := miner.AggregateNetworkFee(10, builtin.OneNanoFIL) + assert.Equal(t, big.Mul(builtin.OneNanoFIL, big.NewInt(65733297)), tenAtOneNanoBaseFee) + hundredAtThreeNanoBaseFee := miner.AggregateNetworkFee(100, big.Mul(big.NewInt(3), builtin.OneNanoFIL)) + assert.Equal(t, big.Mul(builtin.OneNanoFIL, big.NewInt(985999455)), hundredAtThreeNanoBaseFee) + }) +} diff --git a/actors/builtin/miner/policy.go b/actors/builtin/miner/policy.go index 3e518c486..73cd56f24 100644 --- a/actors/builtin/miner/policy.go +++ b/actors/builtin/miner/policy.go @@ -156,16 +156,16 @@ var MaxProveCommitDuration = map[abi.RegisteredSealProof]abi.ChainEpoch{ abi.RegisteredSealProof_StackedDrg512MiBV1: builtin.EpochsInDay + PreCommitChallengeDelay, abi.RegisteredSealProof_StackedDrg64GiBV1: builtin.EpochsInDay + PreCommitChallengeDelay, - abi.RegisteredSealProof_StackedDrg32GiBV1_1: 6*builtin.EpochsInDay + PreCommitChallengeDelay, // PARAM_SPEC - abi.RegisteredSealProof_StackedDrg2KiBV1_1: 6*builtin.EpochsInDay + PreCommitChallengeDelay, - abi.RegisteredSealProof_StackedDrg8MiBV1_1: 6*builtin.EpochsInDay + PreCommitChallengeDelay, - abi.RegisteredSealProof_StackedDrg512MiBV1_1: 6*builtin.EpochsInDay + PreCommitChallengeDelay, - abi.RegisteredSealProof_StackedDrg64GiBV1_1: 6*builtin.EpochsInDay + PreCommitChallengeDelay, + abi.RegisteredSealProof_StackedDrg32GiBV1_1: 9*builtin.EpochsInDay + PreCommitChallengeDelay, // PARAM_SPEC + abi.RegisteredSealProof_StackedDrg2KiBV1_1: 9*builtin.EpochsInDay + PreCommitChallengeDelay, + abi.RegisteredSealProof_StackedDrg8MiBV1_1: 9*builtin.EpochsInDay + PreCommitChallengeDelay, + abi.RegisteredSealProof_StackedDrg512MiBV1_1: 9*builtin.EpochsInDay + PreCommitChallengeDelay, + abi.RegisteredSealProof_StackedDrg64GiBV1_1: 9*builtin.EpochsInDay + PreCommitChallengeDelay, } // The maximum number of sector pre-commitments in a single batch. // 32 sectors per epoch would support a single miner onboarding 1EiB of 32GiB sectors in 1 year. -const PreCommitSectorBatchMaxSize = 32 +const PreCommitSectorBatchMaxSize = 256 // Maximum delay between challenge and pre-commitment. // This prevents a miner sealing sectors far in advance of committing them to the chain, thus committing to a @@ -297,8 +297,8 @@ func RewardForDisputedWindowPoSt(proofType abi.RegisteredPoStProof, disputedPowe } const MaxAggregatedSectors = 819 -const MinAggregatedSectors = 1 -const MaxAggregateProofSize = 192000 +const MinAggregatedSectors = 4 +const MaxAggregateProofSize = 81960 // The delay between pre commit expiration and clean up from state. This enforces that expired pre-commits // stay in state for a period of time creating a grace period during which a late-running aggregated prove-commit diff --git a/actors/builtin/network.go b/actors/builtin/network.go index d34ed8571..8f86e544a 100644 --- a/actors/builtin/network.go +++ b/actors/builtin/network.go @@ -58,3 +58,6 @@ var VerifiedDealWeightMultiplier = big.NewInt(100) // Precision used for making QA power calculations const SectorQualityPrecision = 20 + +// 1 NanoFIL +var OneNanoFIL = big.NewInt(1_000_000_000) diff --git a/actors/runtime/runtime.go b/actors/runtime/runtime.go index ba0b4f3de..4f5cf3fde 100644 --- a/actors/runtime/runtime.go +++ b/actors/runtime/runtime.go @@ -140,6 +140,9 @@ type Runtime interface { // Note events that may make debugging easier Log(level rt.LogLevel, msg string, args ...interface{}) + + // BaseFee returns the basefee value in attoFIL per unit gas for the currently exectuting tipset. + BaseFee() abi.TokenAmount } // Store defines the storage module exposed to actors. diff --git a/actors/test/commit_post_test.go b/actors/test/commit_post_test.go index 098cb3e89..e5fec43f9 100644 --- a/actors/test/commit_post_test.go +++ b/actors/test/commit_post_test.go @@ -586,7 +586,7 @@ func TestAggregateOnePreCommitExpires(t *testing.T) { v, _ = vm.AdvanceByDeadlineTillEpoch(t, v, minerAddrs.IDAddress, earlyPreCommitInvalid) // later precommits - laterPrecommits := preCommitSectors(t, v, 2, miner.PreCommitSectorBatchMaxSize, addrs[0], minerAddrs.IDAddress, sealProof, firstSectorNo+1, false) + laterPrecommits := preCommitSectors(t, v, 3, miner.PreCommitSectorBatchMaxSize, addrs[0], minerAddrs.IDAddress, sealProof, firstSectorNo+1, false) allPrecommits := append(earlyPrecommits, laterPrecommits...) sectorNosBf := precommitSectorNumbers(allPrecommits) @@ -596,6 +596,10 @@ func TestAggregateOnePreCommitExpires(t *testing.T) { v, _ = vm.AdvanceByDeadlineTillEpoch(t, v, minerAddrs.IDAddress, dlInfo.Close) // Assert that precommit should not yet be cleaned up. This makes fixing this test easier if parameters change. require.True(t, proveTime < earlyPreCommitTime+miner.MaxProveCommitDuration[sealProof]+miner.ExpiredPreCommitCleanUpDelay) + // Assert that we have a valid aggregate batch size + aggSectorsCount, err := sectorNosBf.Count() + require.NoError(t, err) + require.True(t, aggSectorsCount >= miner.MinAggregatedSectors && aggSectorsCount < miner.MaxAggregatedSectors) proveCommitAggregateParams := miner.ProveCommitAggregateParams{ SectorNumbers: sectorNosBf, @@ -611,6 +615,7 @@ func TestAggregateOnePreCommitExpires(t *testing.T) { {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward}, {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}, {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.UpdatePledgeTotal}, + {To: builtin.BurntFundsActorAddr, Method: builtin.MethodSend}, }, }.Matches(t, v.LastInvocation()) @@ -620,6 +625,69 @@ func TestAggregateOnePreCommitExpires(t *testing.T) { } +func TestAggregateSizeLimits(t *testing.T) { + overSizedBatch := 820 + ctx := context.Background() + blkStore := ipld.NewBlockStoreInMemory() + v := vm.NewVMWithSingletons(ctx, t, blkStore) + addrs := vm.CreateAccounts(ctx, t, v, 1, big.Mul(big.NewInt(10_000), big.NewInt(1e18)), 93837778) + + // create miner + sealProof := abi.RegisteredSealProof_StackedDrg32GiBV1_1 + wPoStProof, err := sealProof.RegisteredWindowPoStProof() + require.NoError(t, err) + owner, worker := addrs[0], addrs[0] + minerAddrs := createMiner(t, v, owner, worker, wPoStProof, big.Mul(big.NewInt(10_000), vm.FIL)) + + // advance vm so we can have seal randomness epoch in the past + v, err = v.WithEpoch(abi.ChainEpoch(200)) + require.NoError(t, err) + + // + // precommit sectors + // + firstSectorNo := abi.SectorNumber(100) + precommits := preCommitSectors(t, v, overSizedBatch, miner.PreCommitSectorBatchMaxSize, addrs[0], minerAddrs.IDAddress, sealProof, firstSectorNo, true) + balances := vm.GetMinerBalances(t, v, minerAddrs.IDAddress) + assert.True(t, balances.PreCommitDeposit.GreaterThan(big.Zero())) + + // advance time to max seal duration + proveTime := v.GetEpoch() + miner.MaxProveCommitDuration[sealProof] + v, _ = vm.AdvanceByDeadlineTillEpoch(t, v, minerAddrs.IDAddress, proveTime) + + // + // attempt proving with invalid args + // + + // Fail with too many sectors + v, err = v.WithEpoch(proveTime) + require.NoError(t, err) + sectorNosBf := precommitSectorNumbers(precommits) + + proveCommitAggregateTooManyParams := miner.ProveCommitAggregateParams{ + SectorNumbers: sectorNosBf, + } + res := v.ApplyMessage(addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.ProveCommitAggregate, &proveCommitAggregateTooManyParams) + assert.Equal(t, exitcode.ErrIllegalArgument, res.Code) // fail with too many aggregates + + // Fail with too few sectors + tooFewSectorNosBf := precommitSectorNumbers(precommits[:miner.MinAggregatedSectors-1]) + proveCommitAggregateTooFewParams := miner.ProveCommitAggregateParams{ + SectorNumbers: tooFewSectorNosBf, + } + res = v.ApplyMessage(addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.ProveCommitAggregate, &proveCommitAggregateTooFewParams) + assert.Equal(t, exitcode.ErrIllegalArgument, res.Code) + + // Fail with proof too big + justRightSectorNosBf := precommitSectorNumbers(precommits[:miner.MaxAggregatedSectors]) + proveCommitAggregateTooBigProofParams := miner.ProveCommitAggregateParams{ + SectorNumbers: justRightSectorNosBf, + AggregateProof: make([]byte, miner.MaxAggregateProofSize+1), + } + res = v.ApplyMessage(addrs[0], minerAddrs.RobustAddress, big.Zero(), builtin.MethodsMiner.ProveCommitAggregate, &proveCommitAggregateTooBigProofParams) + assert.Equal(t, exitcode.ErrIllegalArgument, res.Code) +} + func TestMeasureAggregatePorepGas(t *testing.T) { sectorCount := 819 fmt.Printf("batch size = %d\n", sectorCount) @@ -673,6 +741,7 @@ func TestMeasureAggregatePorepGas(t *testing.T) { {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward}, {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}, {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.UpdatePledgeTotal}, + {To: builtin.BurntFundsActorAddr, Method: builtin.MethodSend}, }, }.Matches(t, v.LastInvocation()) @@ -790,6 +859,7 @@ func proveCommitSectors(t *testing.T, v *vm.VM, worker, actor address.Address, p {To: builtin.RewardActorAddr, Method: builtin.MethodsReward.ThisEpochReward}, {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.CurrentTotalPower}, {To: builtin.StoragePowerActorAddr, Method: builtin.MethodsPower.UpdatePledgeTotal}, + {To: builtin.BurntFundsActorAddr, Method: builtin.MethodSend}, }, }.Matches(t, v.LastInvocation()) } diff --git a/support/mock/builder.go b/support/mock/builder.go index c8a9d6bea..3734b6721 100644 --- a/support/mock/builder.go +++ b/support/mock/builder.go @@ -39,6 +39,7 @@ func (b RuntimeBuilder) Build(t testing.TB) *Runtime { miner: addr.Address{}, idAddresses: make(map[addr.Address]addr.Address), circulatingSupply: abi.NewTokenAmount(0), + baseFee: abi.NewTokenAmount(0), state: cid.Undef, store: make(map[cid.Cid][]byte), diff --git a/support/mock/mockrt.go b/support/mock/mockrt.go index 441eac59e..476b546fe 100644 --- a/support/mock/mockrt.go +++ b/support/mock/mockrt.go @@ -46,6 +46,7 @@ type Runtime struct { actorCodeCIDs map[addr.Address]cid.Cid newActorAddr addr.Address circulatingSupply abi.TokenAmount + baseFee abi.TokenAmount // Actor state state cid.Cid @@ -256,6 +257,11 @@ func (rt *Runtime) CurrentBalance() abi.TokenAmount { return rt.balance } +func (rt *Runtime) BaseFee() abi.TokenAmount { + rt.requireInCall() + return rt.baseFee +} + func (rt *Runtime) ResolveAddress(address addr.Address) (ret addr.Address, ok bool) { rt.requireInCall() if address.Protocol() == addr.ID { diff --git a/support/vm/invocation_context.go b/support/vm/invocation_context.go index 90c2e534a..bf29d7d17 100644 --- a/support/vm/invocation_context.go +++ b/support/vm/invocation_context.go @@ -261,6 +261,11 @@ func (ic *invocationContext) CurrEpoch() abi.ChainEpoch { return ic.rt.currentEpoch } +func (ic *invocationContext) BaseFee() abi.TokenAmount { + // for now test vm always runs with zero base fee + return big.Zero() +} + func (ic *invocationContext) CurrentBalance() abi.TokenAmount { // load balance act, found, err := ic.rt.GetActor(ic.msg.to)