decred.org/dcrwallet/v3@v3.1.0/wallet/txrules/poolfees.go (about)

     1  // Copyright (c) 2016 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package txrules
     6  
     7  import (
     8  	"math"
     9  	"math/big"
    10  	"sync"
    11  
    12  	blockchain "github.com/decred/dcrd/blockchain/standalone/v2"
    13  	"github.com/decred/dcrd/chaincfg/v3"
    14  	"github.com/decred/dcrd/dcrutil/v4"
    15  )
    16  
    17  // ValidPoolFeeRate tests to see if a pool fee is a valid percentage from
    18  // 0.01% to 100.00%.
    19  func ValidPoolFeeRate(feeRate float64) bool {
    20  	poolFeeRateTest := feeRate * 100
    21  	poolFeeRateTest = math.Floor(poolFeeRateTest)
    22  	return poolFeeRateTest >= 1.0 && poolFeeRateTest <= 10000.0
    23  }
    24  
    25  var subsidyCache *blockchain.SubsidyCache
    26  var initSubsidyCacheOnce sync.Once
    27  
    28  // StakePoolTicketFee determines the stake pool ticket fee for a given ticket
    29  // from the passed percentage. Pool fee as a percentage is truncated from 0.01%
    30  // to 100.00%. This all must be done with integers, so bear with the big.Int
    31  // usage below.
    32  //
    33  // See the included doc.go of this package for more information about the
    34  // calculation of this fee.
    35  func StakePoolTicketFee(stakeDiff dcrutil.Amount, relayFee dcrutil.Amount,
    36  	height int32, poolFee float64, params *chaincfg.Params,
    37  	dcp0010Active bool) dcrutil.Amount {
    38  	// Shift the decimal two places, e.g. 1.00%
    39  	// to 100. This assumes that the proportion
    40  	// is already multiplied by 100 to give a
    41  	// percentage, thus making the entirety
    42  	// be a multiplication by 10000.
    43  	poolFeeAbs := math.Floor(poolFee * 100.0)
    44  	poolFeeInt := int64(poolFeeAbs)
    45  
    46  	// Subsidy is fetched from the blockchain package, then
    47  	// pushed forward a number of adjustment periods for
    48  	// compensation in gradual subsidy decay. Recall that
    49  	// the average time to claiming 50% of the tickets as
    50  	// votes is the approximately the same as the ticket
    51  	// pool size (params.TicketPoolSize), so take the
    52  	// ceiling of the ticket pool size divided by the
    53  	// reduction interval.
    54  	adjs := int(math.Ceil(float64(params.TicketPoolSize) /
    55  		float64(params.SubsidyReductionInterval)))
    56  	initSubsidyCacheOnce.Do(func() {
    57  		subsidyCache = blockchain.NewSubsidyCache(params)
    58  	})
    59  	subsidy := subsidyCache.CalcStakeVoteSubsidyV2(int64(height),
    60  		dcp0010Active)
    61  	for i := 0; i < adjs; i++ {
    62  		subsidy *= 100
    63  		subsidy /= 101
    64  	}
    65  
    66  	// The numerator is (p*10000*s*(v+z)) << 64.
    67  	shift := uint(64)
    68  	s := new(big.Int).SetInt64(subsidy)
    69  	v := new(big.Int).SetInt64(int64(stakeDiff))
    70  	z := new(big.Int).SetInt64(int64(relayFee))
    71  	num := new(big.Int).SetInt64(poolFeeInt)
    72  	num.Mul(num, s)
    73  	vPlusZ := new(big.Int).Add(v, z)
    74  	num.Mul(num, vPlusZ)
    75  	num.Lsh(num, shift)
    76  
    77  	// The denominator is 10000*(s+v).
    78  	// The extra 10000 above cancels out.
    79  	den := new(big.Int).Set(s)
    80  	den.Add(den, v)
    81  	den.Mul(den, new(big.Int).SetInt64(10000))
    82  
    83  	// Divide and shift back.
    84  	num.Div(num, den)
    85  	num.Rsh(num, shift)
    86  
    87  	return dcrutil.Amount(num.Int64())
    88  }