github.com/cryptogateway/go-paymex@v0.0.0-20210204174735-96277fb1e602/les/lespay/client/valuetracker_test.go (about)

     1  // Copyright 2020 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum 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-ethereum 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-ethereum 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/cryptogateway/go-paymex/common/mclock"
    27  	"github.com/cryptogateway/go-paymex/ethdb/memorydb"
    28  	"github.com/cryptogateway/go-paymex/p2p/enode"
    29  
    30  	"github.com/cryptogateway/go-paymex/les/utils"
    31  )
    32  
    33  const (
    34  	testReqTypes  = 3
    35  	testNodeCount = 5
    36  	testReqCount  = 10000
    37  	testRounds    = 10
    38  )
    39  
    40  func TestValueTracker(t *testing.T) {
    41  	db := memorydb.New()
    42  	clock := &mclock.Simulated{}
    43  	requestList := make([]RequestInfo, testReqTypes)
    44  	relPrices := make([]float64, testReqTypes)
    45  	totalAmount := make([]uint64, testReqTypes)
    46  	for i := range requestList {
    47  		requestList[i] = RequestInfo{Name: "testreq" + strconv.Itoa(i), InitAmount: 1, InitValue: 1}
    48  		totalAmount[i] = 1
    49  		relPrices[i] = rand.Float64() + 0.1
    50  	}
    51  	nodes := make([]*NodeValueTracker, testNodeCount)
    52  	for round := 0; round < testRounds; round++ {
    53  		makeRequests := round < testRounds-2
    54  		useExpiration := round == testRounds-1
    55  		var expRate float64
    56  		if useExpiration {
    57  			expRate = math.Log(2) / float64(time.Hour*100)
    58  		}
    59  
    60  		vt := NewValueTracker(db, clock, requestList, time.Minute, 1/float64(time.Hour), expRate, expRate)
    61  		updateCosts := func(i int) {
    62  			costList := make([]uint64, testReqTypes)
    63  			baseCost := rand.Float64()*10000000 + 100000
    64  			for j := range costList {
    65  				costList[j] = uint64(baseCost * relPrices[j])
    66  			}
    67  			vt.UpdateCosts(nodes[i], costList)
    68  		}
    69  		for i := range nodes {
    70  			nodes[i] = vt.Register(enode.ID{byte(i)})
    71  			updateCosts(i)
    72  		}
    73  		if makeRequests {
    74  			for i := 0; i < testReqCount; i++ {
    75  				reqType := rand.Intn(testReqTypes)
    76  				reqAmount := rand.Intn(10) + 1
    77  				node := rand.Intn(testNodeCount)
    78  				respTime := time.Duration((rand.Float64() + 1) * float64(time.Second) * float64(node+1) / testNodeCount)
    79  				totalAmount[reqType] += uint64(reqAmount)
    80  				vt.Served(nodes[node], []ServedRequest{{uint32(reqType), uint32(reqAmount)}}, respTime)
    81  				clock.Run(time.Second)
    82  			}
    83  		} else {
    84  			clock.Run(time.Hour * 100)
    85  			if useExpiration {
    86  				for i, a := range totalAmount {
    87  					totalAmount[i] = a / 2
    88  				}
    89  			}
    90  		}
    91  		vt.Stop()
    92  		var sumrp, sumrv float64
    93  		for i, rp := range relPrices {
    94  			sumrp += rp
    95  			sumrv += vt.refBasket.reqValues[i]
    96  		}
    97  		for i, rp := range relPrices {
    98  			ratio := vt.refBasket.reqValues[i] * sumrp / (rp * sumrv)
    99  			if ratio < 0.99 || ratio > 1.01 {
   100  				t.Errorf("reqValues (%v) does not match relPrices (%v)", vt.refBasket.reqValues, relPrices)
   101  				break
   102  			}
   103  		}
   104  		exp := utils.ExpFactor(vt.StatsExpirer().LogOffset(clock.Now()))
   105  		basketAmount := make([]uint64, testReqTypes)
   106  		for i, bi := range vt.refBasket.basket.items {
   107  			basketAmount[i] += uint64(exp.Value(float64(bi.amount), vt.refBasket.basket.exp))
   108  		}
   109  		if makeRequests {
   110  			// if we did not make requests in this round then we expect all amounts to be
   111  			// in the reference basket
   112  			for _, node := range nodes {
   113  				for i, bi := range node.basket.basket.items {
   114  					basketAmount[i] += uint64(exp.Value(float64(bi.amount), node.basket.basket.exp))
   115  				}
   116  			}
   117  		}
   118  		for i, a := range basketAmount {
   119  			amount := a / basketFactor
   120  			if amount+10 < totalAmount[i] || amount > totalAmount[i]+10 {
   121  				t.Errorf("totalAmount[%d] mismatch in round %d (expected %d, got %d)", i, round, totalAmount[i], amount)
   122  			}
   123  		}
   124  		var sumValue float64
   125  		for _, node := range nodes {
   126  			s := node.RtStats()
   127  			sumValue += s.Value(maxResponseWeights, exp)
   128  		}
   129  		s := vt.RtStats()
   130  		mainValue := s.Value(maxResponseWeights, exp)
   131  		if sumValue < mainValue-10 || sumValue > mainValue+10 {
   132  			t.Errorf("Main rtStats value does not match sum of node rtStats values in round %d (main %v, sum %v)", round, mainValue, sumValue)
   133  		}
   134  	}
   135  }