coin/coin.go
98 lines
1// Package coin computes the value of a coin entry over time: demurrage,2// rent, the spendable amount and prunability.3//4// The nominal amount stored in an entry never changes. Its effective value5// after t slots is amount × 0.983^(t/SlotsPerPeriod), computed by6// exponentiation by squaring in 63-bit fixed point:7//8// Decay[0] = floor(2^63 × 0.983^(1/SlotsPerPeriod))9// Decay[i+1] = (Decay[i] × Decay[i]) >> 63 i = 0 .. 2510//11// decay(amount, t):12// if t ≥ 2^27: return 013// x = amount14// for i in 0 .. 26: if bit i of t is set: x = (x × Decay[i]) >> 6315// return x16//17// Decay[i] is what a coin retains after 2^i slots. The squaring chain, not18// the real-valued curve, is the definition: every node computes the same19// integer. Beyond 2^27 slots the value is zero — the largest uint64 has20// decayed below one base unit long before that.21//22// Rent is one base unit per 2^RentShift slots, computed from the same t:23//24// rent(t) = t >> RentShift25// spendable = max(effective − rent, 0)26// prunable = rent ≥ effective27package coin2829import (30"math/bits"3132"peoplecoin/params"33)3435// DecayLen is the number of entries in the decay table; t ≥ 2^DecayLen36// decays to zero.37const DecayLen = 273839// Decay[i] is the fixed-point factor a coin retains after 2^i slots.40var Decay [DecayLen]uint644142func init() {43Decay[0] = params.Decay044for i := 0; i < DecayLen-1; i++ {45Decay[i+1] = mulShift(Decay[i], Decay[i])46}47}4849// mulShift returns (a × b) >> DecayShift using a 128-bit intermediate.50// With b < 2^63 the result always fits in 64 bits.51func mulShift(a, b uint64) uint64 {52hi, lo := bits.Mul64(a, b)53return hi<<(64-params.DecayShift) | lo>>params.DecayShift54}5556// Effective returns the value of amount after t slots of demurrage.57func Effective(amount, t uint64) uint64 {58if t >= 1<<DecayLen {59return 060}61x := amount62for i := 0; i < DecayLen && x != 0; i++ {63if t&(1<<uint(i)) != 0 {64x = mulShift(x, Decay[i])65}66}67return x68}6970// Rent returns the rent accrued over t slots.71func Rent(t uint64) uint64 {72return t >> params.RentShift73}7475// Spendable returns the effective value minus rent, saturating at zero.76func Spendable(amount, t uint64) uint64 {77e := Effective(amount, t)78r := Rent(t)79if r >= e {80return 081}82return e - r83}8485// Prunable reports whether rent has consumed the entry's effective value.86func Prunable(amount, t uint64) bool {87return Rent(t) >= Effective(amount, t)88}8990// Elapsed returns t = current − created, the slots an entry has existed;91// zero if current is earlier (which a valid chain never produces).92func Elapsed(created uint32, current uint64) uint64 {93if current < uint64(created) {94return 095}96return current - uint64(created)97}