github.com/ethereum/go-ethereum@v1.16.1/core/rawdb/accessors_indexes_test.go (about) 1 // Copyright 2018 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 rawdb 18 19 import ( 20 "errors" 21 "math/big" 22 "testing" 23 24 "github.com/davecgh/go-spew/spew" 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/core/types" 27 "github.com/ethereum/go-ethereum/ethdb" 28 "github.com/ethereum/go-ethereum/internal/blocktest" 29 "github.com/ethereum/go-ethereum/rlp" 30 "github.com/holiman/uint256" 31 ) 32 33 var newTestHasher = blocktest.NewHasher 34 35 // Tests that positional lookup metadata can be stored and retrieved. 36 func TestLookupStorage(t *testing.T) { 37 tests := []struct { 38 name string 39 writeTxLookupEntriesByBlock func(ethdb.KeyValueWriter, *types.Block) 40 }{ 41 { 42 "DatabaseV6", 43 func(db ethdb.KeyValueWriter, block *types.Block) { 44 WriteTxLookupEntriesByBlock(db, block) 45 }, 46 }, 47 { 48 "DatabaseV4-V5", 49 func(db ethdb.KeyValueWriter, block *types.Block) { 50 for _, tx := range block.Transactions() { 51 db.Put(txLookupKey(tx.Hash()), block.Hash().Bytes()) 52 } 53 }, 54 }, 55 { 56 "DatabaseV3", 57 func(db ethdb.KeyValueWriter, block *types.Block) { 58 for index, tx := range block.Transactions() { 59 entry := LegacyTxLookupEntry{ 60 BlockHash: block.Hash(), 61 BlockIndex: block.NumberU64(), 62 Index: uint64(index), 63 } 64 data, _ := rlp.EncodeToBytes(entry) 65 db.Put(txLookupKey(tx.Hash()), data) 66 } 67 }, 68 }, 69 } 70 71 for _, tc := range tests { 72 t.Run(tc.name, func(t *testing.T) { 73 db := NewMemoryDatabase() 74 75 tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), 1111, big.NewInt(11111), []byte{0x11, 0x11, 0x11}) 76 tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), 2222, big.NewInt(22222), []byte{0x22, 0x22, 0x22}) 77 tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), 3333, big.NewInt(33333), []byte{0x33, 0x33, 0x33}) 78 tx4 := types.NewTx(&types.DynamicFeeTx{ 79 To: new(common.Address), 80 Nonce: 5, 81 Value: big.NewInt(5), 82 Gas: 5, 83 GasTipCap: big.NewInt(55), 84 GasFeeCap: big.NewInt(1055), 85 }) 86 txs := []*types.Transaction{tx1, tx2, tx3, tx4} 87 88 block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, &types.Body{Transactions: txs}, nil, newTestHasher()) 89 90 // Check that no transactions entries are in a pristine database 91 for i, tx := range txs { 92 if txn, _, _, _ := ReadCanonicalTransaction(db, tx.Hash()); txn != nil { 93 t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn) 94 } 95 } 96 // Insert all the transactions into the database, and verify contents 97 WriteCanonicalHash(db, block.Hash(), block.NumberU64()) 98 WriteBlock(db, block) 99 tc.writeTxLookupEntriesByBlock(db, block) 100 101 for i, tx := range txs { 102 if txn, hash, number, index := ReadCanonicalTransaction(db, tx.Hash()); txn == nil { 103 t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash()) 104 } else { 105 if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) { 106 t.Fatalf("tx #%d [%x]: positional metadata mismatch: have %x/%d/%d, want %x/%v/%v", i, tx.Hash(), hash, number, index, block.Hash(), block.NumberU64(), i) 107 } 108 if tx.Hash() != txn.Hash() { 109 t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx) 110 } 111 } 112 } 113 // Delete the transactions and check purge 114 for i, tx := range txs { 115 DeleteTxLookupEntry(db, tx.Hash()) 116 if txn, _, _, _ := ReadCanonicalTransaction(db, tx.Hash()); txn != nil { 117 t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn) 118 } 119 } 120 }) 121 } 122 } 123 124 func TestFindTxInBlockBody(t *testing.T) { 125 tx1 := types.NewTx(&types.LegacyTx{ 126 Nonce: 1, 127 GasPrice: big.NewInt(1), 128 Gas: 1, 129 To: new(common.Address), 130 Value: big.NewInt(5), 131 Data: []byte{0x11, 0x11, 0x11}, 132 }) 133 tx2 := types.NewTx(&types.AccessListTx{ 134 Nonce: 1, 135 GasPrice: big.NewInt(1), 136 Gas: 1, 137 To: new(common.Address), 138 Value: big.NewInt(5), 139 Data: []byte{0x11, 0x11, 0x11}, 140 AccessList: []types.AccessTuple{ 141 { 142 Address: common.Address{0x1}, 143 StorageKeys: []common.Hash{{0x1}, {0x2}}, 144 }, 145 }, 146 }) 147 tx3 := types.NewTx(&types.DynamicFeeTx{ 148 Nonce: 1, 149 Gas: 1, 150 To: new(common.Address), 151 Value: big.NewInt(5), 152 Data: []byte{0x11, 0x11, 0x11}, 153 GasTipCap: big.NewInt(55), 154 GasFeeCap: big.NewInt(1055), 155 AccessList: []types.AccessTuple{ 156 { 157 Address: common.Address{0x1}, 158 StorageKeys: []common.Hash{{0x1}, {0x2}}, 159 }, 160 }, 161 }) 162 tx4 := types.NewTx(&types.BlobTx{ 163 Nonce: 1, 164 Gas: 1, 165 To: common.Address{0x1}, 166 Value: uint256.NewInt(5), 167 Data: []byte{0x11, 0x11, 0x11}, 168 GasTipCap: uint256.NewInt(55), 169 GasFeeCap: uint256.NewInt(1055), 170 AccessList: []types.AccessTuple{ 171 { 172 Address: common.Address{0x1}, 173 StorageKeys: []common.Hash{{0x1}, {0x2}}, 174 }, 175 }, 176 BlobFeeCap: uint256.NewInt(1), 177 BlobHashes: []common.Hash{{0x1}, {0x2}}, 178 }) 179 tx5 := types.NewTx(&types.SetCodeTx{ 180 Nonce: 1, 181 Gas: 1, 182 To: common.Address{0x1}, 183 Value: uint256.NewInt(5), 184 Data: []byte{0x11, 0x11, 0x11}, 185 GasTipCap: uint256.NewInt(55), 186 GasFeeCap: uint256.NewInt(1055), 187 AccessList: []types.AccessTuple{ 188 { 189 Address: common.Address{0x1}, 190 StorageKeys: []common.Hash{{0x1}, {0x2}}, 191 }, 192 }, 193 AuthList: []types.SetCodeAuthorization{ 194 { 195 ChainID: uint256.Int{1}, 196 Address: common.Address{0x1}, 197 }, 198 }, 199 }) 200 201 txs := []*types.Transaction{tx1, tx2, tx3, tx4, tx5} 202 203 block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, &types.Body{Transactions: txs}, nil, newTestHasher()) 204 db := NewMemoryDatabase() 205 WriteBlock(db, block) 206 207 rlp := ReadBodyRLP(db, block.Hash(), block.NumberU64()) 208 for i := 0; i < len(txs); i++ { 209 tx, txIndex, err := findTxInBlockBody(rlp, txs[i].Hash()) 210 if err != nil { 211 t.Fatalf("Failed to retrieve tx, err: %v", err) 212 } 213 if txIndex != uint64(i) { 214 t.Fatalf("Unexpected transaction index, want: %d, got: %d", i, txIndex) 215 } 216 if tx.Hash() != txs[i].Hash() { 217 want := spew.Sdump(txs[i]) 218 got := spew.Sdump(tx) 219 t.Fatalf("Unexpected transaction, want: %s, got: %s", want, got) 220 } 221 } 222 } 223 224 func TestExtractReceiptFields(t *testing.T) { 225 receiptWithPostState := types.ReceiptForStorage(types.Receipt{ 226 Type: types.LegacyTxType, 227 PostState: []byte{0x1, 0x2, 0x3}, 228 CumulativeGasUsed: 100, 229 }) 230 receiptWithPostStateBlob, _ := rlp.EncodeToBytes(&receiptWithPostState) 231 232 receiptNoLogs := types.ReceiptForStorage(types.Receipt{ 233 Type: types.LegacyTxType, 234 Status: types.ReceiptStatusSuccessful, 235 CumulativeGasUsed: 100, 236 }) 237 receiptNoLogBlob, _ := rlp.EncodeToBytes(&receiptNoLogs) 238 239 receiptWithLogs := types.ReceiptForStorage(types.Receipt{ 240 Type: types.LegacyTxType, 241 Status: types.ReceiptStatusSuccessful, 242 CumulativeGasUsed: 100, 243 Logs: []*types.Log{ 244 { 245 Address: common.BytesToAddress([]byte{0x1}), 246 Topics: []common.Hash{ 247 common.BytesToHash([]byte{0x1}), 248 }, 249 Data: []byte{0x1}, 250 }, 251 { 252 Address: common.BytesToAddress([]byte{0x2}), 253 Topics: []common.Hash{ 254 common.BytesToHash([]byte{0x2}), 255 }, 256 Data: []byte{0x2}, 257 }, 258 }, 259 }) 260 receiptWithLogBlob, _ := rlp.EncodeToBytes(&receiptWithLogs) 261 262 invalidReceipt := types.ReceiptForStorage(types.Receipt{ 263 Type: types.LegacyTxType, 264 Status: types.ReceiptStatusSuccessful, 265 CumulativeGasUsed: 100, 266 }) 267 invalidReceiptBlob, _ := rlp.EncodeToBytes(&invalidReceipt) 268 invalidReceiptBlob[len(invalidReceiptBlob)-1] = 0xf 269 270 var cases = []struct { 271 logs rlp.RawValue 272 expErr error 273 expGasUsed uint64 274 expLogs uint 275 }{ 276 {receiptWithPostStateBlob, nil, 100, 0}, 277 {receiptNoLogBlob, nil, 100, 0}, 278 {receiptWithLogBlob, nil, 100, 2}, 279 {invalidReceiptBlob, rlp.ErrExpectedList, 100, 0}, 280 } 281 for _, c := range cases { 282 gasUsed, logs, err := extractReceiptFields(c.logs) 283 if c.expErr != nil { 284 if !errors.Is(err, c.expErr) { 285 t.Fatalf("Unexpected error, want: %v, got: %v", c.expErr, err) 286 } 287 } else { 288 if err != nil { 289 t.Fatalf("Unexpected error %v", err) 290 } 291 if gasUsed != c.expGasUsed { 292 t.Fatalf("Unexpected gas used, want %d, got %d", c.expGasUsed, gasUsed) 293 } 294 if logs != c.expLogs { 295 t.Fatalf("Unexpected logs, want %d, got %d", c.expLogs, logs) 296 } 297 } 298 } 299 }