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  }