github.com/cryptogateway/go-paymex@v0.0.0-20210204174735-96277fb1e602/les/lespay/client/requestbasket_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/rand"
    21  	"testing"
    22  
    23  	"github.com/cryptogateway/go-paymex/les/utils"
    24  )
    25  
    26  func checkU64(t *testing.T, name string, value, exp uint64) {
    27  	if value != exp {
    28  		t.Errorf("Incorrect value for %s: got %d, expected %d", name, value, exp)
    29  	}
    30  }
    31  
    32  func checkF64(t *testing.T, name string, value, exp, tol float64) {
    33  	if value < exp-tol || value > exp+tol {
    34  		t.Errorf("Incorrect value for %s: got %f, expected %f", name, value, exp)
    35  	}
    36  }
    37  
    38  func TestServerBasket(t *testing.T) {
    39  	var s serverBasket
    40  	s.init(2)
    41  	// add some requests with different request value factors
    42  	s.updateRvFactor(1)
    43  	noexp := utils.ExpirationFactor{Factor: 1}
    44  	s.add(0, 1000, 10000, noexp)
    45  	s.add(1, 3000, 60000, noexp)
    46  	s.updateRvFactor(10)
    47  	s.add(0, 4000, 4000, noexp)
    48  	s.add(1, 2000, 4000, noexp)
    49  	s.updateRvFactor(10)
    50  	// check basket contents directly
    51  	checkU64(t, "s.basket[0].amount", s.basket.items[0].amount, 5000*basketFactor)
    52  	checkU64(t, "s.basket[0].value", s.basket.items[0].value, 50000)
    53  	checkU64(t, "s.basket[1].amount", s.basket.items[1].amount, 5000*basketFactor)
    54  	checkU64(t, "s.basket[1].value", s.basket.items[1].value, 100000)
    55  	// transfer 50% of the contents of the basket
    56  	transfer1 := s.transfer(0.5)
    57  	checkU64(t, "transfer1[0].amount", transfer1.items[0].amount, 2500*basketFactor)
    58  	checkU64(t, "transfer1[0].value", transfer1.items[0].value, 25000)
    59  	checkU64(t, "transfer1[1].amount", transfer1.items[1].amount, 2500*basketFactor)
    60  	checkU64(t, "transfer1[1].value", transfer1.items[1].value, 50000)
    61  	// add more requests
    62  	s.updateRvFactor(100)
    63  	s.add(0, 1000, 100, noexp)
    64  	// transfer 25% of the contents of the basket
    65  	transfer2 := s.transfer(0.25)
    66  	checkU64(t, "transfer2[0].amount", transfer2.items[0].amount, (2500+1000)/4*basketFactor)
    67  	checkU64(t, "transfer2[0].value", transfer2.items[0].value, (25000+10000)/4)
    68  	checkU64(t, "transfer2[1].amount", transfer2.items[1].amount, 2500/4*basketFactor)
    69  	checkU64(t, "transfer2[1].value", transfer2.items[1].value, 50000/4)
    70  }
    71  
    72  func TestConvertMapping(t *testing.T) {
    73  	b := requestBasket{items: []basketItem{{3, 3}, {1, 1}, {2, 2}}}
    74  	oldMap := []string{"req3", "req1", "req2"}
    75  	newMap := []string{"req1", "req2", "req3", "req4"}
    76  	init := requestBasket{items: []basketItem{{2, 2}, {4, 4}, {6, 6}, {8, 8}}}
    77  	bc := b.convertMapping(oldMap, newMap, init)
    78  	checkU64(t, "bc[0].amount", bc.items[0].amount, 1)
    79  	checkU64(t, "bc[1].amount", bc.items[1].amount, 2)
    80  	checkU64(t, "bc[2].amount", bc.items[2].amount, 3)
    81  	checkU64(t, "bc[3].amount", bc.items[3].amount, 4) // 8 should be scaled down to 4
    82  }
    83  
    84  func TestReqValueFactor(t *testing.T) {
    85  	var ref referenceBasket
    86  	ref.basket = requestBasket{items: make([]basketItem, 4)}
    87  	for i := range ref.basket.items {
    88  		ref.basket.items[i].amount = uint64(i+1) * basketFactor
    89  		ref.basket.items[i].value = uint64(i+1) * basketFactor
    90  	}
    91  	ref.init(4)
    92  	rvf := ref.reqValueFactor([]uint64{1000, 2000, 3000, 4000})
    93  	// expected value is (1000000+2000000+3000000+4000000) / (1*1000+2*2000+3*3000+4*4000) = 10000000/30000 = 333.333
    94  	checkF64(t, "reqValueFactor", rvf, 333.333, 1)
    95  }
    96  
    97  func TestNormalize(t *testing.T) {
    98  	for cycle := 0; cycle < 100; cycle += 1 {
    99  		// Initialize data for testing
   100  		valueRange, lower := 1000000, 1000000
   101  		ref := referenceBasket{basket: requestBasket{items: make([]basketItem, 10)}}
   102  		for i := 0; i < 10; i++ {
   103  			ref.basket.items[i].amount = uint64(rand.Intn(valueRange) + lower)
   104  			ref.basket.items[i].value = uint64(rand.Intn(valueRange) + lower)
   105  		}
   106  		ref.normalize()
   107  
   108  		// Check whether SUM(amount) ~= SUM(value)
   109  		var sumAmount, sumValue uint64
   110  		for i := 0; i < 10; i++ {
   111  			sumAmount += ref.basket.items[i].amount
   112  			sumValue += ref.basket.items[i].value
   113  		}
   114  		var epsilon = 0.01
   115  		if float64(sumAmount)*(1+epsilon) < float64(sumValue) || float64(sumAmount)*(1-epsilon) > float64(sumValue) {
   116  			t.Fatalf("Failed to normalize sumAmount: %d sumValue: %d", sumAmount, sumValue)
   117  		}
   118  	}
   119  }
   120  
   121  func TestReqValueAdjustment(t *testing.T) {
   122  	var s1, s2 serverBasket
   123  	s1.init(3)
   124  	s2.init(3)
   125  	cost1 := []uint64{30000, 60000, 90000}
   126  	cost2 := []uint64{100000, 200000, 300000}
   127  	var ref referenceBasket
   128  	ref.basket = requestBasket{items: make([]basketItem, 3)}
   129  	for i := range ref.basket.items {
   130  		ref.basket.items[i].amount = 123 * basketFactor
   131  		ref.basket.items[i].value = 123 * basketFactor
   132  	}
   133  	ref.init(3)
   134  	// initial reqValues are expected to be {1, 1, 1}
   135  	checkF64(t, "reqValues[0]", ref.reqValues[0], 1, 0.01)
   136  	checkF64(t, "reqValues[1]", ref.reqValues[1], 1, 0.01)
   137  	checkF64(t, "reqValues[2]", ref.reqValues[2], 1, 0.01)
   138  	var logOffset utils.Fixed64
   139  	for period := 0; period < 1000; period++ {
   140  		exp := utils.ExpFactor(logOffset)
   141  		s1.updateRvFactor(ref.reqValueFactor(cost1))
   142  		s2.updateRvFactor(ref.reqValueFactor(cost2))
   143  		// throw in random requests into each basket using their internal pricing
   144  		for i := 0; i < 1000; i++ {
   145  			reqType, reqAmount := uint32(rand.Intn(3)), uint32(rand.Intn(10)+1)
   146  			reqCost := uint64(reqAmount) * cost1[reqType]
   147  			s1.add(reqType, reqAmount, reqCost, exp)
   148  			reqType, reqAmount = uint32(rand.Intn(3)), uint32(rand.Intn(10)+1)
   149  			reqCost = uint64(reqAmount) * cost2[reqType]
   150  			s2.add(reqType, reqAmount, reqCost, exp)
   151  		}
   152  		ref.add(s1.transfer(0.1))
   153  		ref.add(s2.transfer(0.1))
   154  		ref.normalize()
   155  		ref.updateReqValues()
   156  		logOffset += utils.Float64ToFixed64(0.1)
   157  	}
   158  	checkF64(t, "reqValues[0]", ref.reqValues[0], 0.5, 0.01)
   159  	checkF64(t, "reqValues[1]", ref.reqValues[1], 1, 0.01)
   160  	checkF64(t, "reqValues[2]", ref.reqValues[2], 1.5, 0.01)
   161  }