github.com/jimmyx0x/go-ethereum@v1.10.28/tests/fuzzers/les/les-fuzzer.go (about)

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