github.com/core-coin/go-core/v2@v2.1.9/les/lespay/client/valuetracker_test.go (about)

     1  // Copyright 2020 by the Authors
     2  // This file is part of the go-core library.
     3  //
     4  // The go-core library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-core library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-core library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package client
    18  
    19  import (
    20  	"math"
    21  	"math/rand"
    22  	"strconv"
    23  	"testing"
    24  	"time"
    25  
    26  	"github.com/core-coin/go-core/v2/xcbdb/memorydb"
    27  
    28  	"github.com/core-coin/go-core/v2/common/mclock"
    29  	"github.com/core-coin/go-core/v2/p2p/enode"
    30  
    31  	"github.com/core-coin/go-core/v2/les/utils"
    32  )
    33  
    34  const (
    35  	testReqTypes  = 3
    36  	testNodeCount = 5
    37  	testReqCount  = 10000
    38  	testRounds    = 10
    39  )
    40  
    41  func TestValueTracker(t *testing.T) {
    42  	db := memorydb.New()
    43  	clock := &mclock.Simulated{}
    44  	requestList := make([]RequestInfo, testReqTypes)
    45  	relPrices := make([]float64, testReqTypes)
    46  	totalAmount := make([]uint64, testReqTypes)
    47  	for i := range requestList {
    48  		requestList[i] = RequestInfo{Name: "testreq" + strconv.Itoa(i), InitAmount: 1, InitValue: 1}
    49  		totalAmount[i] = 1
    50  		relPrices[i] = rand.Float64() + 0.1
    51  	}
    52  	nodes := make([]*NodeValueTracker, testNodeCount)
    53  	for round := 0; round < testRounds; round++ {
    54  		makeRequests := round < testRounds-2
    55  		useExpiration := round == testRounds-1
    56  		var expRate float64
    57  		if useExpiration {
    58  			expRate = math.Log(2) / float64(time.Hour*100)
    59  		}
    60  
    61  		vt := NewValueTracker(db, clock, requestList, time.Minute, 1/float64(time.Hour), expRate, expRate)
    62  		updateCosts := func(i int) {
    63  			costList := make([]uint64, testReqTypes)
    64  			baseCost := rand.Float64()*10000000 + 100000
    65  			for j := range costList {
    66  				costList[j] = uint64(baseCost * relPrices[j])
    67  			}
    68  			vt.UpdateCosts(nodes[i], costList)
    69  		}
    70  		for i := range nodes {
    71  			nodes[i] = vt.Register(enode.ID{byte(i)})
    72  			updateCosts(i)
    73  		}
    74  		if makeRequests {
    75  			for i := 0; i < testReqCount; i++ {
    76  				reqType := rand.Intn(testReqTypes)
    77  				reqAmount := rand.Intn(10) + 1
    78  				node := rand.Intn(testNodeCount)
    79  				respTime := time.Duration((rand.Float64() + 1) * float64(time.Second) * float64(node+1) / testNodeCount)
    80  				totalAmount[reqType] += uint64(reqAmount)
    81  				vt.Served(nodes[node], []ServedRequest{{uint32(reqType), uint32(reqAmount)}}, respTime)
    82  				clock.Run(time.Second)
    83  			}
    84  		} else {
    85  			clock.Run(time.Hour * 100)
    86  			if useExpiration {
    87  				for i, a := range totalAmount {
    88  					totalAmount[i] = a / 2
    89  				}
    90  			}
    91  		}
    92  		vt.Stop()
    93  		var sumrp, sumrv float64
    94  		for i, rp := range relPrices {
    95  			sumrp += rp
    96  			sumrv += vt.refBasket.reqValues[i]
    97  		}
    98  		for i, rp := range relPrices {
    99  			ratio := vt.refBasket.reqValues[i] * sumrp / (rp * sumrv)
   100  			if ratio < 0.99 || ratio > 1.01 {
   101  				t.Errorf("reqValues (%v) does not match relPrices (%v)", vt.refBasket.reqValues, relPrices)
   102  				break
   103  			}
   104  		}
   105  		exp := utils.ExpFactor(vt.StatsExpirer().LogOffset(clock.Now()))
   106  		basketAmount := make([]uint64, testReqTypes)
   107  		for i, bi := range vt.refBasket.basket.items {
   108  			basketAmount[i] += uint64(exp.Value(float64(bi.amount), vt.refBasket.basket.exp))
   109  		}
   110  		if makeRequests {
   111  			// if we did not make requests in this round then we expect all amounts to be
   112  			// in the reference basket
   113  			for _, node := range nodes {
   114  				for i, bi := range node.basket.basket.items {
   115  					basketAmount[i] += uint64(exp.Value(float64(bi.amount), node.basket.basket.exp))
   116  				}
   117  			}
   118  		}
   119  		for i, a := range basketAmount {
   120  			amount := a / basketFactor
   121  			if amount+10 < totalAmount[i] || amount > totalAmount[i]+10 {
   122  				t.Errorf("totalAmount[%d] mismatch in round %d (expected %d, got %d)", i, round, totalAmount[i], amount)
   123  			}
   124  		}
   125  		var sumValue float64
   126  		for _, node := range nodes {
   127  			s := node.RtStats()
   128  			sumValue += s.Value(maxResponseWeights, exp)
   129  		}
   130  		s := vt.RtStats()
   131  		mainValue := s.Value(maxResponseWeights, exp)
   132  		if sumValue < mainValue-10 || sumValue > mainValue+10 {
   133  			t.Errorf("Main rtStats value does not match sum of node rtStats values in round %d (main %v, sum %v)", round, mainValue, sumValue)
   134  		}
   135  	}
   136  }