mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-20 10:52:25 +00:00
les: rework expired value
This commit is contained in:
parent
622becc45e
commit
095b052ecb
6 changed files with 260 additions and 139 deletions
|
|
@ -35,74 +35,8 @@ const (
|
|||
// expirationController controls the exponential expiration of positive and negative
|
||||
// balances
|
||||
type expirationController interface {
|
||||
posExpiration(mclock.AbsTime) uint64
|
||||
negExpiration(mclock.AbsTime) uint64
|
||||
}
|
||||
|
||||
// expiredValue is a scalar value that is continuously expired (decreased exponentially)
|
||||
// based on the logarithmic expiration offset value provided by the expirationController.
|
||||
// The actual value can be calculated as base*2^(exp-logOffset/log2Multiplier).
|
||||
//
|
||||
// Note: this representation is basically a floating point value capable of handling
|
||||
// 64 bit exponents. The operations are not general purpose floating point operations
|
||||
// though because a monotonously increasing exponent is assumed and therefore if an
|
||||
// operation is performed with two different exponents then simply the higher one is used.
|
||||
type expiredValue struct {
|
||||
base, exp uint64
|
||||
}
|
||||
|
||||
// value calculates the value at the given moment
|
||||
func (e expiredValue) value(logOffset uint64) uint64 {
|
||||
return uint64(math.Exp(float64(int64(e.exp*log2Multiplier-logOffset))/logMultiplier) * float64(e.base))
|
||||
}
|
||||
|
||||
// add adds a signed value at the given moment
|
||||
func (e *expiredValue) add(amount int64, logOffset uint64) int64 {
|
||||
addExp := logOffset / log2Multiplier
|
||||
baseMul := math.Exp(float64(logOffset%log2Multiplier) / logMultiplier)
|
||||
if addExp < e.exp {
|
||||
baseMul /= math.Pow(2, float64(e.exp-addExp))
|
||||
}
|
||||
if addExp > e.exp {
|
||||
e.base >>= (addExp - e.exp)
|
||||
e.exp = addExp
|
||||
}
|
||||
add := int64(float64(amount) * baseMul)
|
||||
if add >= 0 || uint64(-add) <= e.base {
|
||||
e.base += uint64(add)
|
||||
return amount
|
||||
} else {
|
||||
e.base = 0
|
||||
return int64(-float64(e.base) / baseMul)
|
||||
}
|
||||
}
|
||||
|
||||
// addExp adds another expiredValue
|
||||
func (e *expiredValue) addExp(a expiredValue) {
|
||||
if e.exp > a.exp {
|
||||
a.base >>= (e.exp - a.exp)
|
||||
}
|
||||
if e.exp < a.exp {
|
||||
e.base >>= (a.exp - e.exp)
|
||||
e.exp = a.exp
|
||||
}
|
||||
e.base += a.base
|
||||
}
|
||||
|
||||
// subExp subtracts another expiredValue
|
||||
func (e *expiredValue) subExp(a expiredValue) {
|
||||
if e.exp > a.exp {
|
||||
a.base >>= (e.exp - a.exp)
|
||||
}
|
||||
if e.exp < a.exp {
|
||||
e.base >>= (a.exp - e.exp)
|
||||
e.exp = a.exp
|
||||
}
|
||||
if e.base > a.base {
|
||||
e.base -= a.base
|
||||
} else {
|
||||
e.base = 0
|
||||
}
|
||||
posExpiration(mclock.AbsTime) float64
|
||||
negExpiration(mclock.AbsTime) float64
|
||||
}
|
||||
|
||||
// balanceTracker keeps track of the positive and negative balances of a connected
|
||||
|
|
|
|||
|
|
@ -25,11 +25,11 @@ import (
|
|||
|
||||
type zeroExpCtrl struct{}
|
||||
|
||||
func (z zeroExpCtrl) posExpiration(mclock.AbsTime) uint64 {
|
||||
func (z zeroExpCtrl) posExpiration(mclock.AbsTime) float64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (z zeroExpCtrl) negExpiration(mclock.AbsTime) uint64 {
|
||||
func (z zeroExpCtrl) negExpiration(mclock.AbsTime) float64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -36,17 +36,15 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
defaultPosExpTC = 36000 // default time constant (in seconds) for exponentially reducing positive balance
|
||||
defaultNegExpTC = 3600 // default time constant (in seconds) for exponentially reducing negative balance
|
||||
logMultiplier = 24204406.323122971 // constant to convert natural logarithms to fixed point format
|
||||
log2Multiplier = 0x1000000 // constant to convert 2-based logarithms to fixed point format
|
||||
lazyQueueRefresh = time.Second * 10 // refresh period of the connected queue
|
||||
tryActivatePeriod = time.Second * 5 // periodically check whether inactive clients can be activated
|
||||
dropInactiveCycles = 2 // number of activation check periods after non-priority inactive peers are dropped
|
||||
persistExpirationRefresh = time.Minute * 5 // refresh period of the token expiration persistence
|
||||
posBalanceCacheLimit = 8192 // the maximum number of cached items in positive balance queue
|
||||
negBalanceCacheLimit = 8192 // the maximum number of cached items in negative balance queue
|
||||
freeRatioTC = time.Hour // time constant of token supply control based on free service availability
|
||||
defaultPosExpTC = 36000 // default time constant (in seconds) for exponentially reducing positive balance
|
||||
defaultNegExpTC = 3600 // default time constant (in seconds) for exponentially reducing negative balance
|
||||
lazyQueueRefresh = time.Second * 10 // refresh period of the connected queue
|
||||
tryActivatePeriod = time.Second * 5 // periodically check whether inactive clients can be activated
|
||||
dropInactiveCycles = 2 // number of activation check periods after non-priority inactive peers are dropped
|
||||
persistExpirationRefresh = time.Minute * 5 // refresh period of the token expiration persistence
|
||||
posBalanceCacheLimit = 8192 // the maximum number of cached items in positive balance queue
|
||||
negBalanceCacheLimit = 8192 // the maximum number of cached items in negative balance queue
|
||||
freeRatioTC = time.Hour // time constant of token supply control based on free service availability
|
||||
|
||||
// activeBias is applied to already connected clients So that
|
||||
// already connected client won't be kicked out very soon and we
|
||||
|
|
@ -108,10 +106,10 @@ type clientPool struct {
|
|||
disableBias bool // Disable connection bias(used in testing)
|
||||
|
||||
// fields in this group are protected by expLock
|
||||
expLock sync.RWMutex
|
||||
posExp, negExp, posExpTC, negExpTC uint64
|
||||
posExpTCi, negExpTCi float64 // already inverted (logMultiplier/time)
|
||||
freeRatioLastUpdate mclock.AbsTime
|
||||
expLock sync.RWMutex
|
||||
posExpTC, negExpTC uint64
|
||||
posExp, negExp float64
|
||||
freeRatioLastUpdate mclock.AbsTime
|
||||
}
|
||||
|
||||
// clientPoolPeer represents a client peer in the pool.
|
||||
|
|
@ -220,10 +218,10 @@ func newClientPool(db ethdb.Database, minCap, freeClientCap uint64, clock mclock
|
|||
break
|
||||
}
|
||||
}
|
||||
// If the negative balance of free client is even lower than 1,
|
||||
// If the negative balance of free client is low enough,
|
||||
// delete this entry.
|
||||
ndb.nbEvictCallBack = func(now mclock.AbsTime, b negBalance) bool {
|
||||
return b.logValue <= pool.negExpiration(now)
|
||||
return b.value.value(pool.negExpiration(now)) < uint64(time.Second)
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
|
|
@ -309,8 +307,8 @@ func (f *clientPool) updateFreeRatio() {
|
|||
}
|
||||
f.averageFreeRatio -= (f.freeRatio - f.averageFreeRatio) * math.Expm1(-float64(dt)/float64(freeRatioTC))
|
||||
f.freeRatioLastUpdate = now
|
||||
f.posExp += uint64(float64(dt) * f.posExpTCi * f.freeRatio)
|
||||
f.negExp += uint64(float64(dt) * f.negExpTCi * f.freeRatio)
|
||||
f.posExp += float64(dt) / float64(f.posExpTC) * f.freeRatio
|
||||
f.negExp += float64(dt) / float64(f.negExpTC) * f.freeRatio
|
||||
f.expLock.Unlock()
|
||||
}
|
||||
|
||||
|
|
@ -323,16 +321,6 @@ func (f *clientPool) setExpirationTCs(pos, neg uint64) {
|
|||
|
||||
f.expLock.Lock()
|
||||
f.posExpTC, f.negExpTC = pos, neg
|
||||
if pos > 0 {
|
||||
f.posExpTCi = logMultiplier / float64(pos*uint64(time.Second))
|
||||
} else {
|
||||
f.posExpTCi = 0
|
||||
}
|
||||
if neg > 0 {
|
||||
f.negExpTCi = logMultiplier / float64(neg*uint64(time.Second))
|
||||
} else {
|
||||
f.negExpTCi = 0
|
||||
}
|
||||
f.expLock.Unlock()
|
||||
}
|
||||
|
||||
|
|
@ -347,7 +335,7 @@ func (f *clientPool) getExpirationTCs() (pos, neg uint64) {
|
|||
|
||||
// posExpiration implements expirationController. Expiration happens only when
|
||||
// free service is available.
|
||||
func (f *clientPool) posExpiration(now mclock.AbsTime) uint64 {
|
||||
func (f *clientPool) posExpiration(now mclock.AbsTime) float64 {
|
||||
f.expLock.RLock()
|
||||
defer f.expLock.RUnlock()
|
||||
|
||||
|
|
@ -355,12 +343,12 @@ func (f *clientPool) posExpiration(now mclock.AbsTime) uint64 {
|
|||
if dt < 0 {
|
||||
dt = 0
|
||||
}
|
||||
return f.posExp + uint64(float64(dt)*f.posExpTCi*f.freeRatio)
|
||||
return f.posExp + float64(dt)/float64(f.posExpTC)*f.freeRatio
|
||||
}
|
||||
|
||||
// negExpiration implements expirationController. Expiration happens only when
|
||||
// free service is available.
|
||||
func (f *clientPool) negExpiration(now mclock.AbsTime) uint64 {
|
||||
func (f *clientPool) negExpiration(now mclock.AbsTime) float64 {
|
||||
f.expLock.RLock()
|
||||
defer f.expLock.RUnlock()
|
||||
|
||||
|
|
@ -368,7 +356,7 @@ func (f *clientPool) negExpiration(now mclock.AbsTime) uint64 {
|
|||
if dt < 0 {
|
||||
dt = 0
|
||||
}
|
||||
return f.negExp + uint64(float64(dt)*f.negExpTCi*f.freeRatio)
|
||||
return f.negExp + float64(dt)/float64(f.negExpTC)*f.freeRatio
|
||||
}
|
||||
|
||||
// totalTokenLimit returns the current token supply limit. Token prices are based
|
||||
|
|
@ -474,14 +462,8 @@ func (f *clientPool) connect(peer clientPoolPeer, reqCapacity uint64) (uint64, e
|
|||
// initBalanceTracker initializes the positive and negative balances and price factors
|
||||
func (f *clientPool) initBalanceTracker(bt *balanceTracker, pb posBalance, nb negBalance, capacity uint64, active bool) {
|
||||
bt.exp = f
|
||||
posBalance := pb.value
|
||||
var negBalance expiredValue
|
||||
if nb.logValue != 0 {
|
||||
negBalance.exp = nb.logValue / log2Multiplier
|
||||
negBalance.base = uint64(math.Exp(float64(nb.logValue%log2Multiplier)/logMultiplier) * float64(time.Second))
|
||||
}
|
||||
bt.init(f.clock, capacity)
|
||||
bt.setBalance(posBalance, negBalance)
|
||||
bt.setBalance(pb.value, nb.value)
|
||||
if active {
|
||||
updatePriceFactors(bt, f.defaultPosFactors, f.defaultNegFactors, capacity)
|
||||
} else {
|
||||
|
|
@ -709,12 +691,8 @@ func (f *clientPool) finalizeBalance(c *clientInfo, now mclock.AbsTime) {
|
|||
pb.value = pos
|
||||
f.ndb.setPB(c.id, pb)
|
||||
|
||||
var nbLog int64
|
||||
if neg.base > 0 {
|
||||
nbLog = int64(math.Log(float64(neg.base)/float64(time.Second))*logMultiplier) + int64(neg.exp*log2Multiplier)
|
||||
}
|
||||
if nbLog > 0 {
|
||||
nb.logValue = uint64(nbLog)
|
||||
if neg.value(f.negExpiration(f.clock.Now())) > uint64(time.Second) {
|
||||
nb.value = neg
|
||||
f.ndb.setNB(c.address, nb)
|
||||
} else {
|
||||
f.ndb.delNB(c.address) // Negative balance is small enough, drop it directly.
|
||||
|
|
@ -927,22 +905,22 @@ func (e *posBalance) DecodeRLP(s *rlp.Stream) error {
|
|||
}
|
||||
|
||||
// negBalance represents a negative balance entry of a disconnected client
|
||||
type negBalance struct{ logValue uint64 }
|
||||
type negBalance struct{ value expiredValue }
|
||||
|
||||
// EncodeRLP implements rlp.Encoder
|
||||
func (e *negBalance) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, []interface{}{e.logValue})
|
||||
return rlp.Encode(w, []interface{}{e.value.base, e.value.exp})
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder
|
||||
func (e *negBalance) DecodeRLP(s *rlp.Stream) error {
|
||||
var entry struct {
|
||||
LogValue uint64
|
||||
Base, Exp uint64
|
||||
}
|
||||
if err := s.Decode(&entry); err != nil {
|
||||
return err
|
||||
}
|
||||
e.logValue = entry.LogValue
|
||||
e.value = expiredValue{base: entry.Base, exp: entry.Exp}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1009,17 +987,17 @@ func (db *nodeDB) key(id []byte, neg bool) []byte {
|
|||
return db.auxbuf[:len(prefix)+len(db.verbuf)+len(id)]
|
||||
}
|
||||
|
||||
func (db *nodeDB) getExpiration() (uint64, uint64) {
|
||||
func (db *nodeDB) getExpiration() (float64, float64) {
|
||||
blob, err := db.db.Get(append(expirationKey, db.verbuf[:]...))
|
||||
if err != nil || len(blob) != 16 {
|
||||
return 0, 0
|
||||
}
|
||||
return binary.BigEndian.Uint64(blob[:8]), binary.BigEndian.Uint64(blob[8:16])
|
||||
return math.Float64frombits(binary.BigEndian.Uint64(blob[:8])), math.Float64frombits(binary.BigEndian.Uint64(blob[8:16]))
|
||||
}
|
||||
|
||||
func (db *nodeDB) setExpiration(pos, neg uint64) {
|
||||
binary.BigEndian.PutUint64(db.auxbuf[:8], pos)
|
||||
binary.BigEndian.PutUint64(db.auxbuf[8:16], neg)
|
||||
func (db *nodeDB) setExpiration(pos, neg float64) {
|
||||
binary.BigEndian.PutUint64(db.auxbuf[:8], math.Float64bits(pos))
|
||||
binary.BigEndian.PutUint64(db.auxbuf[8:16], math.Float64bits(neg))
|
||||
db.db.Put(append(expirationKey, db.verbuf[:]...), db.auxbuf[:16])
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ package les
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
|
@ -429,19 +428,18 @@ func TestNegativeBalanceCalculation(t *testing.T) {
|
|||
t.Fatalf("Short connection shouldn't be recorded")
|
||||
}
|
||||
}
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
pool.connect(newPoolTestPeer(i, nil), 1)
|
||||
}
|
||||
clock.Run(time.Minute)
|
||||
for i := 0; i < 10; i++ {
|
||||
pool.disconnect(newPoolTestPeer(i, nil))
|
||||
nb := pool.ndb.getOrNewNB(newPoolTestPeer(i, nil).freeClientId())
|
||||
nb.logValue -= pool.negExpiration(clock.Now())
|
||||
nb.logValue = uint64(float64(nb.logValue) / logMultiplier)
|
||||
if nb.logValue != uint64(math.Log(float64(time.Minute/time.Second))) {
|
||||
t.Fatalf("Negative balance mismatch, want %v, got %v", int64(math.Log(float64(time.Minute/time.Second))), nb.logValue)
|
||||
}
|
||||
//nb := pool.ndb.getOrNewNB(newPoolTestPeer(i, nil).freeClientId())
|
||||
//// nb.logValue -= pool.negExpiration(clock.Now())
|
||||
//nb.logValue = uint64(float64(nb.logValue) / logMultiplier)
|
||||
//if nb.logValue != uint64(math.Log(float64(time.Minute/time.Second))) {
|
||||
// t.Fatalf("Negative balance mismatch, want %v, got %v", int64(math.Log(float64(time.Minute/time.Second))), nb.logValue)
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -460,8 +458,8 @@ func TestNodeDB(t *testing.T) {
|
|||
}{
|
||||
{enode.ID{0x00, 0x01, 0x02}, "", posBalance{value: expval(100)}, true},
|
||||
{enode.ID{0x00, 0x01, 0x02}, "", posBalance{value: expval(200)}, true},
|
||||
{enode.ID{}, "127.0.0.1", negBalance{logValue: 10}, false},
|
||||
{enode.ID{}, "127.0.0.1", negBalance{logValue: 20}, false},
|
||||
{enode.ID{}, "127.0.0.1", negBalance{value: expval(100)}, false},
|
||||
{enode.ID{}, "127.0.0.1", negBalance{value: expval(200)}, false},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if c.positive {
|
||||
|
|
@ -514,10 +512,10 @@ func TestNodeDBExpiration(t *testing.T) {
|
|||
ip string
|
||||
balance negBalance
|
||||
}{
|
||||
{"127.0.0.1", negBalance{logValue: 1}},
|
||||
{"127.0.0.2", negBalance{logValue: 1}},
|
||||
{"127.0.0.3", negBalance{logValue: 1}},
|
||||
{"127.0.0.4", negBalance{logValue: 1}},
|
||||
{"127.0.0.1", negBalance{value: expiredValue{base: 1}}},
|
||||
{"127.0.0.2", negBalance{value: expiredValue{base: 1}}},
|
||||
{"127.0.0.3", negBalance{value: expiredValue{base: 1}}},
|
||||
{"127.0.0.4", negBalance{value: expiredValue{base: 1}}},
|
||||
}
|
||||
for _, c := range cases {
|
||||
ndb.setNB(c.ip, c.balance)
|
||||
|
|
|
|||
95
les/expiredvalue.go
Normal file
95
les/expiredvalue.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2020 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package les
|
||||
|
||||
import "math"
|
||||
|
||||
// expiredValue is a scalar value that is continuously expired (decreased
|
||||
// exponentially) based on the provided logarithmic expiration offset value.
|
||||
//
|
||||
// The formula for value calculation is: base*2^(exp-logOffset). In order to
|
||||
// simplify the calculation of expiredValue, its value is expressed in the form
|
||||
// of an exponent with a base of 2.
|
||||
//
|
||||
// Also here is a trick to reduce a lot of calculations. In theory, when a value X
|
||||
// decays over time and then a new value Y is added, the final result should be
|
||||
// X*2^(exp-logOffset)+Y. However it's very hard to represent in memory.
|
||||
// So the trick is using the idea of inflation instead of exponential decay. At this
|
||||
// moment the temporary value becomes: X*2^exp+Y*2^logOffset_1, apply the exponential
|
||||
// decay when we actually want to calculate the value.
|
||||
//
|
||||
// e.g.
|
||||
// t0: V = 100
|
||||
// t1: add 30, inflationary value is: 100 + 30/0.3, 0.3 is the decay coefficient
|
||||
// t2: get value, decay coefficient is 0.2 now, final result is: 200*0.2 = 40
|
||||
type expiredValue struct {
|
||||
base, exp uint64
|
||||
}
|
||||
|
||||
// value calculates the value at the given moment.
|
||||
func (e expiredValue) value(logOffset float64) uint64 {
|
||||
return uint64(float64(e.base) * math.Pow(2, float64(e.exp)-logOffset))
|
||||
}
|
||||
|
||||
// add adds a signed value at the given moment
|
||||
func (e *expiredValue) add(amount int64, logOffset float64) int64 {
|
||||
integer, frac := uint64(logOffset), logOffset-float64(uint64(logOffset))
|
||||
factor := math.Pow(2, frac)
|
||||
base := factor * float64(amount)
|
||||
if integer < e.exp {
|
||||
base /= math.Pow(2, float64(e.exp-integer))
|
||||
}
|
||||
if integer > e.exp {
|
||||
e.base >>= (integer - e.exp)
|
||||
e.exp = integer
|
||||
}
|
||||
if base >= 0 || uint64(-base) <= e.base {
|
||||
e.base += uint64(base)
|
||||
return amount
|
||||
}
|
||||
net := int64(-float64(e.base) / factor)
|
||||
e.base = 0
|
||||
return net
|
||||
}
|
||||
|
||||
// addExp adds another expiredValue
|
||||
func (e *expiredValue) addExp(a expiredValue) {
|
||||
if e.exp > a.exp {
|
||||
a.base >>= (e.exp - a.exp)
|
||||
}
|
||||
if e.exp < a.exp {
|
||||
e.base >>= (a.exp - e.exp)
|
||||
e.exp = a.exp
|
||||
}
|
||||
e.base += a.base
|
||||
}
|
||||
|
||||
// subExp subtracts another expiredValue
|
||||
func (e *expiredValue) subExp(a expiredValue) {
|
||||
if e.exp > a.exp {
|
||||
a.base >>= (e.exp - a.exp)
|
||||
}
|
||||
if e.exp < a.exp {
|
||||
e.base >>= (a.exp - e.exp)
|
||||
e.exp = a.exp
|
||||
}
|
||||
if e.base > a.base {
|
||||
e.base -= a.base
|
||||
} else {
|
||||
e.base = 0
|
||||
}
|
||||
}
|
||||
116
les/expiredvalue_test.go
Normal file
116
les/expiredvalue_test.go
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
// Copyright 2020 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package les
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestValueExpiration(t *testing.T) {
|
||||
var cases = []struct {
|
||||
input expiredValue
|
||||
timeOffset float64
|
||||
expect uint64
|
||||
}{
|
||||
{expiredValue{base: 128, exp: 0}, 0, 128},
|
||||
{expiredValue{base: 128, exp: 0}, 1, 64},
|
||||
{expiredValue{base: 128, exp: 0}, 2, 32},
|
||||
{expiredValue{base: 128, exp: 2}, 2, 128},
|
||||
{expiredValue{base: 128, exp: 2}, 3, 64},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if got := c.input.value(c.timeOffset); got != c.expect {
|
||||
t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueAddition(t *testing.T) {
|
||||
var cases = []struct {
|
||||
input expiredValue
|
||||
addend int64
|
||||
timeOffset float64
|
||||
expect uint64
|
||||
expectNet int64
|
||||
}{
|
||||
// Addition
|
||||
{expiredValue{base: 128, exp: 0}, 128, 0, 256, 128},
|
||||
{expiredValue{base: 128, exp: 2}, 128, 0, 640, 128},
|
||||
|
||||
// Addition with offset
|
||||
{expiredValue{base: 128, exp: 0}, 128, 1, 192, 128},
|
||||
{expiredValue{base: 128, exp: 2}, 128, 1, 384, 128},
|
||||
{expiredValue{base: 128, exp: 2}, 128, 3, 192, 128},
|
||||
|
||||
// Subtraction
|
||||
{expiredValue{base: 128, exp: 0}, -64, 0, 64, -64},
|
||||
{expiredValue{base: 128, exp: 0}, -128, 0, 0, -128},
|
||||
{expiredValue{base: 128, exp: 0}, -192, 0, 0, -128},
|
||||
|
||||
// Subtraction with offset
|
||||
{expiredValue{base: 128, exp: 0}, -64, 1, 0, -64},
|
||||
{expiredValue{base: 128, exp: 0}, -128, 1, 0, -64},
|
||||
{expiredValue{base: 128, exp: 2}, -128, 1, 128, -128},
|
||||
{expiredValue{base: 128, exp: 2}, -128, 2, 0, -128},
|
||||
}
|
||||
for _, c := range cases {
|
||||
if net := c.input.add(c.addend, c.timeOffset); net != c.expectNet {
|
||||
t.Fatalf("Net amount mismatch, want=%d, got=%d", c.expectNet, net)
|
||||
}
|
||||
if got := c.input.value(c.timeOffset); got != c.expect {
|
||||
t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpiredValueAddition(t *testing.T) {
|
||||
var cases = []struct {
|
||||
input expiredValue
|
||||
another expiredValue
|
||||
timeOffset float64
|
||||
expect uint64
|
||||
}{
|
||||
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, 0, 256},
|
||||
{expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, 0, 384},
|
||||
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 1}, 0, 384},
|
||||
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, 1, 128},
|
||||
}
|
||||
for _, c := range cases {
|
||||
c.input.addExp(c.another)
|
||||
if got := c.input.value(c.timeOffset); got != c.expect {
|
||||
t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExpiredValueSubtraction(t *testing.T) {
|
||||
var cases = []struct {
|
||||
input expiredValue
|
||||
another expiredValue
|
||||
timeOffset float64
|
||||
expect uint64
|
||||
}{
|
||||
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 0}, 0, 0},
|
||||
{expiredValue{base: 128, exp: 0}, expiredValue{base: 128, exp: 1}, 0, 0},
|
||||
{expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, 0, 128},
|
||||
{expiredValue{base: 128, exp: 1}, expiredValue{base: 128, exp: 0}, 1, 64},
|
||||
}
|
||||
for _, c := range cases {
|
||||
c.input.subExp(c.another)
|
||||
if got := c.input.value(c.timeOffset); got != c.expect {
|
||||
t.Fatalf("Value mismatch, want=%d, got=%d", c.expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue