github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/states/election_test.go (about)

     1  package states_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/filecoin-project/go-address"
     8  	"github.com/filecoin-project/go-state-types/abi"
     9  	"github.com/filecoin-project/go-state-types/big"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  
    13  	"github.com/filecoin-project/specs-actors/v4/actors/builtin"
    14  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/miner"
    15  	"github.com/filecoin-project/specs-actors/v4/actors/builtin/power"
    16  	"github.com/filecoin-project/specs-actors/v4/actors/states"
    17  	"github.com/filecoin-project/specs-actors/v4/actors/util/adt"
    18  	"github.com/filecoin-project/specs-actors/v4/support/ipld"
    19  	tutil "github.com/filecoin-project/specs-actors/v4/support/testing"
    20  )
    21  
    22  func TestMinerEligibleForElection(t *testing.T) {
    23  	ctx := context.Background()
    24  	store := ipld.NewADTStore(ctx)
    25  	proofType := abi.RegisteredPoStProof_StackedDrgWindow32GiBV1
    26  	pwr := abi.NewStoragePower(1)
    27  
    28  	owner := tutil.NewIDAddr(t, 100)
    29  	maddr := tutil.NewIDAddr(t, 101)
    30  
    31  	t.Run("miner eligible", func(t *testing.T) {
    32  		mstate := constructMinerState(ctx, t, store, owner)
    33  		pstate := constructPowerStateWithMiner(t, store, maddr, pwr, proofType)
    34  		assert.Equal(t, big.Zero(), mstate.InitialPledge) // Not directly relevant.
    35  
    36  		currEpoch := abi.ChainEpoch(100000)
    37  		eligible, err := states.MinerEligibleForElection(store, mstate, pstate, maddr, currEpoch)
    38  		require.NoError(t, err)
    39  		assert.True(t, eligible)
    40  	})
    41  
    42  	t.Run("zero claim", func(t *testing.T) {
    43  		mstate := constructMinerState(ctx, t, store, owner)
    44  		pstate := constructPowerStateWithMiner(t, store, maddr, big.Zero(), proofType)
    45  
    46  		currEpoch := abi.ChainEpoch(100000)
    47  		eligible, err := states.MinerEligibleForElection(store, mstate, pstate, maddr, currEpoch)
    48  		require.NoError(t, err)
    49  		assert.False(t, eligible)
    50  	})
    51  
    52  	t.Run("active consensus fault", func(t *testing.T) {
    53  		mstate := constructMinerState(ctx, t, store, owner)
    54  		pstate := constructPowerStateWithMiner(t, store, maddr, pwr, proofType)
    55  
    56  		info, err := mstate.GetInfo(store)
    57  		require.NoError(t, err)
    58  		info.ConsensusFaultElapsed = abi.ChainEpoch(55)
    59  		err = mstate.SaveInfo(store, info)
    60  		require.NoError(t, err)
    61  
    62  		currEpoch := abi.ChainEpoch(33) // 33 less than 55 so consensus fault still active
    63  		eligible, err := states.MinerEligibleForElection(store, mstate, pstate, maddr, currEpoch)
    64  		require.NoError(t, err)
    65  		assert.False(t, eligible)
    66  	})
    67  
    68  	t.Run("fee debt", func(t *testing.T) {
    69  		mstate := constructMinerState(ctx, t, store, owner)
    70  		pstate := constructPowerStateWithMiner(t, store, maddr, pwr, proofType)
    71  		mstate.FeeDebt = abi.NewTokenAmount(1000)
    72  
    73  		currEpoch := abi.ChainEpoch(100000)
    74  		eligible, err := states.MinerEligibleForElection(store, mstate, pstate, maddr, currEpoch)
    75  		require.NoError(t, err)
    76  		assert.False(t, eligible)
    77  	})
    78  }
    79  
    80  func TestMinerEligibleAtLookback(t *testing.T) {
    81  	ctx := context.Background()
    82  	store := ipld.NewADTStore(ctx)
    83  	windowPoStProofType := abi.RegisteredPoStProof_StackedDrgWindow32GiBV1
    84  	maddr := tutil.NewIDAddr(t, 101)
    85  
    86  	t.Run("power does not meet minimum", func(t *testing.T) {
    87  		// get minimums
    88  		pow32GiBMin, err := builtin.ConsensusMinerMinPower(windowPoStProofType)
    89  		require.NoError(t, err)
    90  		pow64GiBMin, err := builtin.ConsensusMinerMinPower(abi.RegisteredPoStProof_StackedDrgWindow64GiBV1)
    91  		require.NoError(t, err)
    92  
    93  		for _, tc := range []struct {
    94  			consensusMiners int64
    95  			minerProof      abi.RegisteredPoStProof
    96  			power           abi.StoragePower
    97  			eligible        bool
    98  		}{{
    99  			// below consensus minimum miners, power only needs to be positive to be eligible
   100  			consensusMiners: 0,
   101  			minerProof:      windowPoStProofType,
   102  			power:           big.Zero(),
   103  			eligible:        false,
   104  		}, {
   105  			consensusMiners: 0,
   106  			minerProof:      windowPoStProofType,
   107  			power:           big.NewInt(1),
   108  			eligible:        true,
   109  		}, {
   110  			// with enough miners above minimum, power must be at or above consensus min
   111  			consensusMiners: power.ConsensusMinerMinMiners,
   112  			minerProof:      windowPoStProofType,
   113  			power:           big.Sub(pow32GiBMin, big.NewInt(1)),
   114  			eligible:        false,
   115  		}, {
   116  			consensusMiners: power.ConsensusMinerMinMiners,
   117  			minerProof:      windowPoStProofType,
   118  			power:           pow32GiBMin,
   119  			eligible:        true,
   120  		}, {
   121  			// bigger sector size requires higher minimum
   122  			consensusMiners: power.ConsensusMinerMinMiners,
   123  			minerProof:      abi.RegisteredPoStProof_StackedDrgWindow64GiBV1,
   124  			power:           pow64GiBMin,
   125  			eligible:        true,
   126  		}} {
   127  			pstate := constructPowerStateWithMiner(t, store, maddr, tc.power, tc.minerProof)
   128  			pstate.MinerAboveMinPowerCount = tc.consensusMiners
   129  			eligible, err := states.MinerPoStLookbackEligibleForElection(store, pstate, maddr)
   130  			require.NoError(t, err)
   131  			assert.Equal(t, tc.eligible, eligible)
   132  		}
   133  	})
   134  }
   135  
   136  func constructMinerState(ctx context.Context, t *testing.T, store adt.Store, owner address.Address) *miner.State {
   137  	proofType := abi.RegisteredPoStProof_StackedDrgWindow32GiBV1
   138  	ssize, err := proofType.SectorSize()
   139  	require.NoError(t, err)
   140  	psize, err := builtin.PoStProofWindowPoStPartitionSectors(proofType)
   141  	require.NoError(t, err)
   142  
   143  	info := miner.MinerInfo{
   144  		Owner:                      owner,
   145  		Worker:                     owner,
   146  		ControlAddresses:           []address.Address{},
   147  		PendingWorkerKey:           nil,
   148  		PeerId:                     nil,
   149  		Multiaddrs:                 [][]byte{},
   150  		WindowPoStProofType:        proofType,
   151  		SectorSize:                 ssize,
   152  		WindowPoStPartitionSectors: psize,
   153  		ConsensusFaultElapsed:      0,
   154  	}
   155  	infoCid, err := store.Put(ctx, &info)
   156  	require.NoError(t, err)
   157  
   158  	periodStart := abi.ChainEpoch(0)
   159  
   160  	st, err := miner.ConstructState(store, infoCid, periodStart, 0)
   161  	require.NoError(t, err)
   162  
   163  	return st
   164  }
   165  
   166  func constructPowerStateWithMiner(t *testing.T, store adt.Store, maddr address.Address, pwr abi.StoragePower, proof abi.RegisteredPoStProof) *power.State {
   167  	pSt, err := power.ConstructState(store)
   168  	require.NoError(t, err)
   169  
   170  	claims, err := adt.AsMap(store, pSt.Claims, builtin.DefaultHamtBitwidth)
   171  	require.NoError(t, err)
   172  
   173  	claim := &power.Claim{WindowPoStProofType: proof, RawBytePower: pwr, QualityAdjPower: pwr}
   174  
   175  	err = claims.Put(abi.AddrKey(maddr), claim)
   176  	require.NoError(t, err)
   177  
   178  	pSt.MinerCount += 1
   179  
   180  	pSt.Claims, err = claims.Root()
   181  	require.NoError(t, err)
   182  	return pSt
   183  }