github.com/ethereum/go-ethereum@v1.14.3/core/rlp_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 core 18 19 import ( 20 "fmt" 21 "math/big" 22 "testing" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/consensus/ethash" 26 "github.com/ethereum/go-ethereum/core/types" 27 "github.com/ethereum/go-ethereum/crypto" 28 "github.com/ethereum/go-ethereum/params" 29 "github.com/ethereum/go-ethereum/rlp" 30 "golang.org/x/crypto/sha3" 31 ) 32 33 func getBlock(transactions int, uncles int, dataSize int) *types.Block { 34 var ( 35 aa = common.HexToAddress("0x000000000000000000000000000000000000aaaa") 36 engine = ethash.NewFaker() 37 38 // A sender who makes transactions, has some funds 39 key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") 40 address = crypto.PubkeyToAddress(key.PublicKey) 41 funds = big.NewInt(1_000_000_000_000_000_000) 42 gspec = &Genesis{ 43 Config: params.TestChainConfig, 44 Alloc: types.GenesisAlloc{address: {Balance: funds}}, 45 } 46 ) 47 // We need to generate as many blocks +1 as uncles 48 _, blocks, _ := GenerateChainWithGenesis(gspec, engine, uncles+1, 49 func(n int, b *BlockGen) { 50 if n == uncles { 51 // Add transactions and stuff on the last block 52 for i := 0; i < transactions; i++ { 53 tx, _ := types.SignTx(types.NewTransaction(uint64(i), aa, 54 big.NewInt(0), 50000, b.header.BaseFee, make([]byte, dataSize)), types.HomesteadSigner{}, key) 55 b.AddTx(tx) 56 } 57 for i := 0; i < uncles; i++ { 58 b.AddUncle(&types.Header{ParentHash: b.PrevBlock(n - 1 - i).Hash(), Number: big.NewInt(int64(n - i))}) 59 } 60 } 61 }) 62 block := blocks[len(blocks)-1] 63 return block 64 } 65 66 // TestRlpIterator tests that individual transactions can be picked out 67 // from blocks without full unmarshalling/marshalling 68 func TestRlpIterator(t *testing.T) { 69 for _, tt := range []struct { 70 txs int 71 uncles int 72 datasize int 73 }{ 74 {0, 0, 0}, 75 {0, 2, 0}, 76 {10, 0, 0}, 77 {10, 2, 0}, 78 {10, 2, 50}, 79 } { 80 testRlpIterator(t, tt.txs, tt.uncles, tt.datasize) 81 } 82 } 83 84 func testRlpIterator(t *testing.T, txs, uncles, datasize int) { 85 desc := fmt.Sprintf("%d txs [%d datasize] and %d uncles", txs, datasize, uncles) 86 bodyRlp, _ := rlp.EncodeToBytes(getBlock(txs, uncles, datasize).Body()) 87 it, err := rlp.NewListIterator(bodyRlp) 88 if err != nil { 89 t.Fatal(err) 90 } 91 // Check that txs exist 92 if !it.Next() { 93 t.Fatal("expected two elems, got zero") 94 } 95 txdata := it.Value() 96 // Check that uncles exist 97 if !it.Next() { 98 t.Fatal("expected two elems, got one") 99 } 100 // No more after that 101 if it.Next() { 102 t.Fatal("expected only two elems, got more") 103 } 104 txIt, err := rlp.NewListIterator(txdata) 105 if err != nil { 106 t.Fatal(err) 107 } 108 var gotHashes []common.Hash 109 var expHashes []common.Hash 110 for txIt.Next() { 111 gotHashes = append(gotHashes, crypto.Keccak256Hash(txIt.Value())) 112 } 113 114 var expBody types.Body 115 err = rlp.DecodeBytes(bodyRlp, &expBody) 116 if err != nil { 117 t.Fatal(err) 118 } 119 for _, tx := range expBody.Transactions { 120 expHashes = append(expHashes, tx.Hash()) 121 } 122 if gotLen, expLen := len(gotHashes), len(expHashes); gotLen != expLen { 123 t.Fatalf("testcase %v: length wrong, got %d exp %d", desc, gotLen, expLen) 124 } 125 // also sanity check against input 126 if gotLen := len(gotHashes); gotLen != txs { 127 t.Fatalf("testcase %v: length wrong, got %d exp %d", desc, gotLen, txs) 128 } 129 for i, got := range gotHashes { 130 if exp := expHashes[i]; got != exp { 131 t.Errorf("testcase %v: hash wrong, got %x, exp %x", desc, got, exp) 132 } 133 } 134 } 135 136 // BenchmarkHashing compares the speeds of hashing a rlp raw data directly 137 // without the unmarshalling/marshalling step 138 func BenchmarkHashing(b *testing.B) { 139 // Make a pretty fat block 140 var ( 141 bodyRlp []byte 142 blockRlp []byte 143 ) 144 { 145 block := getBlock(200, 2, 50) 146 bodyRlp, _ = rlp.EncodeToBytes(block.Body()) 147 blockRlp, _ = rlp.EncodeToBytes(block) 148 } 149 var got common.Hash 150 var hasher = sha3.NewLegacyKeccak256() 151 b.Run("iteratorhashing", func(b *testing.B) { 152 b.ResetTimer() 153 for i := 0; i < b.N; i++ { 154 var hash common.Hash 155 it, err := rlp.NewListIterator(bodyRlp) 156 if err != nil { 157 b.Fatal(err) 158 } 159 it.Next() 160 txs := it.Value() 161 txIt, err := rlp.NewListIterator(txs) 162 if err != nil { 163 b.Fatal(err) 164 } 165 for txIt.Next() { 166 hasher.Reset() 167 hasher.Write(txIt.Value()) 168 hasher.Sum(hash[:0]) 169 got = hash 170 } 171 } 172 }) 173 var exp common.Hash 174 b.Run("fullbodyhashing", func(b *testing.B) { 175 b.ResetTimer() 176 for i := 0; i < b.N; i++ { 177 var body types.Body 178 rlp.DecodeBytes(bodyRlp, &body) 179 for _, tx := range body.Transactions { 180 exp = tx.Hash() 181 } 182 } 183 }) 184 b.Run("fullblockhashing", func(b *testing.B) { 185 b.ResetTimer() 186 for i := 0; i < b.N; i++ { 187 var block types.Block 188 rlp.DecodeBytes(blockRlp, &block) 189 for _, tx := range block.Transactions() { 190 tx.Hash() 191 } 192 } 193 }) 194 if got != exp { 195 b.Fatalf("hash wrong, got %x exp %x", got, exp) 196 } 197 }