requestbasket_test.go 6.1 KB

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