github.com/aidoskuneen/adk-node@v0.0.0-20220315131952-2e32567cb7f4/tests/fuzzers/les/les-fuzzer.go (about)

     1  // Copyright 2021 The adkgo Authors
     2  // This file is part of the adkgo library (adapted for adkgo from go--ethereum v1.10.8).
     3  //
     4  // the adkgo 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 adkgo 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 adkgo library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package les
    18  
    19  import (
    20  	"bytes"
    21  	"encoding/binary"
    22  	"io"
    23  	"math/big"
    24  
    25  	"github.com/aidoskuneen/adk-node/common"
    26  	"github.com/aidoskuneen/adk-node/consensus/ethash"
    27  	"github.com/aidoskuneen/adk-node/core"
    28  	"github.com/aidoskuneen/adk-node/core/rawdb"
    29  	"github.com/aidoskuneen/adk-node/core/types"
    30  	"github.com/aidoskuneen/adk-node/core/vm"
    31  	"github.com/aidoskuneen/adk-node/crypto"
    32  	l "github.com/aidoskuneen/adk-node/les"
    33  	"github.com/aidoskuneen/adk-node/params"
    34  	"github.com/aidoskuneen/adk-node/rlp"
    35  	"github.com/aidoskuneen/adk-node/trie"
    36  )
    37  
    38  var (
    39  	bankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
    40  	bankAddr   = crypto.PubkeyToAddress(bankKey.PublicKey)
    41  	bankFunds  = new(big.Int).Mul(big.NewInt(100), big.NewInt(params.Ether))
    42  
    43  	testChainLen     = 256
    44  	testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056")
    45  
    46  	chain      *core.BlockChain
    47  	addrHashes []common.Hash
    48  	txHashes   []common.Hash
    49  
    50  	chtTrie   *trie.Trie
    51  	bloomTrie *trie.Trie
    52  	chtKeys   [][]byte
    53  	bloomKeys [][]byte
    54  )
    55  
    56  func makechain() (bc *core.BlockChain, addrHashes, txHashes []common.Hash) {
    57  	db := rawdb.NewMemoryDatabase()
    58  	gspec := core.Genesis{
    59  		Config:   params.TestChainConfig,
    60  		Alloc:    core.GenesisAlloc{bankAddr: {Balance: bankFunds}},
    61  		GasLimit: 100000000,
    62  	}
    63  	genesis := gspec.MustCommit(db)
    64  	signer := types.HomesteadSigner{}
    65  	blocks, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, testChainLen,
    66  		func(i int, gen *core.BlockGen) {
    67  			var (
    68  				tx   *types.Transaction
    69  				addr common.Address
    70  			)
    71  			nonce := uint64(i)
    72  			if i%4 == 0 {
    73  				tx, _ = types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, bankKey)
    74  				addr = crypto.CreateAddress(bankAddr, nonce)
    75  			} else {
    76  				addr = common.BigToAddress(big.NewInt(int64(i)))
    77  				tx, _ = types.SignTx(types.NewTransaction(nonce, addr, big.NewInt(10000), params.TxGas, big.NewInt(params.GWei), nil), signer, bankKey)
    78  			}
    79  			gen.AddTx(tx)
    80  			addrHashes = append(addrHashes, crypto.Keccak256Hash(addr[:]))
    81  			txHashes = append(txHashes, tx.Hash())
    82  		})
    83  	bc, _ = core.NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
    84  	if _, err := bc.InsertChain(blocks); err != nil {
    85  		panic(err)
    86  	}
    87  	return
    88  }
    89  
    90  func makeTries() (chtTrie *trie.Trie, bloomTrie *trie.Trie, chtKeys, bloomKeys [][]byte) {
    91  	chtTrie, _ = trie.New(common.Hash{}, trie.NewDatabase(rawdb.NewMemoryDatabase()))
    92  	bloomTrie, _ = trie.New(common.Hash{}, trie.NewDatabase(rawdb.NewMemoryDatabase()))
    93  	for i := 0; i < testChainLen; i++ {
    94  		// The element in CHT is <big-endian block number> -> <block hash>
    95  		key := make([]byte, 8)
    96  		binary.BigEndian.PutUint64(key, uint64(i+1))
    97  		chtTrie.Update(key, []byte{0x1, 0xf})
    98  		chtKeys = append(chtKeys, key)
    99  
   100  		// The element in Bloom trie is <2 byte bit index> + <big-endian block number> -> bloom
   101  		key2 := make([]byte, 10)
   102  		binary.BigEndian.PutUint64(key2[2:], uint64(i+1))
   103  		bloomTrie.Update(key2, []byte{0x2, 0xe})
   104  		bloomKeys = append(bloomKeys, key2)
   105  	}
   106  	return
   107  }
   108  
   109  func init() {
   110  	chain, addrHashes, txHashes = makechain()
   111  	chtTrie, bloomTrie, chtKeys, bloomKeys = makeTries()
   112  }
   113  
   114  type fuzzer struct {
   115  	chain *core.BlockChain
   116  	pool  *core.TxPool
   117  
   118  	chainLen  int
   119  	addr, txs []common.Hash
   120  	nonce     uint64
   121  
   122  	chtKeys   [][]byte
   123  	bloomKeys [][]byte
   124  	chtTrie   *trie.Trie
   125  	bloomTrie *trie.Trie
   126  
   127  	input     io.Reader
   128  	exhausted bool
   129  }
   130  
   131  func newFuzzer(input []byte) *fuzzer {
   132  	return &fuzzer{
   133  		chain:     chain,
   134  		chainLen:  testChainLen,
   135  		addr:      addrHashes,
   136  		txs:       txHashes,
   137  		chtTrie:   chtTrie,
   138  		bloomTrie: bloomTrie,
   139  		chtKeys:   chtKeys,
   140  		bloomKeys: bloomKeys,
   141  		nonce:     uint64(len(txHashes)),
   142  		pool:      core.NewTxPool(core.DefaultTxPoolConfig, params.TestChainConfig, chain),
   143  		input:     bytes.NewReader(input),
   144  	}
   145  }
   146  
   147  func (f *fuzzer) read(size int) []byte {
   148  	out := make([]byte, size)
   149  	if _, err := f.input.Read(out); err != nil {
   150  		f.exhausted = true
   151  	}
   152  	return out
   153  }
   154  
   155  func (f *fuzzer) randomByte() byte {
   156  	d := f.read(1)
   157  	return d[0]
   158  }
   159  
   160  func (f *fuzzer) randomBool() bool {
   161  	d := f.read(1)
   162  	return d[0]&1 == 1
   163  }
   164  
   165  func (f *fuzzer) randomInt(max int) int {
   166  	if max == 0 {
   167  		return 0
   168  	}
   169  	if max <= 256 {
   170  		return int(f.randomByte()) % max
   171  	}
   172  	var a uint16
   173  	if err := binary.Read(f.input, binary.LittleEndian, &a); err != nil {
   174  		f.exhausted = true
   175  	}
   176  	return int(a % uint16(max))
   177  }
   178  
   179  func (f *fuzzer) randomX(max int) uint64 {
   180  	var a uint16
   181  	if err := binary.Read(f.input, binary.LittleEndian, &a); err != nil {
   182  		f.exhausted = true
   183  	}
   184  	if a < 0x8000 {
   185  		return uint64(a%uint16(max+1)) - 1
   186  	}
   187  	return (uint64(1)<<(a%64+1) - 1) & (uint64(a) * 343897772345826595)
   188  }
   189  
   190  func (f *fuzzer) randomBlockHash() common.Hash {
   191  	h := f.chain.GetCanonicalHash(uint64(f.randomInt(3 * f.chainLen)))
   192  	if h != (common.Hash{}) {
   193  		return h
   194  	}
   195  	return common.BytesToHash(f.read(common.HashLength))
   196  }
   197  
   198  func (f *fuzzer) randomAddrHash() []byte {
   199  	i := f.randomInt(3 * len(f.addr))
   200  	if i < len(f.addr) {
   201  		return f.addr[i].Bytes()
   202  	}
   203  	return f.read(common.HashLength)
   204  }
   205  
   206  func (f *fuzzer) randomCHTTrieKey() []byte {
   207  	i := f.randomInt(3 * len(f.chtKeys))
   208  	if i < len(f.chtKeys) {
   209  		return f.chtKeys[i]
   210  	}
   211  	return f.read(8)
   212  }
   213  
   214  func (f *fuzzer) randomBloomTrieKey() []byte {
   215  	i := f.randomInt(3 * len(f.bloomKeys))
   216  	if i < len(f.bloomKeys) {
   217  		return f.bloomKeys[i]
   218  	}
   219  	return f.read(10)
   220  }
   221  
   222  func (f *fuzzer) randomTxHash() common.Hash {
   223  	i := f.randomInt(3 * len(f.txs))
   224  	if i < len(f.txs) {
   225  		return f.txs[i]
   226  	}
   227  	return common.BytesToHash(f.read(common.HashLength))
   228  }
   229  
   230  func (f *fuzzer) BlockChain() *core.BlockChain {
   231  	return f.chain
   232  }
   233  
   234  func (f *fuzzer) TxPool() *core.TxPool {
   235  	return f.pool
   236  }
   237  
   238  func (f *fuzzer) ArchiveMode() bool {
   239  	return false
   240  }
   241  
   242  func (f *fuzzer) AddTxsSync() bool {
   243  	return false
   244  }
   245  
   246  func (f *fuzzer) GetHelperTrie(typ uint, index uint64) *trie.Trie {
   247  	if typ == 0 {
   248  		return f.chtTrie
   249  	} else if typ == 1 {
   250  		return f.bloomTrie
   251  	}
   252  	return nil
   253  }
   254  
   255  type dummyMsg struct {
   256  	data []byte
   257  }
   258  
   259  func (d dummyMsg) Decode(val interface{}) error {
   260  	return rlp.DecodeBytes(d.data, val)
   261  }
   262  
   263  func (f *fuzzer) doFuzz(msgCode uint64, packet interface{}) {
   264  	enc, err := rlp.EncodeToBytes(packet)
   265  	if err != nil {
   266  		panic(err)
   267  	}
   268  	version := f.randomInt(3) + 2 // [LES2, LES3, LES4]
   269  	peer, closeFn := l.NewFuzzerPeer(version)
   270  	defer closeFn()
   271  	fn, _, _, err := l.Les3[msgCode].Handle(dummyMsg{enc})
   272  	if err != nil {
   273  		panic(err)
   274  	}
   275  	fn(f, peer, func() bool { return true })
   276  }
   277  
   278  func Fuzz(input []byte) int {
   279  	// We expect some large inputs
   280  	if len(input) < 100 {
   281  		return -1
   282  	}
   283  	f := newFuzzer(input)
   284  	if f.exhausted {
   285  		return -1
   286  	}
   287  	for !f.exhausted {
   288  		switch f.randomInt(8) {
   289  		case 0:
   290  			req := &l.GetBlockHeadersPacket{
   291  				Query: l.GetBlockHeadersData{
   292  					Amount:  f.randomX(l.MaxHeaderFetch + 1),
   293  					Skip:    f.randomX(10),
   294  					Reverse: f.randomBool(),
   295  				},
   296  			}
   297  			if f.randomBool() {
   298  				req.Query.Origin.Hash = f.randomBlockHash()
   299  			} else {
   300  				req.Query.Origin.Number = uint64(f.randomInt(f.chainLen * 2))
   301  			}
   302  			f.doFuzz(l.GetBlockHeadersMsg, req)
   303  
   304  		case 1:
   305  			req := &l.GetBlockBodiesPacket{Hashes: make([]common.Hash, f.randomInt(l.MaxBodyFetch+1))}
   306  			for i := range req.Hashes {
   307  				req.Hashes[i] = f.randomBlockHash()
   308  			}
   309  			f.doFuzz(l.GetBlockBodiesMsg, req)
   310  
   311  		case 2:
   312  			req := &l.GetCodePacket{Reqs: make([]l.CodeReq, f.randomInt(l.MaxCodeFetch+1))}
   313  			for i := range req.Reqs {
   314  				req.Reqs[i] = l.CodeReq{
   315  					BHash:  f.randomBlockHash(),
   316  					AccKey: f.randomAddrHash(),
   317  				}
   318  			}
   319  			f.doFuzz(l.GetCodeMsg, req)
   320  
   321  		case 3:
   322  			req := &l.GetReceiptsPacket{Hashes: make([]common.Hash, f.randomInt(l.MaxReceiptFetch+1))}
   323  			for i := range req.Hashes {
   324  				req.Hashes[i] = f.randomBlockHash()
   325  			}
   326  			f.doFuzz(l.GetReceiptsMsg, req)
   327  
   328  		case 4:
   329  			req := &l.GetProofsPacket{Reqs: make([]l.ProofReq, f.randomInt(l.MaxProofsFetch+1))}
   330  			for i := range req.Reqs {
   331  				if f.randomBool() {
   332  					req.Reqs[i] = l.ProofReq{
   333  						BHash:     f.randomBlockHash(),
   334  						AccKey:    f.randomAddrHash(),
   335  						Key:       f.randomAddrHash(),
   336  						FromLevel: uint(f.randomX(3)),
   337  					}
   338  				} else {
   339  					req.Reqs[i] = l.ProofReq{
   340  						BHash:     f.randomBlockHash(),
   341  						Key:       f.randomAddrHash(),
   342  						FromLevel: uint(f.randomX(3)),
   343  					}
   344  				}
   345  			}
   346  			f.doFuzz(l.GetProofsV2Msg, req)
   347  
   348  		case 5:
   349  			req := &l.GetHelperTrieProofsPacket{Reqs: make([]l.HelperTrieReq, f.randomInt(l.MaxHelperTrieProofsFetch+1))}
   350  			for i := range req.Reqs {
   351  				switch f.randomInt(3) {
   352  				case 0:
   353  					// Canonical hash trie
   354  					req.Reqs[i] = l.HelperTrieReq{
   355  						Type:      0,
   356  						TrieIdx:   f.randomX(3),
   357  						Key:       f.randomCHTTrieKey(),
   358  						FromLevel: uint(f.randomX(3)),
   359  						AuxReq:    uint(2),
   360  					}
   361  				case 1:
   362  					// Bloom trie
   363  					req.Reqs[i] = l.HelperTrieReq{
   364  						Type:      1,
   365  						TrieIdx:   f.randomX(3),
   366  						Key:       f.randomBloomTrieKey(),
   367  						FromLevel: uint(f.randomX(3)),
   368  						AuxReq:    0,
   369  					}
   370  				default:
   371  					// Random trie
   372  					req.Reqs[i] = l.HelperTrieReq{
   373  						Type:      2,
   374  						TrieIdx:   f.randomX(3),
   375  						Key:       f.randomCHTTrieKey(),
   376  						FromLevel: uint(f.randomX(3)),
   377  						AuxReq:    0,
   378  					}
   379  				}
   380  			}
   381  			f.doFuzz(l.GetHelperTrieProofsMsg, req)
   382  
   383  		case 6:
   384  			req := &l.SendTxPacket{Txs: make([]*types.Transaction, f.randomInt(l.MaxTxSend+1))}
   385  			signer := types.HomesteadSigner{}
   386  			for i := range req.Txs {
   387  				var nonce uint64
   388  				if f.randomBool() {
   389  					nonce = uint64(f.randomByte())
   390  				} else {
   391  					nonce = f.nonce
   392  					f.nonce += 1
   393  				}
   394  				req.Txs[i], _ = types.SignTx(types.NewTransaction(nonce, common.Address{}, big.NewInt(10000), params.TxGas, big.NewInt(1000000000*int64(f.randomByte())), nil), signer, bankKey)
   395  			}
   396  			f.doFuzz(l.SendTxV2Msg, req)
   397  
   398  		case 7:
   399  			req := &l.GetTxStatusPacket{Hashes: make([]common.Hash, f.randomInt(l.MaxTxStatus+1))}
   400  			for i := range req.Hashes {
   401  				req.Hashes[i] = f.randomTxHash()
   402  			}
   403  			f.doFuzz(l.GetTxStatusMsg, req)
   404  		}
   405  	}
   406  	return 0
   407  }