diff options
author | Péter Szilágyi <peterke@gmail.com> | 2017-07-15 00:39:53 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-07-15 00:39:53 +0800 |
commit | 0ff35e170d1b913082313089d13e3e6d5490839b (patch) | |
tree | 42b8eafa61c6e5894768c41a97d51e4e5427b50f /internal | |
parent | 8d6a5a3581ce6221786eb464bfef7e8c31e7ad95 (diff) | |
download | dexon-0ff35e170d1b913082313089d13e3e6d5490839b.tar.gz dexon-0ff35e170d1b913082313089d13e3e6d5490839b.tar.zst dexon-0ff35e170d1b913082313089d13e3e6d5490839b.zip |
core: remove redundant storage of transactions and receipts (#14801)
* core: remove redundant storage of transactions and receipts
* core, eth, internal: new transaction schema usage polishes
* eth: implement upgrade mechanism for db deduplication
* core, eth: drop old sequential key db upgrader
* eth: close last iterator on successful db upgrage
* core: prefix the lookup entries to make their purpose clearer
Diffstat (limited to 'internal')
-rw-r--r-- | internal/ethapi/api.go | 214 |
1 files changed, 68 insertions, 146 deletions
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index c22c56dfb..1b23ac559 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -17,7 +17,6 @@ package ethapi import ( - "bytes" "context" "errors" "fmt" @@ -35,7 +34,6 @@ import ( "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/vm" "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/p2p" "github.com/ethereum/go-ethereum/params" @@ -769,7 +767,7 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx if fullTx { formatTx = func(tx *types.Transaction) (interface{}, error) { - return newRPCTransaction(b, tx.Hash()) + return newRPCTransactionFromBlockHash(b, tx.Hash()), nil } } @@ -812,15 +810,17 @@ type RPCTransaction struct { S *hexutil.Big `json:"s"` } -// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation -func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { +// newRPCTransaction returns a transaction that will serialize to the RPC +// representation, with the given location metadata set (if available). +func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64) *RPCTransaction { var signer types.Signer = types.FrontierSigner{} if tx.Protected() { signer = types.NewEIP155Signer(tx.ChainId()) } from, _ := types.Sender(signer, tx) v, r, s := tx.RawSignatureValues() - return &RPCTransaction{ + + result := &RPCTransaction{ From: from, Gas: (*hexutil.Big)(tx.Gas()), GasPrice: (*hexutil.Big)(tx.GasPrice()), @@ -833,58 +833,46 @@ func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { R: (*hexutil.Big)(r), S: (*hexutil.Big)(s), } + if blockHash != (common.Hash{}) { + result.BlockHash = blockHash + result.BlockNumber = (*hexutil.Big)(new(big.Int).SetUint64(blockNumber)) + result.TransactionIndex = hexutil.Uint(index) + } + return result } -// newRPCTransaction returns a transaction that will serialize to the RPC representation. -func newRPCTransactionFromBlockIndex(b *types.Block, txIndex uint) (*RPCTransaction, error) { - if txIndex < uint(len(b.Transactions())) { - tx := b.Transactions()[txIndex] - var signer types.Signer = types.FrontierSigner{} - if tx.Protected() { - signer = types.NewEIP155Signer(tx.ChainId()) - } - from, _ := types.Sender(signer, tx) - v, r, s := tx.RawSignatureValues() - return &RPCTransaction{ - BlockHash: b.Hash(), - BlockNumber: (*hexutil.Big)(b.Number()), - From: from, - Gas: (*hexutil.Big)(tx.Gas()), - GasPrice: (*hexutil.Big)(tx.GasPrice()), - Hash: tx.Hash(), - Input: hexutil.Bytes(tx.Data()), - Nonce: hexutil.Uint64(tx.Nonce()), - To: tx.To(), - TransactionIndex: hexutil.Uint(txIndex), - Value: (*hexutil.Big)(tx.Value()), - V: (*hexutil.Big)(v), - R: (*hexutil.Big)(r), - S: (*hexutil.Big)(s), - }, nil - } - - return nil, nil +// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation +func newRPCPendingTransaction(tx *types.Transaction) *RPCTransaction { + return newRPCTransaction(tx, common.Hash{}, 0, 0) } -// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. -func newRPCRawTransactionFromBlockIndex(b *types.Block, txIndex uint) (hexutil.Bytes, error) { - if txIndex < uint(len(b.Transactions())) { - tx := b.Transactions()[txIndex] - return rlp.EncodeToBytes(tx) +// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation. +func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction { + txs := b.Transactions() + if index >= uint64(len(txs)) { + return nil } + return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index) +} - return nil, nil +// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index. +func newRPCRawTransactionFromBlockIndex(b *types.Block, index uint64) hexutil.Bytes { + txs := b.Transactions() + if index >= uint64(len(txs)) { + return nil + } + blob, _ := rlp.EncodeToBytes(txs[index]) + return blob } -// newRPCTransaction returns a transaction that will serialize to the RPC representation. -func newRPCTransaction(b *types.Block, txHash common.Hash) (*RPCTransaction, error) { +// newRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation. +func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction { for idx, tx := range b.Transactions() { - if tx.Hash() == txHash { - return newRPCTransactionFromBlockIndex(b, uint(idx)) + if tx.Hash() == hash { + return newRPCTransactionFromBlockIndex(b, uint64(idx)) } } - - return nil, nil + return nil } // PublicTransactionPoolAPI exposes methods for the RPC interface @@ -898,24 +886,6 @@ func NewPublicTransactionPoolAPI(b Backend, nonceLock *AddrLocker) *PublicTransa return &PublicTransactionPoolAPI{b, nonceLock} } -func getTransaction(chainDb ethdb.Database, b Backend, txHash common.Hash) (*types.Transaction, bool, error) { - txData, err := chainDb.Get(txHash.Bytes()) - isPending := false - tx := new(types.Transaction) - - if err == nil && len(txData) > 0 { - if err := rlp.DecodeBytes(txData, tx); err != nil { - return nil, isPending, err - } - } else { - // pending transaction? - tx = b.GetPoolTransaction(txHash) - isPending = true - } - - return tx, isPending, nil -} - // GetBlockTransactionCountByNumber returns the number of transactions in the block with the given block number. func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByNumber(ctx context.Context, blockNr rpc.BlockNumber) *hexutil.Uint { if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { @@ -935,35 +905,35 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Co } // GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index. -func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (*RPCTransaction, error) { +func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction { if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { - return newRPCTransactionFromBlockIndex(block, uint(index)) + return newRPCTransactionFromBlockIndex(block, uint64(index)) } - return nil, nil + return nil } // GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index. -func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (*RPCTransaction, error) { +func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction { if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { - return newRPCTransactionFromBlockIndex(block, uint(index)) + return newRPCTransactionFromBlockIndex(block, uint64(index)) } - return nil, nil + return nil } // GetRawTransactionByBlockNumberAndIndex returns the bytes of the transaction for the given block number and index. -func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) (hexutil.Bytes, error) { +func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) hexutil.Bytes { if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil { - return newRPCRawTransactionFromBlockIndex(block, uint(index)) + return newRPCRawTransactionFromBlockIndex(block, uint64(index)) } - return nil, nil + return nil } // GetRawTransactionByBlockHashAndIndex returns the bytes of the transaction for the given block hash and index. -func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) (hexutil.Bytes, error) { +func (s *PublicTransactionPoolAPI) GetRawTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) hexutil.Bytes { if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { - return newRPCRawTransactionFromBlockIndex(block, uint(index)) + return newRPCRawTransactionFromBlockIndex(block, uint64(index)) } - return nil, nil + return nil } // GetTransactionCount returns the number of transactions the given address has sent for the given block number @@ -976,90 +946,42 @@ func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr return (*hexutil.Uint64)(&nonce), state.Error() } -// getTransactionBlockData fetches the meta data for the given transaction from the chain database. This is useful to -// retrieve block information for a hash. It returns the block hash, block index and transaction index. -func getTransactionBlockData(chainDb ethdb.Database, txHash common.Hash) (common.Hash, uint64, uint64, error) { - var txBlock struct { - BlockHash common.Hash - BlockIndex uint64 - Index uint64 - } - - blockData, err := chainDb.Get(append(txHash.Bytes(), 0x0001)) - if err != nil { - return common.Hash{}, uint64(0), uint64(0), err - } - - reader := bytes.NewReader(blockData) - if err = rlp.Decode(reader, &txBlock); err != nil { - return common.Hash{}, uint64(0), uint64(0), err - } - - return txBlock.BlockHash, txBlock.BlockIndex, txBlock.Index, nil -} - // GetTransactionByHash returns the transaction for the given hash -func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) { - var tx *types.Transaction - var isPending bool - var err error - - if tx, isPending, err = getTransaction(s.b.ChainDb(), s.b, hash); err != nil { - log.Debug("Failed to retrieve transaction", "hash", hash, "err", err) - return nil, nil - } else if tx == nil { - return nil, nil +func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) *RPCTransaction { + // Try to return an already finalized transaction + if tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash); tx != nil { + return newRPCTransaction(tx, blockHash, blockNumber, index) } - if isPending { - return newRPCPendingTransaction(tx), nil - } - - blockHash, _, _, err := getTransactionBlockData(s.b.ChainDb(), hash) - if err != nil { - log.Debug("Failed to retrieve transaction block", "hash", hash, "err", err) - return nil, nil + // No finalized transaction, try to retrieve it from the pool + if tx := s.b.GetPoolTransaction(hash); tx != nil { + return newRPCPendingTransaction(tx) } - - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { - return newRPCTransaction(block, hash) - } - return nil, nil + // Transaction unknown, return as such + return nil } // GetRawTransactionByHash returns the bytes of the transaction for the given hash. func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, hash common.Hash) (hexutil.Bytes, error) { var tx *types.Transaction - var err error - if tx, _, err = getTransaction(s.b.ChainDb(), s.b, hash); err != nil { - log.Debug("Failed to retrieve transaction", "hash", hash, "err", err) - return nil, nil - } else if tx == nil { - return nil, nil + // Retrieve a finalized transaction, or a pooled otherwise + if tx, _, _, _ = core.GetTransaction(s.b.ChainDb(), hash); tx == nil { + if tx = s.b.GetPoolTransaction(hash); tx == nil { + // Transaction not found anywhere, abort + return nil, nil + } } - + // Serialize to RLP and return return rlp.EncodeToBytes(tx) } // GetTransactionReceipt returns the transaction receipt for the given transaction hash. func (s *PublicTransactionPoolAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) { - receipt := core.GetReceipt(s.b.ChainDb(), hash) - if receipt == nil { - log.Debug("Receipt not found for transaction", "hash", hash) - return nil, nil - } - - tx, _, err := getTransaction(s.b.ChainDb(), s.b, hash) - if err != nil { - log.Debug("Failed to retrieve transaction", "hash", hash, "err", err) - return nil, nil - } - - txBlock, blockIndex, index, err := getTransactionBlockData(s.b.ChainDb(), hash) - if err != nil { - log.Debug("Failed to retrieve transaction block", "hash", hash, "err", err) + tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash) + if tx == nil { return nil, nil } + receipt, _, _, _ := core.GetReceipt(s.b.ChainDb(), hash) // Old receipts don't have the lookup data available var signer types.Signer = types.FrontierSigner{} if tx.Protected() { @@ -1069,8 +991,8 @@ func (s *PublicTransactionPoolAPI) GetTransactionReceipt(hash common.Hash) (map[ fields := map[string]interface{}{ "root": hexutil.Bytes(receipt.PostState), - "blockHash": txBlock, - "blockNumber": hexutil.Uint64(blockIndex), + "blockHash": blockHash, + "blockNumber": hexutil.Uint64(blockNumber), "transactionHash": hash, "transactionIndex": hexutil.Uint64(index), "from": from, |