Skip to content

Commit f8a2b00

Browse files
JukLee0iragzliudan
authored andcommitted
core/rawdb,eth: use lightweight accessor for log filtering (ethereum#23147)
1 parent c072bba commit f8a2b00

File tree

6 files changed

+810
-7
lines changed

6 files changed

+810
-7
lines changed

core/rawdb/accessors_chain.go

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
// Copyright 2018 The go-ethereum Authors
2+
// This file is part of the go-ethereum library.
3+
//
4+
// The go-ethereum library is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU Lesser General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// The go-ethereum library is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU Lesser General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU Lesser General Public License
15+
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
16+
17+
package rawdb
18+
19+
import (
20+
"bytes"
21+
"encoding/binary"
22+
"errors"
23+
24+
"github.com/XinFinOrg/XDPoSChain/common"
25+
"github.com/XinFinOrg/XDPoSChain/core/types"
26+
"github.com/XinFinOrg/XDPoSChain/ethdb"
27+
"github.com/XinFinOrg/XDPoSChain/log"
28+
"github.com/XinFinOrg/XDPoSChain/params"
29+
"github.com/XinFinOrg/XDPoSChain/rlp"
30+
)
31+
32+
// ReadHeaderNumber returns the header number assigned to a hash.
33+
func ReadHeaderNumber(db ethdb.KeyValueReader, hash common.Hash) *uint64 {
34+
data, _ := db.Get(headerNumberKey(hash))
35+
if len(data) != 8 {
36+
return nil
37+
}
38+
number := binary.BigEndian.Uint64(data)
39+
return &number
40+
}
41+
42+
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
43+
func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
44+
// First try to look up the data in ancient database. Extra hash
45+
// comparison is necessary since ancient database only maintains
46+
// the canonical data.
47+
data, _ := db.Ancient(freezerBodiesTable, number)
48+
if len(data) > 0 {
49+
h, _ := db.Ancient(freezerHashTable, number)
50+
if common.BytesToHash(h) == hash {
51+
return data
52+
}
53+
}
54+
// Then try to look up the data in leveldb.
55+
data, _ = db.Get(blockBodyKey(number, hash))
56+
if len(data) > 0 {
57+
return data
58+
}
59+
// In the background freezer is moving data from leveldb to flatten files.
60+
// So during the first check for ancient db, the data is not yet in there,
61+
// but when we reach into leveldb, the data was already moved. That would
62+
// result in a not found error.
63+
data, _ = db.Ancient(freezerBodiesTable, number)
64+
if len(data) > 0 {
65+
h, _ := db.Ancient(freezerHashTable, number)
66+
if common.BytesToHash(h) == hash {
67+
return data
68+
}
69+
}
70+
return nil // Can't find the data anywhere.
71+
}
72+
73+
// WriteBodyRLP stores an RLP encoded block body into the database.
74+
func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp rlp.RawValue) {
75+
if err := db.Put(blockBodyKey(number, hash), rlp); err != nil {
76+
log.Crit("Failed to store block body", "err", err)
77+
}
78+
}
79+
80+
// ReadBody retrieves the block body corresponding to the hash.
81+
func ReadBody(db ethdb.Reader, hash common.Hash, number uint64) *types.Body {
82+
data := ReadBodyRLP(db, hash, number)
83+
if len(data) == 0 {
84+
return nil
85+
}
86+
body := new(types.Body)
87+
if err := rlp.Decode(bytes.NewReader(data), body); err != nil {
88+
log.Error("Invalid block body RLP", "hash", hash, "err", err)
89+
return nil
90+
}
91+
return body
92+
}
93+
94+
// WriteBody stores a block body into the database.
95+
func WriteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64, body *types.Body) {
96+
data, err := rlp.EncodeToBytes(body)
97+
if err != nil {
98+
log.Crit("Failed to RLP encode body", "err", err)
99+
}
100+
WriteBodyRLP(db, hash, number, data)
101+
}
102+
103+
// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding.
104+
func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
105+
// First try to look up the data in ancient database. Extra hash
106+
// comparison is necessary since ancient database only maintains
107+
// the canonical data.
108+
data, _ := db.Ancient(freezerReceiptTable, number)
109+
if len(data) > 0 {
110+
h, _ := db.Ancient(freezerHashTable, number)
111+
if common.BytesToHash(h) == hash {
112+
return data
113+
}
114+
}
115+
// Then try to look up the data in leveldb.
116+
data, _ = db.Get(blockReceiptsKey(number, hash))
117+
if len(data) > 0 {
118+
return data
119+
}
120+
// In the background freezer is moving data from leveldb to flatten files.
121+
// So during the first check for ancient db, the data is not yet in there,
122+
// but when we reach into leveldb, the data was already moved. That would
123+
// result in a not found error.
124+
data, _ = db.Ancient(freezerReceiptTable, number)
125+
if len(data) > 0 {
126+
h, _ := db.Ancient(freezerHashTable, number)
127+
if common.BytesToHash(h) == hash {
128+
return data
129+
}
130+
}
131+
return nil // Can't find the data anywhere.
132+
}
133+
134+
// ReadRawReceipts retrieves all the transaction receipts belonging to a block.
135+
// The receipt metadata fields are not guaranteed to be populated, so they
136+
// should not be used. Use ReadReceipts instead if the metadata is needed.
137+
func ReadRawReceipts(db ethdb.Reader, hash common.Hash, number uint64) types.Receipts {
138+
// Retrieve the flattened receipt slice
139+
data := ReadReceiptsRLP(db, hash, number)
140+
if len(data) == 0 {
141+
return nil
142+
}
143+
// Convert the receipts from their storage form to their internal representation
144+
storageReceipts := []*types.ReceiptForStorage{}
145+
if err := rlp.DecodeBytes(data, &storageReceipts); err != nil {
146+
log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
147+
return nil
148+
}
149+
receipts := make(types.Receipts, len(storageReceipts))
150+
for i, storageReceipt := range storageReceipts {
151+
receipts[i] = (*types.Receipt)(storageReceipt)
152+
}
153+
return receipts
154+
}
155+
156+
// ReadReceipts retrieves all the transaction receipts belonging to a block, including
157+
// its correspoinding metadata fields. If it is unable to populate these metadata
158+
// fields then nil is returned.
159+
//
160+
// The current implementation populates these metadata fields by reading the receipts'
161+
// corresponding block body, so if the block body is not found it will return nil even
162+
// if the receipt itself is stored.
163+
func ReadReceipts(db ethdb.Reader, hash common.Hash, number uint64, config *params.ChainConfig) types.Receipts {
164+
// We're deriving many fields from the block body, retrieve beside the receipt
165+
receipts := ReadRawReceipts(db, hash, number)
166+
if receipts == nil {
167+
return nil
168+
}
169+
body := ReadBody(db, hash, number)
170+
if body == nil {
171+
log.Error("Missing body but have receipt", "hash", hash, "number", number)
172+
return nil
173+
}
174+
if err := receipts.DeriveFields(config, hash, number, body.Transactions); err != nil {
175+
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
176+
return nil
177+
}
178+
return receipts
179+
}
180+
181+
// WriteReceipts stores all the transaction receipts belonging to a block.
182+
func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, receipts types.Receipts) {
183+
// Convert the receipts into their storage form and serialize them
184+
storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
185+
for i, receipt := range receipts {
186+
storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
187+
}
188+
bytes, err := rlp.EncodeToBytes(storageReceipts)
189+
if err != nil {
190+
log.Crit("Failed to encode block receipts", "err", err)
191+
}
192+
// Store the flattened receipt slice
193+
if err := db.Put(blockReceiptsKey(number, hash), bytes); err != nil {
194+
log.Crit("Failed to store block receipts", "err", err)
195+
}
196+
}
197+
198+
// storedReceiptRLP is the storage encoding of a receipt.
199+
// Re-definition in core/types/receipt.go.
200+
type storedReceiptRLP struct {
201+
PostStateOrStatus []byte
202+
CumulativeGasUsed uint64
203+
Bloom types.Bloom
204+
TxHash common.Hash
205+
ContractAddress common.Address
206+
Logs []*types.LogForStorage
207+
GasUsed uint64
208+
}
209+
210+
// ReceiptLogs is a barebone version of ReceiptForStorage which only keeps
211+
// the list of logs. When decoding a stored receipt into this object we
212+
// avoid creating the bloom filter.
213+
type receiptLogs struct {
214+
Logs []*types.Log
215+
}
216+
217+
// DecodeRLP implements rlp.Decoder.
218+
func (r *receiptLogs) DecodeRLP(s *rlp.Stream) error {
219+
var stored storedReceiptRLP
220+
if err := s.Decode(&stored); err != nil {
221+
return err
222+
}
223+
r.Logs = make([]*types.Log, len(stored.Logs))
224+
for i, log := range stored.Logs {
225+
r.Logs[i] = (*types.Log)(log)
226+
}
227+
return nil
228+
}
229+
230+
// DeriveLogFields fills the logs in receiptLogs with information such as block number, txhash, etc.
231+
func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, txs types.Transactions) error {
232+
logIndex := uint(0)
233+
if len(txs) != len(receipts) {
234+
return errors.New("transaction and receipt count mismatch")
235+
}
236+
for i := 0; i < len(receipts); i++ {
237+
txHash := txs[i].Hash()
238+
// The derived log fields can simply be set from the block and transaction
239+
for j := 0; j < len(receipts[i].Logs); j++ {
240+
receipts[i].Logs[j].BlockNumber = number
241+
receipts[i].Logs[j].BlockHash = hash
242+
receipts[i].Logs[j].TxHash = txHash
243+
receipts[i].Logs[j].TxIndex = uint(i)
244+
receipts[i].Logs[j].Index = logIndex
245+
logIndex++
246+
}
247+
}
248+
return nil
249+
}
250+
251+
// ReadLogs retrieves the logs for all transactions in a block. The log fields
252+
// are populated with metadata. In case the receipts or the block body
253+
// are not found, a nil is returned.
254+
func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log {
255+
// Retrieve the flattened receipt slice
256+
data := ReadReceiptsRLP(db, hash, number)
257+
if len(data) == 0 {
258+
return nil
259+
}
260+
receipts := []*receiptLogs{}
261+
if err := rlp.DecodeBytes(data, &receipts); err != nil {
262+
log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
263+
return nil
264+
}
265+
266+
body := ReadBody(db, hash, number)
267+
if body == nil {
268+
log.Error("Missing body but have receipt", "hash", hash, "number", number)
269+
return nil
270+
}
271+
if err := deriveLogFields(receipts, hash, number, body.Transactions); err != nil {
272+
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
273+
return nil
274+
}
275+
logs := make([][]*types.Log, len(receipts))
276+
for i, receipt := range receipts {
277+
logs[i] = receipt.Logs
278+
}
279+
return logs
280+
}

0 commit comments

Comments
 (0)