Documentation ¶
Index ¶
- func Calculate(balance math.Ndau, blockTime, lastEAICalc math.Timestamp, ...) (math.Ndau, error)
- type Lock
- type RSRow
- type RTRow
- func (z *RTRow) DecodeMsg(dc *msgp.Reader) (err error)
- func (z *RTRow) EncodeMsg(en *msgp.Writer) (err error)
- func (z *RTRow) MarshalMsg(b []byte) (o []byte, err error)
- func (r RTRow) MarshalText() ([]byte, error)
- func (z *RTRow) Msgsize() (s int)
- func (z *RTRow) UnmarshalMsg(bts []byte) (o []byte, err error)
- func (r *RTRow) UnmarshalText(text []byte) error
- type Rate
- type RateSlice
- type RateTable
- func (z *RateTable) DecodeMsg(dc *msgp.Reader) (err error)
- func (z RateTable) EncodeMsg(en *msgp.Writer) (err error)
- func (z RateTable) MarshalMsg(b []byte) (o []byte, err error)
- func (z RateTable) Msgsize() (s int)
- func (rt RateTable) RateAt(point math.Duration) Rate
- func (rt RateTable) Slice(from, to, offset math.Duration) RateSlice
- func (rt RateTable) SliceF(from, to, offset, freeze math.Duration) RateSlice
- func (z *RateTable) UnmarshalMsg(bts []byte) (o []byte, err error)
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Calculate ¶
func Calculate( balance math.Ndau, blockTime, lastEAICalc math.Timestamp, weightedAverageAge math.Duration, lock Lock, ageTable RateTable, fixUnlockBug bool, ) (math.Ndau, error)
Calculate the EAI due for a given account
EAI is not interest. Ndau never earns interest. However, for ease of explanation, we will borrow some terminology from interest rate calculations.
EAI is continuously compounded according to the formula
eai = balance * (e ^ (rate * time) - 1)
Rates are expressed in percent per year.
Thus, 100 ndau at 1 percent rate over 1 year yields 1.00501670 ndau EAI.
The use of continuously compounded interest instead of simple interest aids in EAI predictability: using simple interest, an account which simply accumulates its EAI, whose node won frequently, would see a higher rate of actual return than an identical account whose node won infrequently. Continuously compounded interest avoids that issue: both accounts will see the same rate of return; the benefit of the one registered to the frequent node is that it sees the increase more often.
Types ¶
type Lock ¶
type Lock interface { GetNoticePeriod() math.Duration GetUnlocksOn() *math.Timestamp GetBonusRate() Rate }
Lock is anything which acts like a lock struct.
If we want to use a Lock struct literal, we have two options:
- Implement it in this package and end up with a dependency on noms.
- Implement it in the ndaunode.backing package and end up with a dependency cycle.
We started with option 1, but a noms dependency from this low in the stack is not great. Instead, let's define a Lock interface which we can implement elsewhere.
type RSRow ¶
RSRow is a single row of a rate slice.
func (*RSRow) MarshalMsg ¶
MarshalMsg implements msgp.Marshaler
type RTRow ¶
RTRow is a single row of a rate table
func (*RTRow) MarshalMsg ¶
MarshalMsg implements msgp.Marshaler
func (RTRow) MarshalText ¶
MarshalText implements encoding.TextMarshaler
func (*RTRow) Msgsize ¶
Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
func (*RTRow) UnmarshalMsg ¶
UnmarshalMsg implements msgp.Unmarshaler
func (*RTRow) UnmarshalText ¶
UnmarshalText implements encoding.TextUnmarshaler
type Rate ¶
type Rate int64
A Rate defines a rate of increase over time.
EAI is not interest. Ndau never earns interest. However, for ease of explanation, we will borrow some terminology from interest rate calculations.
EAI is continuously compounded according to the formula
eai = balance * (e ^ (rate * time) - 1)
Rates are expressed in percent per year.
Thus, 100 ndau at 1 percent rate over 1 year yields 1.00501670 ndau EAI.
The use of continuously compounded interest instead of simple interest aids in EAI predictability: using simple interest, an account which simply accumulates its EAI, whose node won frequently, would see a higher rate of actual return than an identical account whose node won infrequently. Continuously compounded interest avoids that issue: both accounts will see the same rate of return; the benefit of the one registered to the frequent node is that it sees the increase more often.
We use a signed int so that json2msgp won't need type hints for encoding rate tables in system variables. If we use a rate denominator of 1e12, corresponding to a rate of 100%, then 63 bits gives us enough room to handle rates in the hundreds of millions of percents.
func CalculateEAIRate ¶
func CalculateEAIRate( weightedAverageAge math.Duration, lock Lock, unlockedTable RateTable, at math.Timestamp, ) Rate
CalculateEAIRate accepts a WAA, a lock, a rate table, and a calculation timestamp, and looks up the current EAI rate from that info. The rate is returned as a Rate: a newtype wrapping a uint64, with an implied denominator of constants.RateDenominator.
The timestamp is necessary in order to determine whether the lock is still notified, or the notice period has expired.
func RateFromPercent ¶
RateFromPercent returns a Rate whose value is that of the input, as percent.
i.e. to express 1%, `nPercent` should equal `1`
func (Rate) MarshalMsg ¶
MarshalMsg implements msgp.Marshaler
type RateSlice ¶
type RateSlice []RSRow
A RateSlice is derived from a RateTable, optimized for computation.
Whereas a RateTable is meant to be easy for humans to understand, a RateSlice is more efficient for computation. It is a list of rates, and the actual duration over which each rate is active.
func (RateSlice) MarshalMsg ¶
MarshalMsg implements msgp.Marshaler
type RateTable ¶
type RateTable []RTRow
A RateTable defines a stepped sequence of EAI rates which apply at varying durations.
It is a logic error if the elements of a RateTable are not sorted in increasing order by their From field.
var ( // DefaultUnlockedEAI is the default base rate table for unlocked accounts // // The UnlockedEAI rate table is a system variable which is adjustable // whenever the BPC desires, but for testing purposes, we use this // approximation as a default. // // Defaults drawn from https://tresor.it/p#0041o9iot7hm4kb5y707es7o/Oneiro%20Company%20Info/Whitepapers%20and%20Presentations/ndau%20Whitepaper%201.3%2020180425%20Final.pdf // page 15. DefaultUnlockedEAI RateTable // DefaultLockBonusEAI is the bonus rate for locks of varying length // // The LockBonusEAI rate table is a system variable which is adjustable // whenever the BPC desires, but for testing purposes, we use this // approximation as a default. // // Defaults drawn from https://tresor.it/p#0041o9iot7hm4kb5y707es7o/Oneiro%20Company%20Info/Whitepapers%20and%20Presentations/ndau%20Whitepaper%201.3%2020180425%20Final.pdf // page 15. DefaultLockBonusEAI RateTable )
func (RateTable) MarshalMsg ¶
MarshalMsg implements msgp.Marshaler
func (RateTable) Msgsize ¶
Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
func (RateTable) Slice ¶
Slice transforms a RateTable into a form more amenable for computation.
Rates vary over time, and we want to efficiently compute the sum of interest at varying rates. Instead of repeatedly calling RateAt, it's more efficient to perform the calculation once to slice the affected data out of the RateTable.
func (RateTable) SliceF ¶
SliceF transforms a RateTable into a form more amenable for computation.
Rates vary over time, and we want to efficiently compute the sum of interest at varying rates. Instead of repeatedly calling RateAt, it's more efficient to perform the calculation once to slice the affected data out of the RateTable.
Let's diagram the variables in play in here: (parentheticized variables are not present)
Timestamps │ (effective account open) │ │ (lastEAICalc) │ │ │ (notify) (blockTime) (lock.UnlocksOn)
TIME ─┼───┼───────────┼─────┼─────────────────────┼────────────┼──>
│ │ │ ├────── freeze ───────┤ │ │ │ │ └───────────── offset ─────────────┘ │ ├── from ───┴──── (lastEAICalcAge) ─────┤ │ └──────────────── to ───────────────────┘ Durations
Where freeze == 0, this function returns the rate slice from (from+offset) to (to+offset).
R3 ┌────|────────... R2 ┌────────────┘ / /| R1 ┌────|────────┘ / / / / / / / / | R0 ────────────┘ | / / / / / / / / / / / / /| (from+offset) (to+offset)
Where freeze != 0, this function returns the rate slice from (from+offset) to (to+offset), but with the actual rate frozen at the freeze point.
(This diagram is not to the same scale as the timeline overview above.)
R3 ┌────|────────... R2 ┌─────────|───────|── R1 ┌────|────────┘ / / / / | / / / | R0 ────────────┘ | / / / / / / / / /|/ / / /| (from+offset) | (to+offset) (to+offset-freeze)