github.com/filecoin-project/specs-actors/v4@v4.0.2/actors/builtin/miner/vesting_state.go (about)

     1  package miner
     2  
     3  import (
     4  	"sort"
     5  
     6  	"github.com/filecoin-project/go-state-types/abi"
     7  	"github.com/filecoin-project/go-state-types/big"
     8  )
     9  
    10  // VestingFunds represents the vesting table state for the miner.
    11  // It is a slice of (VestingEpoch, VestingAmount).
    12  // The slice will always be sorted by the VestingEpoch.
    13  type VestingFunds struct {
    14  	Funds []VestingFund
    15  }
    16  
    17  func (v *VestingFunds) unlockVestedFunds(currEpoch abi.ChainEpoch) abi.TokenAmount {
    18  	amountUnlocked := abi.NewTokenAmount(0)
    19  
    20  	lastIndexToRemove := -1
    21  	for i, vf := range v.Funds {
    22  		if vf.Epoch >= currEpoch {
    23  			break
    24  		}
    25  
    26  		amountUnlocked = big.Add(amountUnlocked, vf.Amount)
    27  		lastIndexToRemove = i
    28  	}
    29  
    30  	// remove all entries upto and including lastIndexToRemove
    31  	if lastIndexToRemove != -1 {
    32  		v.Funds = v.Funds[lastIndexToRemove+1:]
    33  	}
    34  
    35  	return amountUnlocked
    36  }
    37  
    38  func (v *VestingFunds) addLockedFunds(currEpoch abi.ChainEpoch, vestingSum abi.TokenAmount,
    39  	provingPeriodStart abi.ChainEpoch, spec *VestSpec) {
    40  	// maps the epochs in VestingFunds to their indices in the slice
    41  	epochToIndex := make(map[abi.ChainEpoch]int, len(v.Funds))
    42  	for i, vf := range v.Funds {
    43  		epochToIndex[vf.Epoch] = i
    44  	}
    45  
    46  	// Quantization is aligned with when regular cron will be invoked, in the last epoch of deadlines.
    47  	vestBegin := currEpoch + spec.InitialDelay // Nothing unlocks here, this is just the start of the clock.
    48  	vestPeriod := big.NewInt(int64(spec.VestPeriod))
    49  	vestedSoFar := big.Zero()
    50  	for e := vestBegin + spec.StepDuration; vestedSoFar.LessThan(vestingSum); e += spec.StepDuration {
    51  		vestEpoch := quantizeUp(e, spec.Quantization, provingPeriodStart)
    52  		elapsed := vestEpoch - vestBegin
    53  
    54  		targetVest := big.Zero() //nolint:ineffassign
    55  		if elapsed < spec.VestPeriod {
    56  			// Linear vesting
    57  			targetVest = big.Div(big.Mul(vestingSum, big.NewInt(int64(elapsed))), vestPeriod)
    58  		} else {
    59  			targetVest = vestingSum
    60  		}
    61  
    62  		vestThisTime := big.Sub(targetVest, vestedSoFar)
    63  		vestedSoFar = targetVest
    64  
    65  		// epoch already exists. Load existing entry
    66  		// and update amount.
    67  		if index, ok := epochToIndex[vestEpoch]; ok {
    68  			currentAmt := v.Funds[index].Amount
    69  			v.Funds[index].Amount = big.Add(currentAmt, vestThisTime)
    70  		} else {
    71  			// append a new entry -> slice will be sorted by epoch later.
    72  			entry := VestingFund{Epoch: vestEpoch, Amount: vestThisTime}
    73  			v.Funds = append(v.Funds, entry)
    74  			epochToIndex[vestEpoch] = len(v.Funds) - 1
    75  		}
    76  	}
    77  
    78  	// sort slice by epoch
    79  	sort.Slice(v.Funds, func(first, second int) bool {
    80  		return v.Funds[first].Epoch < v.Funds[second].Epoch
    81  	})
    82  }
    83  
    84  func (v *VestingFunds) unlockUnvestedFunds(currEpoch abi.ChainEpoch, target abi.TokenAmount) abi.TokenAmount {
    85  	amountUnlocked := abi.NewTokenAmount(0)
    86  	lastIndexToRemove := -1
    87  	startIndexForRemove := 0
    88  
    89  	// retain funds that should have vested and unlock unvested funds
    90  	for i, vf := range v.Funds {
    91  		if amountUnlocked.GreaterThanEqual(target) {
    92  			break
    93  		}
    94  
    95  		if vf.Epoch >= currEpoch {
    96  			unlockAmount := big.Min(big.Sub(target, amountUnlocked), vf.Amount)
    97  			amountUnlocked = big.Add(amountUnlocked, unlockAmount)
    98  			newAmount := big.Sub(vf.Amount, unlockAmount)
    99  
   100  			if newAmount.IsZero() {
   101  				lastIndexToRemove = i
   102  			} else {
   103  				v.Funds[i].Amount = newAmount
   104  			}
   105  		} else {
   106  			startIndexForRemove = i + 1
   107  		}
   108  	}
   109  
   110  	// remove all entries in [startIndexForRemove, lastIndexToRemove]
   111  	if lastIndexToRemove != -1 {
   112  		v.Funds = append(v.Funds[0:startIndexForRemove], v.Funds[lastIndexToRemove+1:]...)
   113  	}
   114  
   115  	return amountUnlocked
   116  }
   117  
   118  // VestingFund represents miner funds that will vest at the given epoch.
   119  type VestingFund struct {
   120  	Epoch  abi.ChainEpoch
   121  	Amount abi.TokenAmount
   122  }
   123  
   124  // ConstructVestingFunds constructs empty VestingFunds state.
   125  func ConstructVestingFunds() *VestingFunds {
   126  	v := new(VestingFunds)
   127  	v.Funds = nil
   128  	return v
   129  }