Skip to content

Commit

Permalink
core, eth, les, light: avoid storing computable receipt metadata (19345)
Browse files Browse the repository at this point in the history
  • Loading branch information
JukLee0ira committed Sep 12, 2024
1 parent efaba63 commit 6196f88
Show file tree
Hide file tree
Showing 17 changed files with 123 additions and 93 deletions.
6 changes: 5 additions & 1 deletion accounts/abi/bind/backends/simulated.go
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,11 @@ func (fb *filterBackend) HeaderByHash(ctx context.Context, hash common.Hash) (*t
}

func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil
number := rawdb.ReadHeaderNumber(fb.db, hash)
if number == nil {
return nil, nil
}
return rawdb.ReadReceipts(fb.db, hash, *number, fb.bc.Config()), nil
}

func (fb *filterBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.BlockNumber) (*types.Body, error) {
Expand Down
3 changes: 2 additions & 1 deletion contracts/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/contracts/blocksigner/contract"
randomizeContract "github.com/XinFinOrg/XDPoSChain/contracts/randomize/contract"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
stateDatabase "github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
Expand Down Expand Up @@ -338,7 +339,7 @@ func GetRewardForCheckpoint(c *XDPoS.XDPoS, chain consensus.ChainReader, header
block := chain.GetBlock(header.Hash(), i)
txs := block.Transactions()
if !chain.Config().IsTIPSigning(header.Number) {
receipts := core.GetBlockReceipts(c.GetDb(), header.Hash(), i)
receipts := rawdb.ReadReceipts(c.GetDb(), header.Hash(), i, chain.Config())
signData = c.CacheNoneTIPSigningTxs(header, txs, receipts)
} else {
signData = c.CacheSigningTxs(header.Hash(), txs)
Expand Down
2 changes: 1 addition & 1 deletion core/bench_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if full {
hash := header.Hash()
GetBody(db, hash, n)
GetBlockReceipts(db, hash, n)
rawdb.ReadReceipts(db, hash, n, chain.Config())
}
}

Expand Down
4 changes: 2 additions & 2 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -807,7 +807,7 @@ func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {

// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
return GetBlockReceipts(bc.db, hash, GetBlockNumber(bc.db, hash))
return rawdb.ReadReceipts(bc.db, hash, GetBlockNumber(bc.db, hash), bc.chainConfig)
}

// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
Expand Down Expand Up @@ -2147,7 +2147,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
// These logs are later announced as deleted.
collectLogs = func(h common.Hash) {
// Coalesce logs and set 'Removed'.
receipts := GetBlockReceipts(bc.db, h, bc.hc.GetBlockNumber(h))
receipts := rawdb.ReadReceipts(bc.db, h, bc.hc.GetBlockNumber(h), bc.chainConfig)
for _, receipt := range receipts {
for _, log := range receipt.Logs {
del := *log
Expand Down
2 changes: 1 addition & 1 deletion core/blockchain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ func TestFastVsFullChains(t *testing.T) {
} else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(ablock.Uncles()) {
t.Errorf("block #%d [%x]: uncles mismatch: have %v, want %v", num, hash, fblock.Uncles(), ablock.Uncles())
}
if freceipts, areceipts := GetBlockReceipts(fastDb, hash, GetBlockNumber(fastDb, hash)), GetBlockReceipts(archiveDb, hash, GetBlockNumber(archiveDb, hash)); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) {
if freceipts, areceipts := rawdb.ReadReceipts(fastDb, hash, GetBlockNumber(fastDb, hash), fast.Config()), rawdb.ReadReceipts(archiveDb, hash, GetBlockNumber(archiveDb, hash), archive.Config()); types.DeriveSha(freceipts) != types.DeriveSha(areceipts) {
t.Errorf("block #%d [%x]: receipts mismatch: have %v, want %v", num, hash, freceipts, areceipts)
}
}
Expand Down
4 changes: 2 additions & 2 deletions core/database_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -298,12 +298,12 @@ func GetTransaction(db DatabaseReader, hash common.Hash) (*types.Transaction, co

// GetReceipt retrieves a specific transaction receipt from the database, along with
// its added positional metadata.
func GetReceipt(db DatabaseReader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) {
func GetReceipt(db ethdb.Reader, hash common.Hash) (*types.Receipt, common.Hash, uint64, uint64) {
// Retrieve the lookup metadata and resolve the receipt from the receipts
blockHash, blockNumber, receiptIndex := GetTxLookupEntry(db, hash)

if blockHash != (common.Hash{}) {
receipts := GetBlockReceipts(db, blockHash, blockNumber)
receipts := rawdb.ReadRawReceipts(db, blockHash, blockNumber)
if len(receipts) <= int(receiptIndex) {
log.Error("Receipt refereced missing", "number", blockNumber, "hash", blockHash, "index", receiptIndex)
return nil, common.Hash{}, 0, 0
Expand Down
65 changes: 0 additions & 65 deletions core/database_util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package core

import (
"bytes"
"math/big"
"testing"

Expand Down Expand Up @@ -310,67 +309,3 @@ func TestLookupStorage(t *testing.T) {
}
}
}

// Tests that receipts associated with a single block can be stored and retrieved.
func TestBlockReceiptStorage(t *testing.T) {
db := rawdb.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)

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}

// Check that no receipt entries are in a pristine database
hash := common.BytesToHash([]byte{0x03, 0x14})
if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 {
t.Fatalf("non existent receipts returned: %v", rs)
}
// Insert the receipt slice into the database and check presence
if err := WriteBlockReceipts(db, hash, 0, receipts); err != nil {
t.Fatalf("failed to write block receipts: %v", err)
}
if rs := GetBlockReceipts(db, hash, 0); len(rs) == 0 {
t.Fatalf("no receipts returned")
} else {
for i := 0; i < len(receipts); i++ {
rlpHave, _ := rlp.EncodeToBytes(rs[i])
rlpWant, _ := rlp.EncodeToBytes(receipts[i])

if !bytes.Equal(rlpHave, rlpWant) {
t.Fatalf("receipt #%d: receipt mismatch: have %v, want %v", i, rs[i], receipts[i])
}
}
}
// Delete the receipt slice and check purge
DeleteBlockReceipts(db, hash, 0)
if rs := GetBlockReceipts(db, hash, 0); len(rs) != 0 {
t.Fatalf("deleted receipts returned: %v", rs)
}
}
7 changes: 7 additions & 0 deletions core/rawdb/accessors_chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,13 @@ func WriteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rec
}
}

// DeleteReceipts removes all receipt data associated with a block hash.
func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
if err := db.Delete(blockReceiptsKey(number, hash)); err != nil {
log.Crit("Failed to delete block receipts", "err", err)
}
}

// storedReceiptRLP is the storage encoding of a receipt.
// Re-definition in core/types/receipt.go.
type storedReceiptRLP struct {
Expand Down
83 changes: 83 additions & 0 deletions core/rawdb/accessors_chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package rawdb
import (
"bytes"
"encoding/hex"
"fmt"
"io/ioutil"
"math/big"
"testing"
Expand All @@ -29,6 +30,88 @@ import (
"github.com/XinFinOrg/XDPoSChain/rlp"
)

// Tests that receipts associated with a single block can be stored and retrieved.
func TestBlockReceiptStorage(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}}

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}

// Check that no receipt entries are in a pristine database
hash := common.BytesToHash([]byte{0x03, 0x14})
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)
if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) == 0 {
t.Fatalf("no receipts returned")
} else {
if err := checkReceiptsRLP(rs, receipts); err != nil {
t.Fatalf(err.Error())
}
}
// Delete the receipt slice and check purge
DeleteReceipts(db, hash, 0)
if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 {
t.Fatalf("deleted receipts returned: %v", rs)
}
}

func checkReceiptsRLP(have, want types.Receipts) error {
if len(have) != len(want) {
return fmt.Errorf("receipts sizes mismatch: have %d, want %d", len(have), len(want))
}
for i := 0; i < len(want); i++ {
rlpHave, err := rlp.EncodeToBytes(have[i])
if err != nil {
return err
}
rlpWant, err := rlp.EncodeToBytes(want[i])
if err != nil {
return err
}
if !bytes.Equal(rlpHave, rlpWant) {
return fmt.Errorf("receipt #%d: receipt mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant))
}
}
return nil
}

type fullLogRLP struct {
Address common.Address
Topics []common.Hash
Expand Down
2 changes: 1 addition & 1 deletion eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ func (b *EthApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*t
}

func (b *EthApiBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
return core.GetBlockReceipts(b.eth.chainDb, blockHash, core.GetBlockNumber(b.eth.chainDb, blockHash)), nil
return b.eth.blockchain.GetReceiptsByHash(blockHash), nil
}

func (b *EthApiBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
Expand Down
3 changes: 2 additions & 1 deletion eth/downloader/fakepeer.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb"
)
Expand Down Expand Up @@ -140,7 +141,7 @@ func (p *FakePeer) RequestBodies(hashes []common.Hash) error {
func (p *FakePeer) RequestReceipts(hashes []common.Hash) error {
var receipts [][]*types.Receipt
for _, hash := range hashes {
receipts = append(receipts, core.GetBlockReceipts(p.db, hash, p.hc.GetBlockNumber(hash)))
receipts = append(receipts, rawdb.ReadRawReceipts(p.db, hash, p.hc.GetBlockNumber(hash)))
}
p.dl.DeliverReceipts(p.id, receipts)
return nil
Expand Down
8 changes: 5 additions & 3 deletions eth/filters/filter_system_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,11 @@ func (b *testBackend) GetBody(ctx context.Context, hash common.Hash, number rpc.
return nil, errors.New("block body not found")
}

func (b *testBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
number := core.GetBlockNumber(b.db, blockHash)
return core.GetBlockReceipts(b.db, blockHash, number), nil
func (b *testBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
if number := rawdb.ReadHeaderNumber(b.db, hash); number != nil {
return rawdb.ReadReceipts(b.db, hash, *number, params.TestChainConfig), nil
}
return nil, nil
}

func (b *testBackend) GetLogs(ctx context.Context, hash common.Hash, number uint64) ([][]*types.Log, error) {
Expand Down
2 changes: 1 addition & 1 deletion les/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
break
}
// Retrieve the requested block's receipts, skipping if unknown to us
results := core.GetBlockReceipts(pm.chainDb, hash, core.GetBlockNumber(pm.chainDb, hash))
results := rawdb.ReadRawReceipts(pm.chainDb, hash, core.GetBlockNumber(pm.chainDb, hash))
if results == nil {
if header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash {
continue
Expand Down
2 changes: 1 addition & 1 deletion les/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@ func testGetReceipt(t *testing.T, protocol int) {
block := bc.GetBlockByNumber(i)

hashes = append(hashes, block.Hash())
receipts = append(receipts, core.GetBlockReceipts(db, block.Hash(), block.NumberU64()))
receipts = append(receipts, rawdb.ReadRawReceipts(db, block.Hash(), block.NumberU64()))
}
// Send the hash request and verify the response
cost := peer.GetRequestCost(GetReceiptsMsg, len(hashes))
Expand Down
2 changes: 1 addition & 1 deletion les/odr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func odrGetBlock(ctx context.Context, db ethdb.Database, config *params.ChainCon
func odrGetReceipts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte {
var receipts types.Receipts
if bc != nil {
receipts = core.GetBlockReceipts(db, bhash, core.GetBlockNumber(db, bhash))
receipts = rawdb.ReadReceipts(db, bhash, core.GetBlockNumber(db, bhash), bc.Config())
} else {
receipts, _ = light.GetBlockReceipts(ctx, lc.Odr(), bhash, core.GetBlockNumber(db, bhash))
}
Expand Down
4 changes: 2 additions & 2 deletions light/odr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
case *BlockRequest:
req.Rlp = core.GetBodyRLP(odr.sdb, req.Hash, core.GetBlockNumber(odr.sdb, req.Hash))
case *ReceiptsRequest:
req.Receipts = core.GetBlockReceipts(odr.sdb, req.Hash, core.GetBlockNumber(odr.sdb, req.Hash))
req.Receipts = rawdb.ReadRawReceipts(odr.sdb, req.Hash, core.GetBlockNumber(odr.sdb, req.Hash))
case *TrieRequest:
t, _ := trie.New(req.Id.Root, trie.NewDatabase(odr.sdb))
nodes := NewNodeSet()
Expand Down Expand Up @@ -111,7 +111,7 @@ func TestOdrGetReceiptsLes1(t *testing.T) { testChainOdr(t, 1, odrGetReceipts) }
func odrGetReceipts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) {
var receipts types.Receipts
if bc != nil {
receipts = core.GetBlockReceipts(db, bhash, core.GetBlockNumber(db, bhash))
receipts = rawdb.ReadReceipts(db, bhash, core.GetBlockNumber(db, bhash), bc.Config())
} else {
receipts, _ = GetBlockReceipts(ctx, lc.Odr(), bhash, core.GetBlockNumber(db, bhash))
}
Expand Down
17 changes: 7 additions & 10 deletions light/odr_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (

"github.com/XinFinOrg/XDPoSChain/common"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/crypto"
"github.com/XinFinOrg/XDPoSChain/rlp"
Expand Down Expand Up @@ -127,7 +128,7 @@ func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint
// in a block given by its hash. Receipts will be filled in with context data.
func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (types.Receipts, error) {
// Retrieve the potentially incomplete receipts from disk or network
receipts := core.GetBlockReceipts(odr.Database(), hash, number)
receipts := rawdb.ReadRawReceipts(odr.Database(), hash, number)
if receipts == nil {
r := &ReceiptsRequest{Hash: hash, Number: number}
if err := odr.Retrieve(ctx, r); err != nil {
Expand All @@ -153,17 +154,13 @@ func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, num
}

// GetBlockLogs retrieves the logs generated by the transactions included in a
// block given by its hash. Logs will be filled in with context data.
// block given by its hash.
func GetBlockLogs(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) ([][]*types.Log, error) {
receipts := core.GetBlockReceipts(odr.Database(), hash, number)
if receipts == nil {
r := &ReceiptsRequest{Hash: hash, Number: number}
if err := odr.Retrieve(ctx, r); err != nil {
return nil, err
}
receipts = r.Receipts
// Retrieve the potentially incomplete receipts from disk or network
receipts, err := GetBlockReceipts(ctx, odr, hash, number)
if err != nil {
return nil, err
}
// Return the logs without deriving any computed fields on the receipts
logs := make([][]*types.Log, len(receipts))
for i, receipt := range receipts {
logs[i] = receipt.Logs
Expand Down

0 comments on commit 6196f88

Please sign in to comment.