From e86e0ecdc8a977db2ff5df60dca3cad8355ace6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 22 Oct 2015 15:43:21 +0300 Subject: [PATCH] core, eth, miner, xeth: clean up tx/receipt db accessors --- core/block_validator_test.go | 2 +- core/blockchain.go | 12 +- core/blockchain_test.go | 8 +- core/{chain_util.go => database_util.go} | 178 +++++++++++++++++- ...ain_util_test.go => database_util_test.go} | 162 +++++++++++++++- core/genesis.go | 2 +- core/transaction_util.go | 171 ----------------- eth/backend_test.go | 4 +- eth/filters/filter_test.go | 8 +- miner/worker.go | 6 +- xeth/xeth.go | 41 +--- 11 files changed, 359 insertions(+), 235 deletions(-) rename core/{chain_util.go => database_util.go} (70%) rename core/{chain_util_test.go => database_util_test.go} (71%) delete mode 100644 core/transaction_util.go diff --git a/core/block_validator_test.go b/core/block_validator_test.go index a0694f067..70953d76d 100644 --- a/core/block_validator_test.go +++ b/core/block_validator_test.go @@ -81,7 +81,7 @@ func TestPutReceipt(t *testing.T) { Index: 0, }} - PutReceipts(db, types.Receipts{receipt}) + WriteReceipts(db, types.Receipts{receipt}) receipt = GetReceipt(db, common.Hash{}) if receipt == nil { t.Error("expected to get 1 receipt, got none.") diff --git a/core/blockchain.go b/core/blockchain.go index b6b00ca04..5e1fc9424 100644 --- a/core/blockchain.go +++ b/core/blockchain.go @@ -972,7 +972,7 @@ func (self *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain glog.Fatal(errs[index]) return } - if err := PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { + if err := WriteBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { errs[index] = fmt.Errorf("failed to write block receipts: %v", err) atomic.AddInt32(&failed, 1) glog.Fatal(errs[index]) @@ -1182,7 +1182,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { // coalesce logs for later processing coalescedLogs = append(coalescedLogs, logs...) - if err := PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { + if err := WriteBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { return i, err } @@ -1201,11 +1201,11 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) { events = append(events, ChainEvent{block, block.Hash(), logs}) // This puts transactions in a extra db for rpc - if err := PutTransactions(self.chainDb, block, block.Transactions()); err != nil { + if err := WriteTransactions(self.chainDb, block); err != nil { return i, err } // store the receipts - if err := PutReceipts(self.chainDb, receipts); err != nil { + if err := WriteReceipts(self.chainDb, receipts); err != nil { return i, err } // Write map map bloom filters @@ -1294,12 +1294,12 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error { // insert the block in the canonical way, re-writing history self.insert(block) // write canonical receipts and transactions - if err := PutTransactions(self.chainDb, block, block.Transactions()); err != nil { + if err := WriteTransactions(self.chainDb, block); err != nil { return err } receipts := GetBlockReceipts(self.chainDb, block.Hash()) // write receipts - if err := PutReceipts(self.chainDb, receipts); err != nil { + if err := WriteReceipts(self.chainDb, receipts); err != nil { return err } // Write map map bloom filters diff --git a/core/blockchain_test.go b/core/blockchain_test.go index e5ed66377..f18b5d084 100644 --- a/core/blockchain_test.go +++ b/core/blockchain_test.go @@ -937,8 +937,8 @@ func TestChainTxReorgs(t *testing.T) { // removed tx for i, tx := range (types.Transactions{pastDrop, freshDrop}) { - if GetTransaction(db, tx.Hash()) != nil { - t.Errorf("drop %d: tx found while shouldn't have been", i) + if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil { + t.Errorf("drop %d: tx %v found while shouldn't have been", i, txn) } if GetReceipt(db, tx.Hash()) != nil { t.Errorf("drop %d: receipt found while shouldn't have been", i) @@ -946,7 +946,7 @@ func TestChainTxReorgs(t *testing.T) { } // added tx for i, tx := range (types.Transactions{pastAdd, freshAdd, futureAdd}) { - if GetTransaction(db, tx.Hash()) == nil { + if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn == nil { t.Errorf("add %d: expected tx to be found", i) } if GetReceipt(db, tx.Hash()) == nil { @@ -955,7 +955,7 @@ func TestChainTxReorgs(t *testing.T) { } // shared tx for i, tx := range (types.Transactions{postponed, swapped}) { - if GetTransaction(db, tx.Hash()) == nil { + if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn == nil { t.Errorf("share %d: expected tx to be found", i) } if GetReceipt(db, tx.Hash()) == nil { diff --git a/core/chain_util.go b/core/database_util.go similarity index 70% rename from core/chain_util.go rename to core/database_util.go index ddff381a1..fbcce3e8c 100644 --- a/core/chain_util.go +++ b/core/database_util.go @@ -43,11 +43,15 @@ var ( bodySuffix = []byte("-body") tdSuffix = []byte("-td") - ExpDiffPeriod = big.NewInt(100000) - blockHashPre = []byte("block-hash-") // [deprecated by eth/63] + txMetaSuffix = []byte{0x01} + receiptsPrefix = []byte("receipts-") + blockReceiptsPrefix = []byte("receipts-block-") mipmapPre = []byte("mipmap-log-bloom-") MIPMapLevels = []uint64{1000000, 500000, 100000, 50000, 1000} + + ExpDiffPeriod = big.NewInt(100000) + blockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually] ) // CalcDifficulty is the difficulty adjustment algorithm. It returns @@ -234,6 +238,67 @@ func GetBlock(db ethdb.Database, hash common.Hash) *types.Block { return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles) } +// GetBlockReceipts retrieves the receipts generated by the transactions included +// in a block given by its hash. +func GetBlockReceipts(db ethdb.Database, hash common.Hash) types.Receipts { + data, _ := db.Get(append(blockReceiptsPrefix, hash[:]...)) + if len(data) == 0 { + return nil + } + storageReceipts := []*types.ReceiptForStorage{} + if err := rlp.DecodeBytes(data, &storageReceipts); err != nil { + glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", hash, err) + return nil + } + receipts := make(types.Receipts, len(storageReceipts)) + for i, receipt := range storageReceipts { + receipts[i] = (*types.Receipt)(receipt) + } + return receipts +} + +// GetTransaction retrieves a specific transaction from the database, along with +// its added positional metadata. +func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, common.Hash, uint64, uint64) { + // Retrieve the transaction itself from the database + data, _ := db.Get(hash.Bytes()) + if len(data) == 0 { + return nil, common.Hash{}, 0, 0 + } + var tx types.Transaction + if err := rlp.DecodeBytes(data, &tx); err != nil { + return nil, common.Hash{}, 0, 0 + } + // Retrieve the blockchain positional metadata + data, _ = db.Get(append(hash.Bytes(), txMetaSuffix...)) + if len(data) == 0 { + return nil, common.Hash{}, 0, 0 + } + var meta struct { + BlockHash common.Hash + BlockIndex uint64 + Index uint64 + } + if err := rlp.DecodeBytes(data, &meta); err != nil { + return nil, common.Hash{}, 0, 0 + } + return &tx, meta.BlockHash, meta.BlockIndex, meta.Index +} + +// GetReceipt returns a receipt by hash +func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt { + data, _ := db.Get(append(receiptsPrefix, txHash[:]...)) + if len(data) == 0 { + return nil + } + var receipt types.ReceiptForStorage + err := rlp.DecodeBytes(data, &receipt) + if err != nil { + glog.V(logger.Core).Infoln("GetReceipt err:", err) + } + return (*types.Receipt)(&receipt) +} + // WriteCanonicalHash stores the canonical hash for the given block number. func WriteCanonicalHash(db ethdb.Database, hash common.Hash, number uint64) error { key := append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...) @@ -329,6 +394,94 @@ func WriteBlock(db ethdb.Database, block *types.Block) error { return nil } +// WriteBlockReceipts stores all the transaction receipts belonging to a block +// as a single receipt slice. This is used during chain reorganisations for +// rescheduling dropped transactions. +func WriteBlockReceipts(db ethdb.Database, hash common.Hash, receipts types.Receipts) error { + // Convert the receipts into their storage form and serialize them + storageReceipts := make([]*types.ReceiptForStorage, len(receipts)) + for i, receipt := range receipts { + storageReceipts[i] = (*types.ReceiptForStorage)(receipt) + } + bytes, err := rlp.EncodeToBytes(storageReceipts) + if err != nil { + return err + } + // Store the flattened receipt slice + if err := db.Put(append(blockReceiptsPrefix, hash.Bytes()...), bytes); err != nil { + glog.Fatalf("failed to store block receipts into database: %v", err) + return err + } + glog.V(logger.Debug).Infof("stored block receipts [%x…]", hash.Bytes()[:4]) + return nil +} + +// WriteTransactions stores the transactions associated with a specific block +// into the given database. Beside writing the transaction, the function also +// stores a metadata entry along with the transaction, detailing the position +// of this within the blockchain. +func WriteTransactions(db ethdb.Database, block *types.Block) error { + batch := db.NewBatch() + + // Iterate over each transaction and encode it with its metadata + for i, tx := range block.Transactions() { + // Encode and queue up the transaction for storage + data, err := rlp.EncodeToBytes(tx) + if err != nil { + return err + } + if err := batch.Put(tx.Hash().Bytes(), data); err != nil { + return err + } + // Encode and queue up the transaction metadata for storage + meta := struct { + BlockHash common.Hash + BlockIndex uint64 + Index uint64 + }{ + BlockHash: block.Hash(), + BlockIndex: block.NumberU64(), + Index: uint64(i), + } + data, err = rlp.EncodeToBytes(meta) + if err != nil { + return err + } + if err := batch.Put(append(tx.Hash().Bytes(), txMetaSuffix...), data); err != nil { + return err + } + } + // Write the scheduled data into the database + if err := batch.Write(); err != nil { + glog.Fatalf("failed to store transactions into database: %v", err) + return err + } + return nil +} + +// WriteReceipts stores a batch of transaction receipts into the database. +func WriteReceipts(db ethdb.Database, receipts types.Receipts) error { + batch := db.NewBatch() + + // Iterate over all the receipts and queue them for database injection + for _, receipt := range receipts { + storageReceipt := (*types.ReceiptForStorage)(receipt) + data, err := rlp.EncodeToBytes(storageReceipt) + if err != nil { + return err + } + if err := batch.Put(append(receiptsPrefix, receipt.TxHash.Bytes()...), data); err != nil { + return err + } + } + // Write the scheduled data into the database + if err := batch.Write(); err != nil { + glog.Fatalf("failed to store receipts into database: %v", err) + return err + } + return nil +} + // DeleteCanonicalHash removes the number to hash canonical mapping. func DeleteCanonicalHash(db ethdb.Database, number uint64) { db.Delete(append(blockNumPrefix, big.NewInt(int64(number)).Bytes()...)) @@ -351,18 +504,35 @@ func DeleteTd(db ethdb.Database, hash common.Hash) { // DeleteBlock removes all block data associated with a hash. func DeleteBlock(db ethdb.Database, hash common.Hash) { + DeleteBlockReceipts(db, hash) DeleteHeader(db, hash) DeleteBody(db, hash) DeleteTd(db, hash) } -// [deprecated by eth/63] +// DeleteBlockReceipts removes all receipt data associated with a block hash. +func DeleteBlockReceipts(db ethdb.Database, hash common.Hash) { + db.Delete(append(blockReceiptsPrefix, hash.Bytes()...)) +} + +// DeleteTransaction removes all transaction data associated with a hash. +func DeleteTransaction(db ethdb.Database, hash common.Hash) { + db.Delete(hash.Bytes()) + db.Delete(append(hash.Bytes(), txMetaSuffix...)) +} + +// DeleteReceipt removes all receipt data associated with a transaction hash. +func DeleteReceipt(db ethdb.Database, hash common.Hash) { + db.Delete(append(receiptsPrefix, hash.Bytes()...)) +} + +// [deprecated by the header/block split, remove eventually] // GetBlockByHashOld returns the old combined block corresponding to the hash // or nil if not found. This method is only used by the upgrade mechanism to // access the old combined block representation. It will be dropped after the // network transitions to eth/63. func GetBlockByHashOld(db ethdb.Database, hash common.Hash) *types.Block { - data, _ := db.Get(append(blockHashPre, hash[:]...)) + data, _ := db.Get(append(blockHashPrefix, hash[:]...)) if len(data) == 0 { return nil } diff --git a/core/chain_util_test.go b/core/database_util_test.go similarity index 71% rename from core/chain_util_test.go rename to core/database_util_test.go index 0bbcbbe53..059f1ae9f 100644 --- a/core/chain_util_test.go +++ b/core/database_util_test.go @@ -17,6 +17,7 @@ package core import ( + "bytes" "encoding/json" "io/ioutil" "math/big" @@ -341,6 +342,163 @@ func TestHeadStorage(t *testing.T) { } } +// Tests that transactions and associated metadata can be stored and retrieved. +func TestTransactionStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + tx1 := types.NewTransaction(1, common.BytesToAddress([]byte{0x11}), big.NewInt(111), big.NewInt(1111), big.NewInt(11111), []byte{0x11, 0x11, 0x11}) + tx2 := types.NewTransaction(2, common.BytesToAddress([]byte{0x22}), big.NewInt(222), big.NewInt(2222), big.NewInt(22222), []byte{0x22, 0x22, 0x22}) + tx3 := types.NewTransaction(3, common.BytesToAddress([]byte{0x33}), big.NewInt(333), big.NewInt(3333), big.NewInt(33333), []byte{0x33, 0x33, 0x33}) + txs := []*types.Transaction{tx1, tx2, tx3} + + block := types.NewBlock(&types.Header{Number: big.NewInt(314)}, txs, nil, nil) + + // Check that no transactions entries are in a pristine database + for i, tx := range txs { + if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil { + t.Fatalf("tx #%d [%x]: non existent transaction returned: %v", i, tx.Hash(), txn) + } + } + // Insert all the transactions into the database, and verify contents + if err := WriteTransactions(db, block); err != nil { + t.Fatalf("failed to write transactions: %v", err) + } + for i, tx := range txs { + if txn, hash, number, index := GetTransaction(db, tx.Hash()); txn == nil { + t.Fatalf("tx #%d [%x]: transaction not found", i, tx.Hash()) + } else { + if hash != block.Hash() || number != block.NumberU64() || index != uint64(i) { + 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) + } + if tx.String() != txn.String() { + t.Fatalf("tx #%d [%x]: transaction mismatch: have %v, want %v", i, tx.Hash(), txn, tx) + } + } + } + // Delete the transactions and check purge + for i, tx := range txs { + DeleteTransaction(db, tx.Hash()) + if txn, _, _, _ := GetTransaction(db, tx.Hash()); txn != nil { + t.Fatalf("tx #%d [%x]: deleted transaction returned: %v", i, tx.Hash(), txn) + } + } +} + +// Tests that receipts can be stored and retrieved. +func TestReceiptStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + receipt1 := &types.Receipt{ + PostState: []byte{0x01}, + CumulativeGasUsed: big.NewInt(1), + Logs: vm.Logs{ + &vm.Log{Address: common.BytesToAddress([]byte{0x11})}, + &vm.Log{Address: common.BytesToAddress([]byte{0x01, 0x11})}, + }, + TxHash: common.BytesToHash([]byte{0x11, 0x11}), + ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), + GasUsed: big.NewInt(111111), + } + receipt2 := &types.Receipt{ + PostState: []byte{0x02}, + CumulativeGasUsed: big.NewInt(2), + Logs: vm.Logs{ + &vm.Log{Address: common.BytesToAddress([]byte{0x22})}, + &vm.Log{Address: common.BytesToAddress([]byte{0x02, 0x22})}, + }, + TxHash: common.BytesToHash([]byte{0x22, 0x22}), + ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}), + GasUsed: big.NewInt(222222), + } + receipts := []*types.Receipt{receipt1, receipt2} + + // Check that no receipt entries are in a pristine database + for i, receipt := range receipts { + if r := GetReceipt(db, receipt.TxHash); r != nil { + t.Fatalf("receipt #%d [%x]: non existent receipt returned: %v", i, receipt.TxHash, r) + } + } + // Insert all the receipts into the database, and verify contents + if err := WriteReceipts(db, receipts); err != nil { + t.Fatalf("failed to write receipts: %v", err) + } + for i, receipt := range receipts { + if r := GetReceipt(db, receipt.TxHash); r == nil { + t.Fatalf("receipt #%d [%x]: receipt not found", i, receipt.TxHash) + } else { + rlpHave, _ := rlp.EncodeToBytes(r) + rlpWant, _ := rlp.EncodeToBytes(receipt) + + if bytes.Compare(rlpHave, rlpWant) != 0 { + t.Fatalf("receipt #%d [%x]: receipt mismatch: have %v, want %v", i, receipt.TxHash, r, receipt) + } + } + } + // Delete the receipts and check purge + for i, receipt := range receipts { + DeleteReceipt(db, receipt.TxHash) + if r := GetReceipt(db, receipt.TxHash); r != nil { + t.Fatalf("receipt #%d [%x]: deleted receipt returned: %v", i, receipt.TxHash, r) + } + } +} + +// Tests that receipts associated with a single block can be stored and retrieved. +func TestBlockReceiptStorage(t *testing.T) { + db, _ := ethdb.NewMemDatabase() + + receipt1 := &types.Receipt{ + PostState: []byte{0x01}, + CumulativeGasUsed: big.NewInt(1), + Logs: vm.Logs{ + &vm.Log{Address: common.BytesToAddress([]byte{0x11})}, + &vm.Log{Address: common.BytesToAddress([]byte{0x01, 0x11})}, + }, + TxHash: common.BytesToHash([]byte{0x11, 0x11}), + ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}), + GasUsed: big.NewInt(111111), + } + receipt2 := &types.Receipt{ + PostState: []byte{0x02}, + CumulativeGasUsed: big.NewInt(2), + Logs: vm.Logs{ + &vm.Log{Address: common.BytesToAddress([]byte{0x22})}, + &vm.Log{Address: common.BytesToAddress([]byte{0x02, 0x22})}, + }, + TxHash: common.BytesToHash([]byte{0x22, 0x22}), + ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}), + GasUsed: big.NewInt(222222), + } + receipts := []*types.Receipt{receipt1, receipt2} + + // Check that no receipt entries are in a pristine database + hash := common.BytesToHash([]byte{0x03, 0x14}) + if rs := GetBlockReceipts(db, hash); len(rs) != 0 { + t.Fatalf("non existent receipts returned: %v", rs) + } + // Insert the receipt slice into the database and check presence + if err := WriteBlockReceipts(db, hash, receipts); err != nil { + t.Fatalf("failed to write block receipts: %v", err) + } + if rs := GetBlockReceipts(db, hash); len(rs) == 0 { + t.Fatalf("no receipts returned") + } else { + for i := 0; i < len(receipts); i++ { + rlpHave, _ := rlp.EncodeToBytes(rs[i]) + rlpWant, _ := rlp.EncodeToBytes(receipts[i]) + + if bytes.Compare(rlpHave, rlpWant) != 0 { + t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i]) + } + } + } + // Delete the receipt slice and check purge + DeleteBlockReceipts(db, hash) + if rs := GetBlockReceipts(db, hash); len(rs) != 0 { + t.Fatalf("deleted receipts returned: %v", rs) + } +} + func TestMipmapBloom(t *testing.T) { db, _ := ethdb.NewMemDatabase() @@ -425,7 +583,7 @@ func TestMipmapChain(t *testing.T) { } // store the receipts - err := PutReceipts(db, receipts) + err := WriteReceipts(db, receipts) if err != nil { t.Fatal(err) } @@ -439,7 +597,7 @@ func TestMipmapChain(t *testing.T) { if err := WriteHeadBlockHash(db, block.Hash()); err != nil { t.Fatalf("failed to insert block number: %v", err) } - if err := PutBlockReceipts(db, block.Hash(), receipts[i]); err != nil { + if err := WriteBlockReceipts(db, block.Hash(), receipts[i]); err != nil { t.Fatal("error writing block receipts:", err) } } diff --git a/core/genesis.go b/core/genesis.go index dac5de92f..3fd8f42b0 100644 --- a/core/genesis.go +++ b/core/genesis.go @@ -103,7 +103,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block, if err := WriteBlock(chainDb, block); err != nil { return nil, err } - if err := PutBlockReceipts(chainDb, block.Hash(), nil); err != nil { + if err := WriteBlockReceipts(chainDb, block.Hash(), nil); err != nil { return nil, err } if err := WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()); err != nil { diff --git a/core/transaction_util.go b/core/transaction_util.go deleted file mode 100644 index e2e5b9aee..000000000 --- a/core/transaction_util.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2015 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package core - -import ( - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/logger" - "github.com/ethereum/go-ethereum/logger/glog" - "github.com/ethereum/go-ethereum/rlp" - "github.com/syndtr/goleveldb/leveldb" -) - -var ( - receiptsPre = []byte("receipts-") - blockReceiptsPre = []byte("receipts-block-") -) - -// PutTransactions stores the transactions in the given database -func PutTransactions(db ethdb.Database, block *types.Block, txs types.Transactions) error { - batch := db.NewBatch() - - for i, tx := range block.Transactions() { - rlpEnc, err := rlp.EncodeToBytes(tx) - if err != nil { - return fmt.Errorf("failed encoding tx: %v", err) - } - - batch.Put(tx.Hash().Bytes(), rlpEnc) - - var txExtra struct { - BlockHash common.Hash - BlockIndex uint64 - Index uint64 - } - txExtra.BlockHash = block.Hash() - txExtra.BlockIndex = block.NumberU64() - txExtra.Index = uint64(i) - rlpMeta, err := rlp.EncodeToBytes(txExtra) - if err != nil { - return fmt.Errorf("failed encoding tx meta data: %v", err) - } - - batch.Put(append(tx.Hash().Bytes(), 0x0001), rlpMeta) - } - - if err := batch.Write(); err != nil { - return fmt.Errorf("failed writing tx to db: %v", err) - } - return nil -} - -func DeleteTransaction(db ethdb.Database, txHash common.Hash) { - db.Delete(txHash[:]) -} - -func GetTransaction(db ethdb.Database, txhash common.Hash) *types.Transaction { - data, _ := db.Get(txhash[:]) - if len(data) != 0 { - var tx types.Transaction - if err := rlp.DecodeBytes(data, &tx); err != nil { - return nil - } - return &tx - } - return nil -} - -// PutReceipts stores the receipts in the current database -func PutReceipts(db ethdb.Database, receipts types.Receipts) error { - batch := new(leveldb.Batch) - _, batchWrite := db.(*ethdb.LDBDatabase) - - for _, receipt := range receipts { - storageReceipt := (*types.ReceiptForStorage)(receipt) - bytes, err := rlp.EncodeToBytes(storageReceipt) - if err != nil { - return err - } - - if batchWrite { - batch.Put(append(receiptsPre, receipt.TxHash[:]...), bytes) - } else { - err = db.Put(append(receiptsPre, receipt.TxHash[:]...), bytes) - if err != nil { - return err - } - } - } - if db, ok := db.(*ethdb.LDBDatabase); ok { - if err := db.LDB().Write(batch, nil); err != nil { - return err - } - } - - return nil -} - -// Delete a receipts from the database -func DeleteReceipt(db ethdb.Database, txHash common.Hash) { - db.Delete(append(receiptsPre, txHash[:]...)) -} - -// GetReceipt returns a receipt by hash -func GetReceipt(db ethdb.Database, txHash common.Hash) *types.Receipt { - data, _ := db.Get(append(receiptsPre, txHash[:]...)) - if len(data) == 0 { - return nil - } - var receipt types.ReceiptForStorage - err := rlp.DecodeBytes(data, &receipt) - if err != nil { - glog.V(logger.Core).Infoln("GetReceipt err:", err) - } - return (*types.Receipt)(&receipt) -} - -// GetBlockReceipts returns the receipts generated by the transactions -// included in block's given hash. -func GetBlockReceipts(db ethdb.Database, hash common.Hash) types.Receipts { - data, _ := db.Get(append(blockReceiptsPre, hash[:]...)) - if len(data) == 0 { - return nil - } - rs := []*types.ReceiptForStorage{} - if err := rlp.DecodeBytes(data, &rs); err != nil { - glog.V(logger.Error).Infof("invalid receipt array RLP for hash %x: %v", hash, err) - return nil - } - receipts := make(types.Receipts, len(rs)) - for i, receipt := range rs { - receipts[i] = (*types.Receipt)(receipt) - } - return receipts -} - -// PutBlockReceipts stores the block's transactions associated receipts -// and stores them by block hash in a single slice. This is required for -// forks and chain reorgs -func PutBlockReceipts(db ethdb.Database, hash common.Hash, receipts types.Receipts) error { - rs := make([]*types.ReceiptForStorage, len(receipts)) - for i, receipt := range receipts { - rs[i] = (*types.ReceiptForStorage)(receipt) - } - bytes, err := rlp.EncodeToBytes(rs) - if err != nil { - return err - } - err = db.Put(append(blockReceiptsPre, hash[:]...), bytes) - if err != nil { - return err - } - return nil -} diff --git a/eth/backend_test.go b/eth/backend_test.go index 0379fc843..83219de62 100644 --- a/eth/backend_test.go +++ b/eth/backend_test.go @@ -32,7 +32,7 @@ func TestMipmapUpgrade(t *testing.T) { } // store the receipts - err := core.PutReceipts(db, receipts) + err := core.WriteReceipts(db, receipts) if err != nil { t.Fatal(err) } @@ -45,7 +45,7 @@ func TestMipmapUpgrade(t *testing.T) { if err := core.WriteHeadBlockHash(db, block.Hash()); err != nil { t.Fatalf("failed to insert block number: %v", err) } - if err := core.PutBlockReceipts(db, block.Hash(), receipts[i]); err != nil { + if err := core.WriteBlockReceipts(db, block.Hash(), receipts[i]); err != nil { t.Fatal("error writing block receipts:", err) } } diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index a5418e2e7..5772114b3 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -64,7 +64,7 @@ func BenchmarkMipmaps(b *testing.B) { } // store the receipts - err := core.PutReceipts(db, receipts) + err := core.WriteReceipts(db, receipts) if err != nil { b.Fatal(err) } @@ -78,7 +78,7 @@ func BenchmarkMipmaps(b *testing.B) { if err := core.WriteHeadBlockHash(db, block.Hash()); err != nil { b.Fatalf("failed to insert block number: %v", err) } - if err := core.PutBlockReceipts(db, block.Hash(), receipts[i]); err != nil { + if err := core.WriteBlockReceipts(db, block.Hash(), receipts[i]); err != nil { b.Fatal("error writing block receipts:", err) } } @@ -163,7 +163,7 @@ func TestFilters(t *testing.T) { } // store the receipts - err := core.PutReceipts(db, receipts) + err := core.WriteReceipts(db, receipts) if err != nil { t.Fatal(err) } @@ -180,7 +180,7 @@ func TestFilters(t *testing.T) { if err := core.WriteHeadBlockHash(db, block.Hash()); err != nil { t.Fatalf("failed to insert block number: %v", err) } - if err := core.PutBlockReceipts(db, block.Hash(), receipts[i]); err != nil { + if err := core.WriteBlockReceipts(db, block.Hash(), receipts[i]); err != nil { t.Fatal("error writing block receipts:", err) } } diff --git a/miner/worker.go b/miner/worker.go index 238f1a4bf..aa0fa85cb 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -305,9 +305,9 @@ func (self *worker) wait() { // check if canon block and write transactions if stat == core.CanonStatTy { // This puts transactions in a extra db for rpc - core.PutTransactions(self.chainDb, block, block.Transactions()) + core.WriteTransactions(self.chainDb, block) // store the receipts - core.PutReceipts(self.chainDb, work.receipts) + core.WriteReceipts(self.chainDb, work.receipts) // Write map map bloom filters core.WriteMipmapBloom(self.chainDb, block.NumberU64(), work.receipts) } @@ -320,7 +320,7 @@ func (self *worker) wait() { self.mux.Post(core.ChainHeadEvent{block}) self.mux.Post(logs) } - if err := core.PutBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { + if err := core.WriteBlockReceipts(self.chainDb, block.Hash(), receipts); err != nil { glog.V(logger.Warn).Infoln("error writing block receipts:", err) } }(block, work.state.Logs(), work.receipts) diff --git a/xeth/xeth.go b/xeth/xeth.go index 243bef0b8..ae9f1fe47 100644 --- a/xeth/xeth.go +++ b/xeth/xeth.go @@ -322,44 +322,11 @@ func (self *XEth) EthBlockByHash(strHash string) *types.Block { return block } -func (self *XEth) EthTransactionByHash(hash string) (tx *types.Transaction, blhash common.Hash, blnum *big.Int, txi uint64) { - // Due to increasing return params and need to determine if this is from transaction pool or - // some chain, this probably needs to be refactored for more expressiveness - data, _ := self.backend.ChainDb().Get(common.FromHex(hash)) - if len(data) != 0 { - dtx := new(types.Transaction) - if err := rlp.DecodeBytes(data, dtx); err != nil { - glog.V(logger.Error).Infoln(err) - return - } - tx = dtx - } else { // check pending transactions - tx = self.backend.TxPool().GetTransaction(common.HexToHash(hash)) - } - - // meta - var txExtra struct { - BlockHash common.Hash - BlockIndex uint64 - Index uint64 - } - - v, dberr := self.backend.ChainDb().Get(append(common.FromHex(hash), 0x0001)) - // TODO check specifically for ErrNotFound - if dberr != nil { - return +func (self *XEth) EthTransactionByHash(hash string) (*types.Transaction, common.Hash, uint64, uint64) { + if tx, hash, number, index := core.GetTransaction(self.backend.ChainDb(), common.HexToHash(hash)); tx != nil { + return tx, hash, number, index } - r := bytes.NewReader(v) - err := rlp.Decode(r, &txExtra) - if err == nil { - blhash = txExtra.BlockHash - blnum = big.NewInt(int64(txExtra.BlockIndex)) - txi = txExtra.Index - } else { - glog.V(logger.Error).Infoln(err) - } - - return + return self.backend.TxPool().GetTransaction(common.HexToHash(hash)), common.Hash{}, 0, 0 } func (self *XEth) BlockByNumber(num int64) *Block {