From 98fd40d2f65851e668725763bed1787146b09e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A9ter=20Szil=C3=A1gyi?= Date: Thu, 13 Jul 2017 20:04:46 +0300 Subject: [PATCH] core, eth, internal: new transaction schema usage polishes --- core/database_util.go | 53 ++++++++--------- eth/backend_test.go | 9 --- eth/filters/filter_test.go | 12 ---- internal/ethapi/api.go | 115 +++++++++++++++++-------------------- 4 files changed, 76 insertions(+), 113 deletions(-) diff --git a/core/database_util.go b/core/database_util.go index 20ffb2a9e8..5e4af7f865 100644 --- a/core/database_util.go +++ b/core/database_util.go @@ -71,6 +71,14 @@ var ( preimageHitCounter = metrics.NewCounter("db/preimage/hits") ) +// lookupEntry is a positional metadata to help looking up the data content of +// a transaction or receipt given only its hash. +type lookupEntry struct { + BlockHash common.Hash + BlockIndex uint64 + Index uint64 +} + // encodeBlockNumber encodes a block number as big endian uint64 func encodeBlockNumber(number uint64) []byte { enc := make([]byte, 8) @@ -258,23 +266,18 @@ func GetBlockReceipts(db ethdb.Database, hash common.Hash, number uint64) types. // GetLookupEntry retrieves the positional metadata associated with a transaction // hash to allow retrieving the transaction or receipt by hash. func GetLookupEntry(db ethdb.Database, hash common.Hash) (common.Hash, uint64, uint64) { - // Define the transaction positional metadata - var entry struct { - BlockHash common.Hash - BlockIndex uint64 - Index uint64 - } - // Retrieve the transaction metadata and resolve the transaction from the body + // Load the positional metadata from disk and bail if it fails data, _ := db.Get(append(lookupPrefix, hash.Bytes()...)) - if len(data) != 0 { - // New style transaction, retrieve contents from the block - if err := rlp.DecodeBytes(data, &entry); err != nil { - log.Error("Invalid lookup entry RLP", "hash", hash, "err", err) - return common.Hash{}, 0, 0 - } - return entry.BlockHash, entry.BlockIndex, entry.Index + if len(data) == 0 { + return common.Hash{}, 0, 0 } - return common.Hash{}, 0, 0 + // Parse and return the contents of the lookup entry + var entry lookupEntry + if err := rlp.DecodeBytes(data, &entry); err != nil { + log.Error("Invalid lookup entry RLP", "hash", hash, "err", err) + return common.Hash{}, 0, 0 + } + return entry.BlockHash, entry.BlockIndex, entry.Index } // GetTransaction retrieves a specific transaction from the database, along with @@ -286,7 +289,7 @@ func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, co if blockHash != (common.Hash{}) { body := GetBody(db, blockHash, blockNumber) if body == nil || len(body.Transactions) <= int(txIndex) { - log.Error("Transaction refereced missing", "number", blockNumber, "hash", blockHash, "index", txIndex) + log.Error("Transaction referenced missing", "number", blockNumber, "hash", blockHash, "index", txIndex) return nil, common.Hash{}, 0, 0 } return body.Transactions[txIndex], blockHash, blockNumber, txIndex @@ -305,15 +308,11 @@ func GetTransaction(db ethdb.Database, hash common.Hash) (*types.Transaction, co 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 { + var entry lookupEntry + if err := rlp.DecodeBytes(data, &entry); err != nil { return nil, common.Hash{}, 0, 0 } - return &tx, meta.BlockHash, meta.BlockIndex, meta.Index + return &tx, entry.BlockHash, entry.BlockIndex, entry.Index } // GetReceipt retrieves a specific transaction receipt from the database, along with @@ -468,16 +467,12 @@ func WriteLookupEntries(db ethdb.Database, block *types.Block) error { // Iterate over each transaction and encode its metadata for i, tx := range block.Transactions() { - meta := struct { - BlockHash common.Hash - BlockIndex uint64 - Index uint64 - }{ + entry := lookupEntry{ BlockHash: block.Hash(), BlockIndex: block.NumberU64(), Index: uint64(i), } - data, err := rlp.EncodeToBytes(meta) + data, err := rlp.EncodeToBytes(entry) if err != nil { return err } diff --git a/eth/backend_test.go b/eth/backend_test.go index f60e3214c3..4351b24cfb 100644 --- a/eth/backend_test.go +++ b/eth/backend_test.go @@ -33,24 +33,15 @@ func TestMipmapUpgrade(t *testing.T) { genesis := new(core.Genesis).MustCommit(db) chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) { - var receipts types.Receipts switch i { case 1: receipt := types.NewReceipt(nil, new(big.Int)) receipt.Logs = []*types.Log{{Address: addr}} gen.AddUncheckedReceipt(receipt) - receipts = types.Receipts{receipt} case 2: receipt := types.NewReceipt(nil, new(big.Int)) receipt.Logs = []*types.Log{{Address: addr}} gen.AddUncheckedReceipt(receipt) - receipts = types.Receipts{receipt} - } - - // store the receipts - err := core.WriteReceipts(db, receipts) - if err != nil { - t.Fatal(err) } }) for i, block := range chain { diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go index cd5e7cafd5..b6cfd4bbc3 100644 --- a/eth/filters/filter_test.go +++ b/eth/filters/filter_test.go @@ -82,12 +82,6 @@ func BenchmarkMipmaps(b *testing.B) { gen.AddUncheckedReceipt(receipt) } - - // store the receipts - err := core.WriteReceipts(db, receipts) - if err != nil { - b.Fatal(err) - } core.WriteMipmapBloom(db, uint64(i+1), receipts) }) for i, block := range chain { @@ -183,12 +177,6 @@ func TestFilters(t *testing.T) { gen.AddUncheckedReceipt(receipt) receipts = types.Receipts{receipt} } - - // store the receipts - err := core.WriteReceipts(db, receipts) - if err != nil { - t.Fatal(err) - } // i is used as block number for the writes but since the i // starts at 0 and block 0 (genesis) is already present increment // by one diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go index 183508e851..1b23ac5593 100644 --- a/internal/ethapi/api.go +++ b/internal/ethapi/api.go @@ -767,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 } } @@ -810,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()), @@ -831,56 +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 +} + +// 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) } // newRPCTransactionFromBlockIndex 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 +func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction { + txs := b.Transactions() + if index >= uint64(len(txs)) { + return nil } - - return nil, nil + return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index) } // 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) +func newRPCRawTransactionFromBlockIndex(b *types.Block, index uint64) hexutil.Bytes { + txs := b.Transactions() + if index >= uint64(len(txs)) { + return nil } - return nil, nil + blob, _ := rlp.EncodeToBytes(txs[index]) + return blob } -// newRPCTransaction returns a transaction that will serialize to the RPC representation. -func newRPCTransaction(b *types.Block, hash 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() == hash { - return newRPCTransactionFromBlockIndex(b, uint(idx)) + return newRPCTransactionFromBlockIndex(b, uint64(idx)) } } - return nil, nil + return nil } // PublicTransactionPoolAPI exposes methods for the RPC interface @@ -913,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 @@ -955,20 +947,17 @@ func (s *PublicTransactionPoolAPI) GetTransactionCount(ctx context.Context, addr } // GetTransactionByHash returns the transaction for the given hash -func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) (*RPCTransaction, error) { +func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, hash common.Hash) *RPCTransaction { // Try to return an already finalized transaction - if blockHash, _, index := core.GetLookupEntry(s.b.ChainDb(), hash); blockHash != (common.Hash{}) { - if block, _ := s.b.GetBlock(ctx, blockHash); block != nil { - return newRPCTransactionFromBlockIndex(block, uint(index)) - } - return nil, nil + if tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash); tx != nil { + return newRPCTransaction(tx, blockHash, blockNumber, index) } // No finalized transaction, try to retrieve it from the pool if tx := s.b.GetPoolTransaction(hash); tx != nil { - return newRPCPendingTransaction(tx), nil + return newRPCPendingTransaction(tx) } // Transaction unknown, return as such - return nil, nil + return nil } // GetRawTransactionByHash returns the bytes of the transaction for the given hash. @@ -988,11 +977,11 @@ func (s *PublicTransactionPoolAPI) GetRawTransactionByHash(ctx context.Context, // GetTransactionReceipt returns the transaction receipt for the given transaction hash. func (s *PublicTransactionPoolAPI) GetTransactionReceipt(hash common.Hash) (map[string]interface{}, error) { - receipt, blockHash, blockNumber, index := core.GetReceipt(s.b.ChainDb(), hash) - if receipt == nil { + tx, blockHash, blockNumber, index := core.GetTransaction(s.b.ChainDb(), hash) + if tx == nil { return nil, nil } - tx, _, _, _ := core.GetTransaction(s.b.ChainDb(), hash) + 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() {