/
vesting_state.go
129 lines (107 loc) · 3.7 KB
/
vesting_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
package miner
import (
"sort"
"github.com/chenjianmei111/go-state-types/abi"
"github.com/chenjianmei111/go-state-types/big"
)
// VestingFunds represents the vesting table state for the miner.
// It is a slice of (VestingEpoch, VestingAmount).
// The slice will always be sorted by the VestingEpoch.
type VestingFunds struct {
Funds []VestingFund
}
func (v *VestingFunds) unlockVestedFunds(currEpoch abi.ChainEpoch) abi.TokenAmount {
amountUnlocked := abi.NewTokenAmount(0)
lastIndexToRemove := -1
for i, vf := range v.Funds {
if vf.Epoch >= currEpoch {
break
}
amountUnlocked = big.Add(amountUnlocked, vf.Amount)
lastIndexToRemove = i
}
// remove all entries upto and including lastIndexToRemove
if lastIndexToRemove != -1 {
v.Funds = v.Funds[lastIndexToRemove+1:]
}
return amountUnlocked
}
func (v *VestingFunds) addLockedFunds(currEpoch abi.ChainEpoch, vestingSum abi.TokenAmount,
provingPeriodStart abi.ChainEpoch, spec *VestSpec) {
// maps the epochs in VestingFunds to their indices in the slice
epochToIndex := make(map[abi.ChainEpoch]int, len(v.Funds))
for i, vf := range v.Funds {
epochToIndex[vf.Epoch] = i
}
// Quantization is aligned with when regular cron will be invoked, in the last epoch of deadlines.
vestBegin := currEpoch + spec.InitialDelay // Nothing unlocks here, this is just the start of the clock.
vestPeriod := big.NewInt(int64(spec.VestPeriod))
vestedSoFar := big.Zero()
for e := vestBegin + spec.StepDuration; vestedSoFar.LessThan(vestingSum); e += spec.StepDuration {
vestEpoch := quantizeUp(e, spec.Quantization, provingPeriodStart)
elapsed := vestEpoch - vestBegin
targetVest := big.Zero() //nolint:ineffassign
if elapsed < spec.VestPeriod {
// Linear vesting
targetVest = big.Div(big.Mul(vestingSum, big.NewInt(int64(elapsed))), vestPeriod)
} else {
targetVest = vestingSum
}
vestThisTime := big.Sub(targetVest, vestedSoFar)
vestedSoFar = targetVest
// epoch already exists. Load existing entry
// and update amount.
if index, ok := epochToIndex[vestEpoch]; ok {
currentAmt := v.Funds[index].Amount
v.Funds[index].Amount = big.Add(currentAmt, vestThisTime)
} else {
// append a new entry -> slice will be sorted by epoch later.
entry := VestingFund{Epoch: vestEpoch, Amount: vestThisTime}
v.Funds = append(v.Funds, entry)
epochToIndex[vestEpoch] = len(v.Funds) - 1
}
}
// sort slice by epoch
sort.Slice(v.Funds, func(first, second int) bool {
return v.Funds[first].Epoch < v.Funds[second].Epoch
})
}
func (v *VestingFunds) unlockUnvestedFunds(currEpoch abi.ChainEpoch, target abi.TokenAmount) abi.TokenAmount {
amountUnlocked := abi.NewTokenAmount(0)
lastIndexToRemove := -1
startIndexForRemove := 0
// retain funds that should have vested and unlock unvested funds
for i, vf := range v.Funds {
if amountUnlocked.GreaterThanEqual(target) {
break
}
if vf.Epoch >= currEpoch {
unlockAmount := big.Min(big.Sub(target, amountUnlocked), vf.Amount)
amountUnlocked = big.Add(amountUnlocked, unlockAmount)
newAmount := big.Sub(vf.Amount, unlockAmount)
if newAmount.IsZero() {
lastIndexToRemove = i
} else {
v.Funds[i].Amount = newAmount
}
} else {
startIndexForRemove = i + 1
}
}
// remove all entries in [startIndexForRemove, lastIndexToRemove]
if lastIndexToRemove != -1 {
v.Funds = append(v.Funds[0:startIndexForRemove], v.Funds[lastIndexToRemove+1:]...)
}
return amountUnlocked
}
// VestingFund represents miner funds that will vest at the given epoch.
type VestingFund struct {
Epoch abi.ChainEpoch
Amount abi.TokenAmount
}
// ConstructVestingFunds constructs empty VestingFunds state.
func ConstructVestingFunds() *VestingFunds {
v := new(VestingFunds)
v.Funds = nil
return v
}