-
Notifications
You must be signed in to change notification settings - Fork 35
/
power_state.go
177 lines (147 loc) · 6.49 KB
/
power_state.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package power
import (
"github.com/ipfs/go-cid"
"golang.org/x/xerrors"
addr "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"
"github.com/filecoin-project/go-state-types/builtin/v14/util/adt"
"github.com/filecoin-project/go-state-types/builtin/v14/util/smoothing"
)
// genesis power in bytes = 750,000 GiB
var InitialQAPowerEstimatePosition = big.Mul(big.NewInt(750_000), big.NewInt(1<<30))
// max chain throughput in bytes per epoch = 120 ProveCommits / epoch = 3,840 GiB
var InitialQAPowerEstimateVelocity = big.Mul(big.NewInt(3_840), big.NewInt(1<<30))
// Bitwidth of CronEventQueue HAMT determined empirically from mutation
// patterns and projections of mainnet data.
const CronQueueHamtBitwidth = 6
// Bitwidth of CronEventQueue AMT determined empirically from mutation
// patterns and projections of mainnet data.
const CronQueueAmtBitwidth = 6
// Bitwidth of ProofValidationBatch AMT determined empirically from mutation
// pattersn and projections of mainnet data.
const ProofValidationBatchAmtBitwidth = 4
// The number of miners that must meet the consensus minimum miner power before that minimum power is enforced
// as a condition of leader election.
// This ensures a network still functions before any miners reach that threshold.
const ConsensusMinerMinMiners = 4 // PARAM_SPEC
// PARAM_SPEC// Maximum number of prove-commits each miner can submit in one epoch.
//
// This limits the number of proof partitions we may need to load in the cron call path.
// Onboarding 1EiB/year requires at least 32 prove-commits per epoch.
const MaxMinerProveCommitsPerEpoch = 200 // PARAM_SPEC
type State struct {
TotalRawBytePower abi.StoragePower
// TotalBytesCommitted includes claims from miners below min power threshold
TotalBytesCommitted abi.StoragePower
TotalQualityAdjPower abi.StoragePower
// TotalQABytesCommitted includes claims from miners below min power threshold
TotalQABytesCommitted abi.StoragePower
TotalPledgeCollateral abi.TokenAmount
// These fields are set once per epoch in the previous cron tick and used
// for consistent values across a single epoch's state transition.
ThisEpochRawBytePower abi.StoragePower
ThisEpochQualityAdjPower abi.StoragePower
ThisEpochPledgeCollateral abi.TokenAmount
ThisEpochQAPowerSmoothed smoothing.FilterEstimate
MinerCount int64
// Number of miners having proven the minimum consensus power.
MinerAboveMinPowerCount int64
// A queue of events to be triggered by cron, indexed by epoch.
CronEventQueue cid.Cid // Multimap, (HAMT[ChainEpoch]AMT[CronEvent])
// First epoch in which a cron task may be stored.
// Cron will iterate every epoch between this and the current epoch inclusively to find tasks to execute.
FirstCronEpoch abi.ChainEpoch
// Claimed power for each miner.
Claims cid.Cid // Map, HAMT[address]Claim
ProofValidationBatch *cid.Cid // Multimap, (HAMT[Address]AMT[SealVerifyInfo])
}
func ConstructState(store adt.Store) (*State, error) {
emptyClaimsMapCid, err := adt.StoreEmptyMap(store, builtin.DefaultHamtBitwidth)
if err != nil {
return nil, xerrors.Errorf("failed to create empty map: %w", err)
}
emptyCronQueueMMapCid, err := adt.StoreEmptyMultimap(store, CronQueueHamtBitwidth, CronQueueAmtBitwidth)
if err != nil {
return nil, xerrors.Errorf("failed to create empty multimap: %w", err)
}
return &State{
TotalRawBytePower: abi.NewStoragePower(0),
TotalBytesCommitted: abi.NewStoragePower(0),
TotalQualityAdjPower: abi.NewStoragePower(0),
TotalQABytesCommitted: abi.NewStoragePower(0),
TotalPledgeCollateral: abi.NewTokenAmount(0),
ThisEpochRawBytePower: abi.NewStoragePower(0),
ThisEpochQualityAdjPower: abi.NewStoragePower(0),
ThisEpochPledgeCollateral: abi.NewTokenAmount(0),
ThisEpochQAPowerSmoothed: smoothing.NewEstimate(InitialQAPowerEstimatePosition, InitialQAPowerEstimateVelocity),
FirstCronEpoch: 0,
CronEventQueue: emptyCronQueueMMapCid,
Claims: emptyClaimsMapCid,
MinerCount: 0,
MinerAboveMinPowerCount: 0,
}, nil
}
type Claim struct {
// Miner's proof type used to determine minimum miner size
WindowPoStProofType abi.RegisteredPoStProof
// Sum of raw byte power for a miner's sectors.
RawBytePower abi.StoragePower
// Sum of quality adjusted power for a miner's sectors.
QualityAdjPower abi.StoragePower
}
type CronEvent struct {
MinerAddr addr.Address
CallbackPayload []byte
}
// MinerNominalPowerMeetsConsensusMinimum is used to validate Election PoSt
// winners outside the chain state. If the miner has over a threshold of power
// the miner meets the minimum. If the network is a below a threshold of
// miners and has power > zero the miner meets the minimum.
func (st *State) MinerNominalPowerMeetsConsensusMinimum(s adt.Store, miner addr.Address) (bool, error) { //nolint:deadcode,unused
claims, err := adt.AsMap(s, st.Claims, builtin.DefaultHamtBitwidth)
if err != nil {
return false, xerrors.Errorf("failed to load claims: %w", err)
}
claim, ok, err := getClaim(claims, miner)
if err != nil {
return false, err
}
if !ok {
return false, xerrors.Errorf("no claim for actor %w", miner)
}
minerNominalPower := claim.RawBytePower
minerMinPower, err := builtin.ConsensusMinerMinPower(claim.WindowPoStProofType)
if err != nil {
return false, xerrors.Errorf("could not get miner min power from proof type: %w", err)
}
// if miner is larger than min power requirement, we're set
if minerNominalPower.GreaterThanEqual(minerMinPower) {
return true, nil
}
// otherwise, if ConsensusMinerMinMiners miners meet min power requirement, return false
if st.MinerAboveMinPowerCount >= ConsensusMinerMinMiners {
return false, nil
}
// If fewer than ConsensusMinerMinMiners over threshold miner can win a block with non-zero power
return minerNominalPower.GreaterThan(abi.NewStoragePower(0)), nil
}
func (st *State) GetClaim(s adt.Store, a addr.Address) (*Claim, bool, error) {
claims, err := adt.AsMap(s, st.Claims, builtin.DefaultHamtBitwidth)
if err != nil {
return nil, false, xerrors.Errorf("failed to load claims: %w", err)
}
return getClaim(claims, a)
}
func getClaim(claims *adt.Map, a addr.Address) (*Claim, bool, error) {
var out Claim
found, err := claims.Get(abi.AddrKey(a), &out)
if err != nil {
return nil, false, xerrors.Errorf("failed to get claim for address %v: %w", a, err)
}
if !found {
return nil, false, nil
}
return &out, true, nil
}