mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-11 15:34:26 +00:00
core/rawdb,eth: use lightweight accessor for log filtering (#23147)
This commit is contained in:
parent
c072bbaac1
commit
f8a2b00505
6 changed files with 809 additions and 6 deletions
280
core/rawdb/accessors_chain.go
Normal file
280
core/rawdb/accessors_chain.go
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
243
core/rawdb/accessors_chain_test.go
Normal file
243
core/rawdb/accessors_chain_test.go
Normal file
|
|
@ -0,0 +1,243 @@
|
||||||
|
// 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/hex"
|
||||||
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fullLogRLP struct {
|
||||||
|
Address common.Address
|
||||||
|
Topics []common.Hash
|
||||||
|
Data []byte
|
||||||
|
BlockNumber uint64
|
||||||
|
TxHash common.Hash
|
||||||
|
TxIndex uint
|
||||||
|
BlockHash common.Hash
|
||||||
|
Index uint
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFullLogRLP(l *types.Log) *fullLogRLP {
|
||||||
|
return &fullLogRLP{
|
||||||
|
Address: l.Address,
|
||||||
|
Topics: l.Topics,
|
||||||
|
Data: l.Data,
|
||||||
|
BlockNumber: l.BlockNumber,
|
||||||
|
TxHash: l.TxHash,
|
||||||
|
TxIndex: l.TxIndex,
|
||||||
|
BlockHash: l.BlockHash,
|
||||||
|
Index: l.Index,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests that logs associated with a single block can be retrieved.
|
||||||
|
func TestReadLogs(t *testing.T) {
|
||||||
|
db := NewMemoryDatabase()
|
||||||
|
|
||||||
|
// Create a live block since we need metadata to reconstruct the receipt
|
||||||
|
tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
|
||||||
|
tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil)
|
||||||
|
|
||||||
|
body := &types.Body{Transactions: types.Transactions{tx1, tx2}}
|
||||||
|
|
||||||
|
// Create the two receipts to manage afterwards
|
||||||
|
receipt1 := &types.Receipt{
|
||||||
|
Status: types.ReceiptStatusFailed,
|
||||||
|
CumulativeGasUsed: 1,
|
||||||
|
Logs: []*types.Log{
|
||||||
|
{Address: common.BytesToAddress([]byte{0x11})},
|
||||||
|
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
|
||||||
|
},
|
||||||
|
TxHash: tx1.Hash(),
|
||||||
|
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
|
||||||
|
GasUsed: 111111,
|
||||||
|
}
|
||||||
|
receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
|
||||||
|
|
||||||
|
receipt2 := &types.Receipt{
|
||||||
|
PostState: common.Hash{2}.Bytes(),
|
||||||
|
CumulativeGasUsed: 2,
|
||||||
|
Logs: []*types.Log{
|
||||||
|
{Address: common.BytesToAddress([]byte{0x22})},
|
||||||
|
{Address: common.BytesToAddress([]byte{0x02, 0x22})},
|
||||||
|
},
|
||||||
|
TxHash: tx2.Hash(),
|
||||||
|
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
|
||||||
|
GasUsed: 222222,
|
||||||
|
}
|
||||||
|
receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
|
||||||
|
receipts := []*types.Receipt{receipt1, receipt2}
|
||||||
|
|
||||||
|
hash := common.BytesToHash([]byte{0x03, 0x14})
|
||||||
|
// Check that no receipt entries are in a pristine database
|
||||||
|
if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 {
|
||||||
|
t.Fatalf("non existent receipts returned: %v", rs)
|
||||||
|
}
|
||||||
|
// Insert the body that corresponds to the receipts
|
||||||
|
WriteBody(db, hash, 0, body)
|
||||||
|
|
||||||
|
// Insert the receipt slice into the database and check presence
|
||||||
|
WriteReceipts(db, hash, 0, receipts)
|
||||||
|
|
||||||
|
logs := ReadLogs(db, hash, 0)
|
||||||
|
if len(logs) == 0 {
|
||||||
|
t.Fatalf("no logs returned")
|
||||||
|
}
|
||||||
|
if have, want := len(logs), 2; have != want {
|
||||||
|
t.Fatalf("unexpected number of logs returned, have %d want %d", have, want)
|
||||||
|
}
|
||||||
|
if have, want := len(logs[0]), 2; have != want {
|
||||||
|
t.Fatalf("unexpected number of logs[0] returned, have %d want %d", have, want)
|
||||||
|
}
|
||||||
|
if have, want := len(logs[1]), 2; have != want {
|
||||||
|
t.Fatalf("unexpected number of logs[1] returned, have %d want %d", have, want)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fill in log fields so we can compare their rlp encoding
|
||||||
|
if err := types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, 0, body.Transactions); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
for i, pr := range receipts {
|
||||||
|
for j, pl := range pr.Logs {
|
||||||
|
rlpHave, err := rlp.EncodeToBytes(newFullLogRLP(logs[i][j]))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
rlpWant, err := rlp.EncodeToBytes(newFullLogRLP(pl))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(rlpHave, rlpWant) {
|
||||||
|
t.Fatalf("receipt #%d: receipt mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDeriveLogFields(t *testing.T) {
|
||||||
|
// Create a few transactions to have receipts for
|
||||||
|
to2 := common.HexToAddress("0x2")
|
||||||
|
to3 := common.HexToAddress("0x3")
|
||||||
|
txs := types.Transactions{
|
||||||
|
types.NewTx(&types.LegacyTx{
|
||||||
|
Nonce: 1,
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
Gas: 1,
|
||||||
|
GasPrice: big.NewInt(1),
|
||||||
|
}),
|
||||||
|
types.NewTx(&types.LegacyTx{
|
||||||
|
To: &to2,
|
||||||
|
Nonce: 2,
|
||||||
|
Value: big.NewInt(2),
|
||||||
|
Gas: 2,
|
||||||
|
GasPrice: big.NewInt(2),
|
||||||
|
}),
|
||||||
|
types.NewTx(&types.AccessListTx{
|
||||||
|
To: &to3,
|
||||||
|
Nonce: 3,
|
||||||
|
Value: big.NewInt(3),
|
||||||
|
Gas: 3,
|
||||||
|
GasPrice: big.NewInt(3),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
// Create the corresponding receipts
|
||||||
|
receipts := []*receiptLogs{
|
||||||
|
{
|
||||||
|
Logs: []*types.Log{
|
||||||
|
{Address: common.BytesToAddress([]byte{0x11})},
|
||||||
|
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Logs: []*types.Log{
|
||||||
|
{Address: common.BytesToAddress([]byte{0x22})},
|
||||||
|
{Address: common.BytesToAddress([]byte{0x02, 0x22})},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Logs: []*types.Log{
|
||||||
|
{Address: common.BytesToAddress([]byte{0x33})},
|
||||||
|
{Address: common.BytesToAddress([]byte{0x03, 0x33})},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive log metadata fields
|
||||||
|
number := big.NewInt(1)
|
||||||
|
hash := common.BytesToHash([]byte{0x03, 0x14})
|
||||||
|
if err := deriveLogFields(receipts, hash, number.Uint64(), txs); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate over all the computed fields and check that they're correct
|
||||||
|
logIndex := uint(0)
|
||||||
|
for i := range receipts {
|
||||||
|
for j := range receipts[i].Logs {
|
||||||
|
if receipts[i].Logs[j].BlockNumber != number.Uint64() {
|
||||||
|
t.Errorf("receipts[%d].Logs[%d].BlockNumber = %d, want %d", i, j, receipts[i].Logs[j].BlockNumber, number.Uint64())
|
||||||
|
}
|
||||||
|
if receipts[i].Logs[j].BlockHash != hash {
|
||||||
|
t.Errorf("receipts[%d].Logs[%d].BlockHash = %s, want %s", i, j, receipts[i].Logs[j].BlockHash.String(), hash.String())
|
||||||
|
}
|
||||||
|
if receipts[i].Logs[j].TxHash != txs[i].Hash() {
|
||||||
|
t.Errorf("receipts[%d].Logs[%d].TxHash = %s, want %s", i, j, receipts[i].Logs[j].TxHash.String(), txs[i].Hash().String())
|
||||||
|
}
|
||||||
|
if receipts[i].Logs[j].TxIndex != uint(i) {
|
||||||
|
t.Errorf("receipts[%d].Logs[%d].TransactionIndex = %d, want %d", i, j, receipts[i].Logs[j].TxIndex, i)
|
||||||
|
}
|
||||||
|
if receipts[i].Logs[j].Index != logIndex {
|
||||||
|
t.Errorf("receipts[%d].Logs[%d].Index = %d, want %d", i, j, receipts[i].Logs[j].Index, logIndex)
|
||||||
|
}
|
||||||
|
logIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkDecodeRLPLogs(b *testing.B) {
|
||||||
|
// Encoded receipts from block 0x14ee094309fbe8f70b65f45ebcc08fb33f126942d97464aad5eb91cfd1e2d269
|
||||||
|
buf, err := ioutil.ReadFile("testdata/stored_receipts.bin")
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
b.Run("ReceiptForStorage", func(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
var r []*types.ReceiptForStorage
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if err := rlp.DecodeBytes(buf, &r); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
b.Run("rlpLogs", func(b *testing.B) {
|
||||||
|
b.ReportAllocs()
|
||||||
|
var r []*receiptLogs
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
if err := rlp.DecodeBytes(buf, &r); err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
66
core/rawdb/schema.go
Normal file
66
core/rawdb/schema.go
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
// 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 contains a collection of low level database accessors.
|
||||||
|
package rawdb
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The fields below define the low level database schema prefixing.
|
||||||
|
var (
|
||||||
|
// Data item prefixes (use single byte to avoid mixing data types, avoid `i`, used for indexes).
|
||||||
|
headerNumberPrefix = []byte("H") // headerNumberPrefix + hash -> num (uint64 big endian)
|
||||||
|
blockBodyPrefix = []byte("b") // blockBodyPrefix + num (uint64 big endian) + hash -> block body
|
||||||
|
|
||||||
|
blockReceiptsPrefix = []byte("r") // blockReceiptsPrefix + num (uint64 big endian) + hash -> block receipts
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// freezerHashTable indicates the name of the freezer canonical hash table.
|
||||||
|
freezerHashTable = "hashes"
|
||||||
|
|
||||||
|
// freezerBodiesTable indicates the name of the freezer block body table.
|
||||||
|
freezerBodiesTable = "bodies"
|
||||||
|
|
||||||
|
// freezerReceiptTable indicates the name of the freezer receipts table.
|
||||||
|
freezerReceiptTable = "receipts"
|
||||||
|
)
|
||||||
|
|
||||||
|
// encodeBlockNumber encodes a block number as big endian uint64
|
||||||
|
func encodeBlockNumber(number uint64) []byte {
|
||||||
|
enc := make([]byte, 8)
|
||||||
|
binary.BigEndian.PutUint64(enc, number)
|
||||||
|
return enc
|
||||||
|
}
|
||||||
|
|
||||||
|
// headerNumberKey = headerNumberPrefix + hash
|
||||||
|
func headerNumberKey(hash common.Hash) []byte {
|
||||||
|
return append(headerNumberPrefix, hash.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockBodyKey = blockBodyPrefix + num (uint64 big endian) + hash
|
||||||
|
func blockBodyKey(number uint64, hash common.Hash) []byte {
|
||||||
|
return append(append(blockBodyPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// blockReceiptsKey = blockReceiptsPrefix + num (uint64 big endian) + hash
|
||||||
|
func blockReceiptsKey(number uint64, hash common.Hash) []byte {
|
||||||
|
return append(append(blockReceiptsPrefix, encodeBlockNumber(number)...), hash.Bytes()...)
|
||||||
|
}
|
||||||
|
|
@ -26,6 +26,8 @@ import (
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
"github.com/XinFinOrg/XDPoSChain/common/hexutil"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -328,3 +330,47 @@ func (r Receipts) GetRlp(i int) []byte {
|
||||||
}
|
}
|
||||||
return bytes
|
return bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeriveFields fills the receipts with their computed fields based on consensus
|
||||||
|
// data and contextual infos like containing block and transactions.
|
||||||
|
func (r Receipts) DeriveFields(config *params.ChainConfig, hash common.Hash, number uint64, txs Transactions) error {
|
||||||
|
signer := MakeSigner(config, new(big.Int).SetUint64(number))
|
||||||
|
|
||||||
|
logIndex := uint(0)
|
||||||
|
if len(txs) != len(r) {
|
||||||
|
return errors.New("transaction and receipt count mismatch")
|
||||||
|
}
|
||||||
|
for i := 0; i < len(r); i++ {
|
||||||
|
// The transaction type and hash can be retrieved from the transaction itself
|
||||||
|
r[i].Type = txs[i].Type()
|
||||||
|
r[i].TxHash = txs[i].Hash()
|
||||||
|
|
||||||
|
// block location fields
|
||||||
|
r[i].BlockHash = hash
|
||||||
|
r[i].BlockNumber = new(big.Int).SetUint64(number)
|
||||||
|
r[i].TransactionIndex = uint(i)
|
||||||
|
|
||||||
|
// The contract address can be derived from the transaction itself
|
||||||
|
if txs[i].To() == nil {
|
||||||
|
// Deriving the signer is expensive, only do if it's actually needed
|
||||||
|
from, _ := Sender(signer, txs[i])
|
||||||
|
r[i].ContractAddress = crypto.CreateAddress(from, txs[i].Nonce())
|
||||||
|
}
|
||||||
|
// The used gas can be calculated based on previous r
|
||||||
|
if i == 0 {
|
||||||
|
r[i].GasUsed = r[i].CumulativeGasUsed
|
||||||
|
} else {
|
||||||
|
r[i].GasUsed = r[i].CumulativeGasUsed - r[i-1].CumulativeGasUsed
|
||||||
|
}
|
||||||
|
// The derived log fields can simply be set from the block and transaction
|
||||||
|
for j := 0; j < len(r[i].Logs); j++ {
|
||||||
|
r[i].Logs[j].BlockNumber = number
|
||||||
|
r[i].Logs[j].BlockHash = hash
|
||||||
|
r[i].Logs[j].TxHash = r[i].TxHash
|
||||||
|
r[i].Logs[j].TxIndex = uint(i)
|
||||||
|
r[i].Logs[j].Index = logIndex
|
||||||
|
logIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,11 +18,14 @@ package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/XinFinOrg/XDPoSChain/common"
|
"github.com/XinFinOrg/XDPoSChain/common"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/crypto"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/params"
|
||||||
"github.com/XinFinOrg/XDPoSChain/rlp"
|
"github.com/XinFinOrg/XDPoSChain/rlp"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -141,6 +144,169 @@ func encodeAsV3StoredReceiptRLP(want *Receipt) ([]byte, error) {
|
||||||
return rlp.EncodeToBytes(stored)
|
return rlp.EncodeToBytes(stored)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests that receipt data can be correctly derived from the contextual infos
|
||||||
|
func TestDeriveFields(t *testing.T) {
|
||||||
|
// Create a few transactions to have receipts for
|
||||||
|
to2 := common.HexToAddress("0x2")
|
||||||
|
to3 := common.HexToAddress("0x3")
|
||||||
|
txs := Transactions{
|
||||||
|
NewTx(&LegacyTx{
|
||||||
|
Nonce: 1,
|
||||||
|
Value: big.NewInt(1),
|
||||||
|
Gas: 1,
|
||||||
|
GasPrice: big.NewInt(1),
|
||||||
|
}),
|
||||||
|
NewTx(&LegacyTx{
|
||||||
|
To: &to2,
|
||||||
|
Nonce: 2,
|
||||||
|
Value: big.NewInt(2),
|
||||||
|
Gas: 2,
|
||||||
|
GasPrice: big.NewInt(2),
|
||||||
|
}),
|
||||||
|
NewTx(&AccessListTx{
|
||||||
|
To: &to3,
|
||||||
|
Nonce: 3,
|
||||||
|
Value: big.NewInt(3),
|
||||||
|
Gas: 3,
|
||||||
|
GasPrice: big.NewInt(3),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
// Create the corresponding receipts
|
||||||
|
receipts := Receipts{
|
||||||
|
&Receipt{
|
||||||
|
Status: ReceiptStatusFailed,
|
||||||
|
CumulativeGasUsed: 1,
|
||||||
|
Logs: []*Log{
|
||||||
|
{Address: common.BytesToAddress([]byte{0x11})},
|
||||||
|
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
|
||||||
|
},
|
||||||
|
TxHash: txs[0].Hash(),
|
||||||
|
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
|
||||||
|
GasUsed: 1,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
PostState: common.Hash{2}.Bytes(),
|
||||||
|
CumulativeGasUsed: 3,
|
||||||
|
Logs: []*Log{
|
||||||
|
{Address: common.BytesToAddress([]byte{0x22})},
|
||||||
|
{Address: common.BytesToAddress([]byte{0x02, 0x22})},
|
||||||
|
},
|
||||||
|
TxHash: txs[1].Hash(),
|
||||||
|
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
|
||||||
|
GasUsed: 2,
|
||||||
|
},
|
||||||
|
&Receipt{
|
||||||
|
Type: AccessListTxType,
|
||||||
|
PostState: common.Hash{3}.Bytes(),
|
||||||
|
CumulativeGasUsed: 6,
|
||||||
|
Logs: []*Log{
|
||||||
|
{Address: common.BytesToAddress([]byte{0x33})},
|
||||||
|
{Address: common.BytesToAddress([]byte{0x03, 0x33})},
|
||||||
|
},
|
||||||
|
TxHash: txs[2].Hash(),
|
||||||
|
ContractAddress: common.BytesToAddress([]byte{0x03, 0x33, 0x33}),
|
||||||
|
GasUsed: 3,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
// Clear all the computed fields and re-derive them
|
||||||
|
number := big.NewInt(1)
|
||||||
|
hash := common.BytesToHash([]byte{0x03, 0x14})
|
||||||
|
|
||||||
|
clearComputedFieldsOnReceipts(t, receipts)
|
||||||
|
if err := receipts.DeriveFields(params.TestChainConfig, hash, number.Uint64(), txs); err != nil {
|
||||||
|
t.Fatalf("DeriveFields(...) = %v, want <nil>", err)
|
||||||
|
}
|
||||||
|
// Iterate over all the computed fields and check that they're correct
|
||||||
|
signer := MakeSigner(params.TestChainConfig, number)
|
||||||
|
|
||||||
|
logIndex := uint(0)
|
||||||
|
for i := range receipts {
|
||||||
|
if receipts[i].Type != txs[i].Type() {
|
||||||
|
t.Errorf("receipts[%d].Type = %d, want %d", i, receipts[i].Type, txs[i].Type())
|
||||||
|
}
|
||||||
|
if receipts[i].TxHash != txs[i].Hash() {
|
||||||
|
t.Errorf("receipts[%d].TxHash = %s, want %s", i, receipts[i].TxHash.String(), txs[i].Hash().String())
|
||||||
|
}
|
||||||
|
if receipts[i].BlockHash != hash {
|
||||||
|
t.Errorf("receipts[%d].BlockHash = %s, want %s", i, receipts[i].BlockHash.String(), hash.String())
|
||||||
|
}
|
||||||
|
if receipts[i].BlockNumber.Cmp(number) != 0 {
|
||||||
|
t.Errorf("receipts[%c].BlockNumber = %s, want %s", i, receipts[i].BlockNumber.String(), number.String())
|
||||||
|
}
|
||||||
|
if receipts[i].TransactionIndex != uint(i) {
|
||||||
|
t.Errorf("receipts[%d].TransactionIndex = %d, want %d", i, receipts[i].TransactionIndex, i)
|
||||||
|
}
|
||||||
|
if receipts[i].GasUsed != txs[i].Gas() {
|
||||||
|
t.Errorf("receipts[%d].GasUsed = %d, want %d", i, receipts[i].GasUsed, txs[i].Gas())
|
||||||
|
}
|
||||||
|
if txs[i].To() != nil && receipts[i].ContractAddress != (common.Address{}) {
|
||||||
|
t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), (common.Address{}).String())
|
||||||
|
}
|
||||||
|
from, _ := Sender(signer, txs[i])
|
||||||
|
contractAddress := crypto.CreateAddress(from, txs[i].Nonce())
|
||||||
|
if txs[i].To() == nil && receipts[i].ContractAddress != contractAddress {
|
||||||
|
t.Errorf("receipts[%d].ContractAddress = %s, want %s", i, receipts[i].ContractAddress.String(), contractAddress.String())
|
||||||
|
}
|
||||||
|
for j := range receipts[i].Logs {
|
||||||
|
if receipts[i].Logs[j].BlockNumber != number.Uint64() {
|
||||||
|
t.Errorf("receipts[%d].Logs[%d].BlockNumber = %d, want %d", i, j, receipts[i].Logs[j].BlockNumber, number.Uint64())
|
||||||
|
}
|
||||||
|
if receipts[i].Logs[j].BlockHash != hash {
|
||||||
|
t.Errorf("receipts[%d].Logs[%d].BlockHash = %s, want %s", i, j, receipts[i].Logs[j].BlockHash.String(), hash.String())
|
||||||
|
}
|
||||||
|
if receipts[i].Logs[j].TxHash != txs[i].Hash() {
|
||||||
|
t.Errorf("receipts[%d].Logs[%d].TxHash = %s, want %s", i, j, receipts[i].Logs[j].TxHash.String(), txs[i].Hash().String())
|
||||||
|
}
|
||||||
|
if receipts[i].Logs[j].TxIndex != uint(i) {
|
||||||
|
t.Errorf("receipts[%d].Logs[%d].TransactionIndex = %d, want %d", i, j, receipts[i].Logs[j].TxIndex, i)
|
||||||
|
}
|
||||||
|
if receipts[i].Logs[j].Index != logIndex {
|
||||||
|
t.Errorf("receipts[%d].Logs[%d].Index = %d, want %d", i, j, receipts[i].Logs[j].Index, logIndex)
|
||||||
|
}
|
||||||
|
logIndex++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
for _, receipt := range receipts {
|
||||||
|
clearComputedFieldsOnReceipt(t, receipt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearComputedFieldsOnReceipt(t *testing.T, receipt *Receipt) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
receipt.TxHash = common.Hash{}
|
||||||
|
receipt.BlockHash = common.Hash{}
|
||||||
|
receipt.BlockNumber = big.NewInt(math.MaxUint32)
|
||||||
|
receipt.TransactionIndex = math.MaxUint32
|
||||||
|
receipt.ContractAddress = common.Address{}
|
||||||
|
receipt.GasUsed = 0
|
||||||
|
|
||||||
|
clearComputedFieldsOnLogs(t, receipt.Logs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearComputedFieldsOnLogs(t *testing.T, logs []*Log) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
for _, log := range logs {
|
||||||
|
clearComputedFieldsOnLog(t, log)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func clearComputedFieldsOnLog(t *testing.T, log *Log) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
log.BlockNumber = math.MaxUint32
|
||||||
|
log.BlockHash = common.Hash{}
|
||||||
|
log.TxHash = common.Hash{}
|
||||||
|
log.TxIndex = math.MaxUint32
|
||||||
|
log.Index = math.MaxUint32
|
||||||
|
}
|
||||||
|
|
||||||
// TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt
|
// TestTypedReceiptEncodingDecoding reproduces a flaw that existed in the receipt
|
||||||
// rlp decoder, which failed due to a shadowing error.
|
// rlp decoder, which failed due to a shadowing error.
|
||||||
func TestTypedReceiptEncodingDecoding(t *testing.T) {
|
func TestTypedReceiptEncodingDecoding(t *testing.T) {
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ import (
|
||||||
"github.com/XinFinOrg/XDPoSChain/contracts"
|
"github.com/XinFinOrg/XDPoSChain/contracts"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core"
|
"github.com/XinFinOrg/XDPoSChain/core"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
|
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
|
||||||
|
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/state"
|
"github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
stateDatabase "github.com/XinFinOrg/XDPoSChain/core/state"
|
stateDatabase "github.com/XinFinOrg/XDPoSChain/core/state"
|
||||||
"github.com/XinFinOrg/XDPoSChain/core/types"
|
"github.com/XinFinOrg/XDPoSChain/core/types"
|
||||||
|
|
@ -233,13 +234,14 @@ func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *EthApiBackend) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) {
|
func (b *EthApiBackend) GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error) {
|
||||||
receipts := core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash))
|
db := b.eth.ChainDb()
|
||||||
if receipts == nil {
|
number := rawdb.ReadHeaderNumber(db, blockHash)
|
||||||
return nil, nil
|
if number == nil {
|
||||||
|
return nil, errors.New("failed to get block number from hash")
|
||||||
}
|
}
|
||||||
logs := make([][]*types.Log, len(receipts))
|
logs := rawdb.ReadLogs(db, blockHash, *number)
|
||||||
for i, receipt := range receipts {
|
if logs == nil {
|
||||||
logs[i] = receipt.Logs
|
return nil, errors.New("failed to get logs for block")
|
||||||
}
|
}
|
||||||
return logs, nil
|
return logs, nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue