clientpool-fuzzer.go 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. // Copyright 2021 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 vflux
  17. import (
  18. "bytes"
  19. "encoding/binary"
  20. "io"
  21. "math"
  22. "math/big"
  23. "time"
  24. "github.com/ethereum/go-ethereum/common/mclock"
  25. "github.com/ethereum/go-ethereum/ethdb/memorydb"
  26. "github.com/ethereum/go-ethereum/les/vflux"
  27. vfs "github.com/ethereum/go-ethereum/les/vflux/server"
  28. "github.com/ethereum/go-ethereum/p2p/enode"
  29. "github.com/ethereum/go-ethereum/p2p/enr"
  30. "github.com/ethereum/go-ethereum/rlp"
  31. )
  32. type fuzzer struct {
  33. peers [256]*clientPeer
  34. disconnectList []*clientPeer
  35. input io.Reader
  36. exhausted bool
  37. activeCount, activeCap uint64
  38. maxCount, maxCap uint64
  39. }
  40. type clientPeer struct {
  41. fuzzer *fuzzer
  42. node *enode.Node
  43. freeID string
  44. timeout time.Duration
  45. balance vfs.ConnectedBalance
  46. capacity uint64
  47. }
  48. func (p *clientPeer) Node() *enode.Node {
  49. return p.node
  50. }
  51. func (p *clientPeer) FreeClientId() string {
  52. return p.freeID
  53. }
  54. func (p *clientPeer) InactiveAllowance() time.Duration {
  55. return p.timeout
  56. }
  57. func (p *clientPeer) UpdateCapacity(newCap uint64, requested bool) {
  58. p.fuzzer.activeCap -= p.capacity
  59. if p.capacity != 0 {
  60. p.fuzzer.activeCount--
  61. }
  62. p.capacity = newCap
  63. p.fuzzer.activeCap += p.capacity
  64. if p.capacity != 0 {
  65. p.fuzzer.activeCount++
  66. }
  67. }
  68. func (p *clientPeer) Disconnect() {
  69. p.fuzzer.disconnectList = append(p.fuzzer.disconnectList, p)
  70. p.fuzzer.activeCap -= p.capacity
  71. if p.capacity != 0 {
  72. p.fuzzer.activeCount--
  73. }
  74. p.capacity = 0
  75. p.balance = nil
  76. }
  77. func newFuzzer(input []byte) *fuzzer {
  78. f := &fuzzer{
  79. input: bytes.NewReader(input),
  80. }
  81. for i := range f.peers {
  82. f.peers[i] = &clientPeer{
  83. fuzzer: f,
  84. node: enode.SignNull(new(enr.Record), enode.ID{byte(i)}),
  85. freeID: string([]byte{byte(i)}),
  86. timeout: f.randomDelay(),
  87. }
  88. }
  89. return f
  90. }
  91. func (f *fuzzer) read(size int) []byte {
  92. out := make([]byte, size)
  93. if _, err := f.input.Read(out); err != nil {
  94. f.exhausted = true
  95. }
  96. return out
  97. }
  98. func (f *fuzzer) randomByte() byte {
  99. d := f.read(1)
  100. return d[0]
  101. }
  102. func (f *fuzzer) randomBool() bool {
  103. d := f.read(1)
  104. return d[0]&1 == 1
  105. }
  106. func (f *fuzzer) randomInt(max int) int {
  107. if max == 0 {
  108. return 0
  109. }
  110. if max <= 256 {
  111. return int(f.randomByte()) % max
  112. }
  113. var a uint16
  114. if err := binary.Read(f.input, binary.LittleEndian, &a); err != nil {
  115. f.exhausted = true
  116. }
  117. return int(a % uint16(max))
  118. }
  119. func (f *fuzzer) randomTokenAmount(signed bool) int64 {
  120. x := uint64(f.randomInt(65000))
  121. x = x * x * x * x
  122. if signed && (x&1) == 1 {
  123. if x <= math.MaxInt64 {
  124. return -int64(x)
  125. }
  126. return math.MinInt64
  127. }
  128. if x <= math.MaxInt64 {
  129. return int64(x)
  130. }
  131. return math.MaxInt64
  132. }
  133. func (f *fuzzer) randomDelay() time.Duration {
  134. delay := f.randomByte()
  135. if delay < 128 {
  136. return time.Duration(delay) * time.Second
  137. }
  138. return 0
  139. }
  140. func (f *fuzzer) randomFactors() vfs.PriceFactors {
  141. return vfs.PriceFactors{
  142. TimeFactor: float64(f.randomByte()) / 25500,
  143. CapacityFactor: float64(f.randomByte()) / 255,
  144. RequestFactor: float64(f.randomByte()) / 255,
  145. }
  146. }
  147. func (f *fuzzer) connectedBalanceOp(balance vfs.ConnectedBalance) {
  148. switch f.randomInt(3) {
  149. case 0:
  150. balance.RequestServed(uint64(f.randomTokenAmount(false)))
  151. case 1:
  152. balance.SetPriceFactors(f.randomFactors(), f.randomFactors())
  153. case 2:
  154. balance.GetBalance()
  155. balance.GetRawBalance()
  156. balance.GetPriceFactors()
  157. }
  158. }
  159. func (f *fuzzer) atomicBalanceOp(balance vfs.AtomicBalanceOperator) {
  160. switch f.randomInt(3) {
  161. case 0:
  162. balance.AddBalance(f.randomTokenAmount(true))
  163. case 1:
  164. balance.SetBalance(uint64(f.randomTokenAmount(false)), uint64(f.randomTokenAmount(false)))
  165. case 2:
  166. balance.GetBalance()
  167. balance.GetRawBalance()
  168. balance.GetPriceFactors()
  169. }
  170. }
  171. func FuzzClientPool(input []byte) int {
  172. if len(input) > 10000 {
  173. return -1
  174. }
  175. f := newFuzzer(input)
  176. if f.exhausted {
  177. return 0
  178. }
  179. clock := &mclock.Simulated{}
  180. db := memorydb.New()
  181. pool := vfs.NewClientPool(db, 10, f.randomDelay(), clock, func() bool { return true })
  182. pool.Start()
  183. defer pool.Stop()
  184. count := 0
  185. for !f.exhausted && count < 1000 {
  186. count++
  187. switch f.randomInt(11) {
  188. case 0:
  189. i := int(f.randomByte())
  190. f.peers[i].balance = pool.Register(f.peers[i])
  191. case 1:
  192. i := int(f.randomByte())
  193. f.peers[i].Disconnect()
  194. case 2:
  195. f.maxCount = uint64(f.randomByte())
  196. f.maxCap = uint64(f.randomByte())
  197. f.maxCap *= f.maxCap
  198. pool.SetLimits(f.maxCount, f.maxCap)
  199. case 3:
  200. pool.SetConnectedBias(f.randomDelay())
  201. case 4:
  202. pool.SetDefaultFactors(f.randomFactors(), f.randomFactors())
  203. case 5:
  204. pool.SetExpirationTCs(uint64(f.randomInt(50000)), uint64(f.randomInt(50000)))
  205. case 6:
  206. if _, err := pool.SetCapacity(f.peers[f.randomByte()].node, uint64(f.randomByte()), f.randomDelay(), f.randomBool()); err == vfs.ErrCantFindMaximum {
  207. panic(nil)
  208. }
  209. case 7:
  210. if balance := f.peers[f.randomByte()].balance; balance != nil {
  211. f.connectedBalanceOp(balance)
  212. }
  213. case 8:
  214. pool.BalanceOperation(f.peers[f.randomByte()].node.ID(), f.peers[f.randomByte()].freeID, func(balance vfs.AtomicBalanceOperator) {
  215. count := f.randomInt(4)
  216. for i := 0; i < count; i++ {
  217. f.atomicBalanceOp(balance)
  218. }
  219. })
  220. case 9:
  221. pool.TotalTokenAmount()
  222. pool.GetExpirationTCs()
  223. pool.Active()
  224. pool.Limits()
  225. pool.GetPosBalanceIDs(f.peers[f.randomByte()].node.ID(), f.peers[f.randomByte()].node.ID(), f.randomInt(100))
  226. case 10:
  227. req := vflux.CapacityQueryReq{
  228. Bias: uint64(f.randomByte()),
  229. AddTokens: make([]vflux.IntOrInf, f.randomInt(vflux.CapacityQueryMaxLen+1)),
  230. }
  231. for i := range req.AddTokens {
  232. v := vflux.IntOrInf{Type: uint8(f.randomInt(4))}
  233. if v.Type < 2 {
  234. v.Value = *big.NewInt(f.randomTokenAmount(false))
  235. }
  236. req.AddTokens[i] = v
  237. }
  238. reqEnc, err := rlp.EncodeToBytes(&req)
  239. if err != nil {
  240. panic(err)
  241. }
  242. p := int(f.randomByte())
  243. if p < len(reqEnc) {
  244. reqEnc[p] = f.randomByte()
  245. }
  246. pool.Handle(f.peers[f.randomByte()].node.ID(), f.peers[f.randomByte()].freeID, vflux.CapacityQueryName, reqEnc)
  247. }
  248. for _, peer := range f.disconnectList {
  249. pool.Unregister(peer)
  250. }
  251. f.disconnectList = nil
  252. if d := f.randomDelay(); d > 0 {
  253. clock.Run(d)
  254. }
  255. //fmt.Println(f.activeCount, f.maxCount, f.activeCap, f.maxCap)
  256. if activeCount, activeCap := pool.Active(); activeCount != f.activeCount || activeCap != f.activeCap {
  257. panic(nil)
  258. }
  259. if f.activeCount > f.maxCount || f.activeCap > f.maxCap {
  260. panic(nil)
  261. }
  262. }
  263. return 0
  264. }