Skip to content
This repository has been archived by the owner on Jun 6, 2023. It is now read-only.

Commit

Permalink
Minting function maintainability (#356)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidad committed May 9, 2020
1 parent 42be662 commit 3c96f54
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 27 deletions.
7 changes: 5 additions & 2 deletions actors/builtin/reward/reward_actor.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,11 @@ func (a Actor) LastPerEpochReward(rt vmr.Runtime, _ *adt.EmptyValue) *abi.TokenA

func (a Actor) computePerEpochReward(st *State, clockTime abi.ChainEpoch, networkTime abi.ChainEpoch, ticketCount int64) abi.TokenAmount {
// TODO: PARAM_FINISH
newSimpleSupply := big.Rsh(big.Mul(SimpleTotal, taylorSeriesExpansion(clockTime)), FixedPoint)
newBaselineSupply := big.Rsh(big.Mul(BaselineTotal, taylorSeriesExpansion(networkTime)), FixedPoint)
newSimpleSupply := mintingFunction(SimpleTotal, big.Lsh(big.NewInt(int64(clockTime)), MintingInputFixedPoint))

// TODO: when network time calculation produces a fixed-point representation
// with a fractional part of MintingInputFixedPoint, remove this Lsh
newBaselineSupply := mintingFunction(BaselineTotal, big.Lsh(big.NewInt(int64(networkTime)), MintingInputFixedPoint))

newSimpleMinted := big.Max(big.Sub(newSimpleSupply, st.SimpleSupply), big.Zero())
newBaselineMinted := big.Max(big.Sub(newBaselineSupply, st.BaselineSupply), big.Zero())
Expand Down
66 changes: 46 additions & 20 deletions actors/builtin/reward/reward_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (

abi "github.com/filecoin-project/specs-actors/actors/abi"
big "github.com/filecoin-project/specs-actors/actors/abi/big"
builtin "github.com/filecoin-project/specs-actors/actors/builtin"
adt "github.com/filecoin-project/specs-actors/actors/util/adt"
)

Expand Down Expand Up @@ -63,13 +64,24 @@ func ConstructState(emptyMultiMapCid cid.Cid) *State {
// have resorted to using an ad-hoc fixed-point standard. Experimental
// testing with the relevant scales of inputs and with a desired "atto"
// level of output precision yielded a recommendation of a 97-bit fractional
// part, which was stored in the constant "FixedPoint".
// !IMPORTANT!: the return value from this function is a factor of 2^FixedPoint
// greater than the number it is semantically intended to represent (which
// will always be between 0 and 1). The expectation is that callers will
// multiply the result by some number, and THEN right-shift the result of
// the multiplication by FixedPoint bits, thus implementing fixed-point
// multiplication by the returned fraction.
// part, which was stored in the constant "MintingOutputFixedPoint".
// Fractional input is only necessary when considering "effective network
// time"; there, the desired precision is determined by the minimum
// plausible ratio between realized network power and network baseline,
// which is set in "MintingInputFixedPoint".
//
// !IMPORTANT!: the time input to this function should be a factor of
// 2^MintingInputFixedPoint greater than the semantically intended value, and
// the return value from this function is a factor of 2^MintingOutputFixedPoint
// greater. The semantics of the output value will always be a fraction
// between 0 and 1, but will be represented as an integer between 0 and
// 2^FixedPoint. The expectation is that callers will multiply the result by
// some number, and THEN right-shift the result of the multiplication by
// FixedPoint bits, thus implementing fixed-point multiplication by the
// returned fraction. Analogously, if callers intend to pass in an integer
// "t", it should be left-shifted by MintingInputFixedPoint before being
// passed; if it is fractional, its fractional part should be
// MintingInputFixedPoint bits long.
//
// 2. Since we do not have a math library in this setting, we cannot directly
// implement the intended closed form using stock implementations of
Expand Down Expand Up @@ -131,12 +143,11 @@ func ConstructState(emptyMultiMapCid cid.Cid) *State {
// from both the numerator and denominator accumulators to control the
// computational complexity of the bigint multiplications.

// Fixed-point precision (in bits) used internally and for output
const FixedPoint = 97
// Fixed-point precision (in bits) used for minting function's input "t"
const MintingInputFixedPoint = 30

// Used in the definition of λ
const BlockTimeSeconds = 30
const SecondsInYear = 31556925
// Fixed-point precision (in bits) used internally and for output
const MintingOutputFixedPoint = 97

// The following are the numerator and denominator of -ln(1/2)=ln(2),
// represented as a rational with sufficient precision. They are parsed from
Expand All @@ -148,18 +159,19 @@ var LnTwoDen, _ = big.FromString("10000000000000000000000000000")
// We multiply the fraction ([Seconds per epoch] / (6 * [Seconds per year]))
// into the rational representation of -ln(1/2) which was just loaded, to
// produce the final, constant, rational representation of λ.
var LambdaNum = big.Mul(big.NewInt(BlockTimeSeconds), LnTwoNum)
var LambdaDen = big.Mul(big.NewInt(6*SecondsInYear), LnTwoDen)
var LambdaNum = big.Mul(big.NewInt(builtin.EpochDurationSeconds), LnTwoNum)
var LambdaDen = big.Mul(big.NewInt(6*builtin.SecondsInYear), LnTwoDen)

// This function implements f(t) as described in the large comment block above,
// with the important caveat that its return value must not be interpreted
// semantically as an integer, but rather as a fixed-point number with
// FixedPoint bits of fractional part.
func taylorSeriesExpansion(t abi.ChainEpoch) big.Int {
func taylorSeriesExpansion(lambdaNum big.Int, lambdaDen big.Int, t big.Int) big.Int {
// `numeratorBase` is the numerator of the rational representation of (-λt).
numeratorBase := big.Mul(LambdaNum.Neg(), big.NewInt(int64(t)))
// The denominator of (-λt) is simply the denominator of λ, as -t is integral.
denominatorBase := LambdaDen
numeratorBase := big.Mul(lambdaNum.Neg(), t)
// The denominator of (-λt) is the denominator of λ times the denominator of t,
// which is a fixed 2^MintingInputFixedPoint. Multiplying by this is a left shift.
denominatorBase := big.Lsh(lambdaDen, MintingInputFixedPoint)

// `numerator` is the accumulator for numerators of the series terms. The
// first term is simply (-1)(-λt). To include that factor of (-1), which
Expand All @@ -184,7 +196,7 @@ func taylorSeriesExpansion(t abi.ChainEpoch) big.Int {
denominator = big.Mul(denominator, big.NewInt(n))

// Left-shift and divide to convert rational into fixed-point.
term := big.Div(big.Lsh(numerator, FixedPoint), denominator)
term := big.Div(big.Lsh(numerator, MintingOutputFixedPoint), denominator)

// Accumulate the fixed-point result into the return accumulator.
ret = big.Add(ret, term)
Expand All @@ -201,7 +213,7 @@ func taylorSeriesExpansion(t abi.ChainEpoch) big.Int {
// by the same number of bits, all we have done is lose unnecessary
// precision that would slow down the next iteration's multiplies.
denominatorLen := big.BitLen(denominator)
unnecessaryBits := denominatorLen - FixedPoint
unnecessaryBits := denominatorLen - MintingOutputFixedPoint
if unnecessaryBits < 0 {
unnecessaryBits = 0
}
Expand All @@ -212,3 +224,17 @@ func taylorSeriesExpansion(t abi.ChainEpoch) big.Int {

return ret
}

// Minting Function Wrapper
//
// Intent
// The necessary calling conventions for the function above are unwieldy:
// the common case is to supply the canonical Lambda, multiply by some other
// number, and right-shift down by MintingOutputFixedPoint. This convenience
// wrapper implements those conventions. However, it does NOT implement
// left-shifting the input by the MintingInputFixedPoint, because baseline
// minting will (soon) actually supply a fractional input, so this would only
// be used for simple minting.
func mintingFunction(factor big.Int, t big.Int) big.Int {
return big.Rsh(big.Mul(factor, taylorSeriesExpansion(LambdaNum, LambdaDen, t)), MintingOutputFixedPoint)
}
20 changes: 15 additions & 5 deletions actors/builtin/reward/reward_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package reward
import (
"testing"

"github.com/filecoin-project/specs-actors/actors/abi"
"github.com/filecoin-project/specs-actors/actors/abi/big"
)

Expand All @@ -26,7 +25,7 @@ import (
var mintingTestVectorPrecision = uint(90)

var mintingTestVectors = []struct {
in abi.ChainEpoch
in int64
out string
}{
{1051897, "135060784589637453410950129"},
Expand All @@ -38,20 +37,31 @@ var mintingTestVectors = []struct {
{7363279, "686500230252085183344830372"},
}

const SecondsInYear = 31556925
const TestEpochDurationSeconds = 30

var TestLambdaNum = big.Mul(big.NewInt(TestEpochDurationSeconds), LnTwoNum)
var TestLambdaDen = big.Mul(big.NewInt(6*SecondsInYear), LnTwoDen)

func TestMintingFunction(t *testing.T) {
for _, vector := range mintingTestVectors {
ts_output := taylorSeriesExpansion(vector.in)
// In order to supply an integer as an input to the minting function, we
// first left-shift zeroes into the fractional part of its fixed-point
// representation.
ts_input := big.Lsh(big.NewInt(vector.in), MintingInputFixedPoint)

ts_output := taylorSeriesExpansion(TestLambdaNum, TestLambdaDen, ts_input)

// ts_output will always range between 0 and 2^FixedPoint. If we
// right-shifted by FixedPoint, without first multiplying by something, we
// would discard _all_ the bits and get 0. Instead, we want to discard only
// those bits in the FixedPoint representation that we don't also want to
// require to exactly match the test vectors.
ts_truncated_fractional_part := big.Rsh(ts_output, FixedPoint-mintingTestVectorPrecision)
ts_truncated_fractional_part := big.Rsh(ts_output, MintingOutputFixedPoint-mintingTestVectorPrecision)

expected_truncated_fractional_part, _ := big.FromString(vector.out)
if !(ts_truncated_fractional_part.Equals(expected_truncated_fractional_part)) {
t.Errorf("at epoch %q, computed supply %q, expected supply %q", vector.in, ts_truncated_fractional_part, expected_truncated_fractional_part)
t.Errorf("minting function: on input %q, computed %q, expected %q", ts_input, ts_truncated_fractional_part, expected_truncated_fractional_part)
}
}
}

0 comments on commit 3c96f54

Please sign in to comment.