forked from ethereum/go-ethereum
-
Notifications
You must be signed in to change notification settings - Fork 63
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
core/rawdb,eth: use lightweight accessor for log filtering (ethereum#…
- Loading branch information
1 parent
44cf9fb
commit 69ddc73
Showing
6 changed files
with
810 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,280 @@ | ||
// Copyright 2018 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 <http://www.gnu.org/licenses/>. | ||
|
||
package rawdb | ||
|
||
import ( | ||
"bytes" | ||
"encoding/binary" | ||
"errors" | ||
|
||
"github.com/XinFinOrg/XDPoSChain/common" | ||
"github.com/XinFinOrg/XDPoSChain/core/types" | ||
"github.com/XinFinOrg/XDPoSChain/ethdb" | ||
"github.com/XinFinOrg/XDPoSChain/log" | ||
"github.com/XinFinOrg/XDPoSChain/params" | ||
"github.com/XinFinOrg/XDPoSChain/rlp" | ||
) | ||
|
||
// ReadHeaderNumber returns the header number assigned to a hash. | ||
func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 { | ||
data, _ := db.Get(headerNumberKey(hash)) | ||
if len(data) != 8 { | ||
return nil | ||
} | ||
number := binary.BigEndian.Uint64(data) | ||
return &number | ||
} | ||
|
||
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. | ||
func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { | ||
// First try to look up the data in ancient database. Extra hash | ||
// comparison is necessary since ancient database only maintains | ||
// the canonical data. | ||
data, _ := db.Ancient(freezerBodiesTable, number) | ||
if len(data) > 0 { | ||
h, _ := db.Ancient(freezerHashTable, number) | ||
if common.BytesToHash(h) == hash { | ||
return data | ||
} | ||
} | ||
// Then try to look up the data in leveldb. | ||
data, _ = db.Get(blockBodyKey(number, hash)) | ||
if len(data) > 0 { | ||
return data | ||
} | ||
// In the background freezer is moving data from leveldb to flatten files. | ||
// So during the first check for ancient db, the data is not yet in there, | ||
// but when we reach into leveldb, the data was already moved. That would | ||
// result in a not found error. | ||
data, _ = db.Ancient(freezerBodiesTable, number) | ||
if len(data) > 0 { | ||
h, _ := db.Ancient(freezerHashTable, number) | ||
if common.BytesToHash(h) == hash { | ||
return data | ||
} | ||
} | ||
return nil // Can't find the data anywhere. | ||
} | ||
|
||
// WriteBodyRLP stores an RLP encoded block body into the database. | ||
func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp rlp.RawValue) { | ||
if err := db.Put(blockBodyKey(number, hash), rlp); err != nil { | ||
log.Crit("Failed to store block body", "err", err) | ||
} | ||
} | ||
|
||
// ReadBody retrieves the block body corresponding to the hash. | ||
func ReadBody(db ethdb.Reader, hash common.Hash, number uint64) *types.Body { | ||
data := ReadBodyRLP(db, hash, number) | ||
if len(data) == 0 { | ||
return nil | ||
} | ||
body := new(types.Body) | ||
if err := rlp.Decode(bytes.NewReader(data), body); err != nil { | ||
log.Error("Invalid block body RLP", "hash", hash, "err", err) | ||
return nil | ||
} | ||
return body | ||
} | ||
|
||
// WriteBody stores a block body into the database. | ||
func WriteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64, body *types.Body) { | ||
data, err := rlp.EncodeToBytes(body) | ||
if err != nil { | ||
log.Crit("Failed to RLP encode body", "err", err) | ||
} | ||
WriteBodyRLP(db, hash, number, data) | ||
} | ||
|
||
// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding. | ||
func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue { | ||
// First try to look up the data in ancient database. Extra hash | ||
// comparison is necessary since ancient database only maintains | ||
// the canonical data. | ||
data, _ := db.Ancient(freezerReceiptTable, number) | ||
if len(data) > 0 { | ||
h, _ := db.Ancient(freezerHashTable, number) | ||
if common.BytesToHash(h) == hash { | ||
return data | ||
} | ||
} | ||
// Then try to look up the data in leveldb. | ||
data, _ = db.Get(blockReceiptsKey(number, hash)) | ||
if len(data) > 0 { | ||
return data | ||
} | ||
// In the background freezer is moving data from leveldb to flatten files. | ||
// So during the first check for ancient db, the data is not yet in there, | ||
// but when we reach into leveldb, the data was already moved. That would | ||
// result in a not found error. | ||
data, _ = db.Ancient(freezerReceiptTable, number) | ||
if len(data) > 0 { | ||
h, _ := db.Ancient(freezerHashTable, number) | ||
if common.BytesToHash(h) == hash { | ||
return data | ||
} | ||
} | ||
return nil // Can't find the data anywhere. | ||
} | ||
|
||
// ReadRawReceipts retrieves all the transaction receipts belonging to a block. | ||
// The receipt metadata fields are not guaranteed to be populated, so they | ||
// should not be used. Use ReadReceipts instead if the metadata is needed. | ||
func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts { | ||
// Retrieve the flattened receipt slice | ||
data := ReadReceiptsRLP(db, hash, number) | ||
if len(data) == 0 { | ||
return nil | ||
} | ||
// Convert the receipts from their storage form to their internal representation | ||
storageReceipts := []*types.ReceiptForStorage{} | ||
if err := rlp.DecodeBytes(data, &storageReceipts); err != nil { | ||
log.Error("Invalid receipt array RLP", "hash", hash, "err", err) | ||
return nil | ||
} | ||
receipts := make(types.Receipts, len(storageReceipts)) | ||
for i, storageReceipt := range storageReceipts { | ||
receipts[i] = (*types.Receipt)(storageReceipt) | ||
} | ||
return receipts | ||
} | ||
|
||
// ReadReceipts retrieves all the transaction receipts belonging to a block, including | ||
// its correspoinding metadata fields. If it is unable to populate these metadata | ||
// fields then nil is returned. | ||
// | ||
// The current implementation populates these metadata fields by reading the receipts' | ||
// corresponding block body, so if the block body is not found it will return nil even | ||
// if the receipt itself is stored. | ||
func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *params.ChainConfig) types.Receipts { | ||
// We're deriving many fields from the block body, retrieve beside the receipt | ||
receipts := ReadRawReceipts(db, hash, number) | ||
if receipts == nil { | ||
return nil | ||
} | ||
body := ReadBody(db, hash, number) | ||
if body == nil { | ||
log.Error("Missing body but have receipt", "hash", hash, "number", number) | ||
return nil | ||
} | ||
if err := receipts.DeriveFields(config, hash, number, body.Transactions); err != nil { | ||
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err) | ||
return nil | ||
} | ||
return receipts | ||
} | ||
|
||
// WriteReceipts stores all the transaction receipts belonging to a block. | ||
func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, receipts types.Receipts) { | ||
// 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 { | ||
log.Crit("Failed to encode block receipts", "err", err) | ||
} | ||
// Store the flattened receipt slice | ||
if err := db.Put(blockReceiptsKey(number, hash), bytes); err != nil { | ||
log.Crit("Failed to store block receipts", "err", err) | ||
} | ||
} | ||
|
||
// storedReceiptRLP is the storage encoding of a receipt. | ||
// Re-definition in core/types/receipt.go. | ||
type storedReceiptRLP struct { | ||
PostStateOrStatus []byte | ||
CumulativeGasUsed uint64 | ||
Bloom types.Bloom | ||
TxHash common.Hash | ||
ContractAddress common.Address | ||
Logs []*types.LogForStorage | ||
GasUsed uint64 | ||
} | ||
|
||
// ReceiptLogs is a barebone version of ReceiptForStorage which only keeps | ||
// the list of logs. When decoding a stored receipt into this object we | ||
// avoid creating the bloom filter. | ||
type receiptLogs struct { | ||
Logs []*types.Log | ||
} | ||
|
||
// DecodeRLP implements rlp.Decoder. | ||
func (r *receiptLogs) DecodeRLP(s *rlp.Stream) error { | ||
var stored storedReceiptRLP | ||
if err := s.Decode(&stored); err != nil { | ||
return err | ||
} | ||
r.Logs = make([]*types.Log, len(stored.Logs)) | ||
for i, log := range stored.Logs { | ||
r.Logs[i] = (*types.Log)(log) | ||
} | ||
return nil | ||
} | ||
|
||
// DeriveLogFields fills the logs in receiptLogs with information such as block number, txhash, etc. | ||
func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, txs types.Transactions) error { | ||
logIndex := uint(0) | ||
if len(txs) != len(receipts) { | ||
return errors.New("transaction and receipt count mismatch") | ||
} | ||
for i := 0; i < len(receipts); i++ { | ||
txHash := txs[i].Hash() | ||
// The derived log fields can simply be set from the block and transaction | ||
for j := 0; j < len(receipts[i].Logs); j++ { | ||
receipts[i].Logs[j].BlockNumber = number | ||
receipts[i].Logs[j].BlockHash = hash | ||
receipts[i].Logs[j].TxHash = txHash | ||
receipts[i].Logs[j].TxIndex = uint(i) | ||
receipts[i].Logs[j].Index = logIndex | ||
logIndex++ | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
// ReadLogs retrieves the logs for all transactions in a block. The log fields | ||
// are populated with metadata. In case the receipts or the block body | ||
// are not found, a nil is returned. | ||
func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log { | ||
// Retrieve the flattened receipt slice | ||
data := ReadReceiptsRLP(db, hash, number) | ||
if len(data) == 0 { | ||
return nil | ||
} | ||
receipts := []*receiptLogs{} | ||
if err := rlp.DecodeBytes(data, &receipts); err != nil { | ||
log.Error("Invalid receipt array RLP", "hash", hash, "err", err) | ||
return nil | ||
} | ||
|
||
body := ReadBody(db, hash, number) | ||
if body == nil { | ||
log.Error("Missing body but have receipt", "hash", hash, "number", number) | ||
return nil | ||
} | ||
if err := deriveLogFields(receipts, hash, number, body.Transactions); err != nil { | ||
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err) | ||
return nil | ||
} | ||
logs := make([][]*types.Log, len(receipts)) | ||
for i, receipt := range receipts { | ||
logs[i] = receipt.Logs | ||
} | ||
return logs | ||
} |
Oops, something went wrong.