Skip to content

Commit f02bfcf

Browse files
committed
core/rawdb: add ReadLogs accessor
1 parent d3546cc commit f02bfcf

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

core/rawdb/accessors_chain.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -680,6 +680,37 @@ func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, t
680680
return nil
681681
}
682682

683+
// ReadLogs retrieves the logs for all transactions in a block. The log fields
684+
// are populated with metadata. In case the receipts or the block body
685+
// are not found, a nil is returned.
686+
func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log {
687+
// Retrieve the flattened receipt slice
688+
data := ReadReceiptsRLP(db, hash, number)
689+
if len(data) == 0 {
690+
return nil
691+
}
692+
receipts := []*receiptLogs{}
693+
if err := rlp.DecodeBytes(data, &receipts); err != nil {
694+
log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
695+
return nil
696+
}
697+
698+
body := ReadBody(db, hash, number)
699+
if body == nil {
700+
log.Error("Missing body but have receipt", "hash", hash, "number", number)
701+
return nil
702+
}
703+
if err := deriveLogFields(receipts, hash, number, body.Transactions); err != nil {
704+
log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
705+
return nil
706+
}
707+
logs := make([][]*types.Log, len(receipts))
708+
for i, receipt := range receipts {
709+
logs[i] = receipt.Logs
710+
}
711+
return logs
712+
}
713+
683714
// ReadBlock retrieves an entire block corresponding to the hash, assembling it
684715
// back from the stored header and body. If either the header or body could not
685716
// be retrieved nil is returned.

core/rawdb/accessors_chain_test.go

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,114 @@ func TestCanonicalHashIteration(t *testing.T) {
529529
}
530530
}
531531

532+
type fullLogRLP struct {
533+
Address common.Address
534+
Topics []common.Hash
535+
Data []byte
536+
BlockNumber uint64
537+
TxHash common.Hash
538+
TxIndex uint
539+
BlockHash common.Hash
540+
Index uint
541+
}
542+
543+
func newFullLogRLP(l *types.Log) *fullLogRLP {
544+
return &fullLogRLP{
545+
Address: l.Address,
546+
Topics: l.Topics,
547+
Data: l.Data,
548+
BlockNumber: l.BlockNumber,
549+
TxHash: l.TxHash,
550+
TxIndex: l.TxIndex,
551+
BlockHash: l.BlockHash,
552+
Index: l.Index,
553+
}
554+
}
555+
556+
// Tests that logs associated with a single block can be retrieved.
557+
func TestReadLogs(t *testing.T) {
558+
db := NewMemoryDatabase()
559+
560+
// Create a live block since we need metadata to reconstruct the receipt
561+
tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
562+
tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil)
563+
564+
body := &types.Body{Transactions: types.Transactions{tx1, tx2}}
565+
566+
// Create the two receipts to manage afterwards
567+
receipt1 := &types.Receipt{
568+
Status: types.ReceiptStatusFailed,
569+
CumulativeGasUsed: 1,
570+
Logs: []*types.Log{
571+
{Address: common.BytesToAddress([]byte{0x11})},
572+
{Address: common.BytesToAddress([]byte{0x01, 0x11})},
573+
},
574+
TxHash: tx1.Hash(),
575+
ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
576+
GasUsed: 111111,
577+
}
578+
receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
579+
580+
receipt2 := &types.Receipt{
581+
PostState: common.Hash{2}.Bytes(),
582+
CumulativeGasUsed: 2,
583+
Logs: []*types.Log{
584+
{Address: common.BytesToAddress([]byte{0x22})},
585+
{Address: common.BytesToAddress([]byte{0x02, 0x22})},
586+
},
587+
TxHash: tx2.Hash(),
588+
ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
589+
GasUsed: 222222,
590+
}
591+
receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
592+
receipts := []*types.Receipt{receipt1, receipt2}
593+
594+
hash := common.BytesToHash([]byte{0x03, 0x14})
595+
// Check that no receipt entries are in a pristine database
596+
if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 {
597+
t.Fatalf("non existent receipts returned: %v", rs)
598+
}
599+
// Insert the body that corresponds to the receipts
600+
WriteBody(db, hash, 0, body)
601+
602+
// Insert the receipt slice into the database and check presence
603+
WriteReceipts(db, hash, 0, receipts)
604+
605+
logs := ReadLogs(db, hash, 0)
606+
if len(logs) == 0 {
607+
t.Fatalf("no logs returned")
608+
}
609+
if have, want := len(logs), 2; have != want {
610+
t.Fatalf("unexpected number of logs returned, have %d want %d", have, want)
611+
}
612+
if have, want := len(logs[0]), 2; have != want {
613+
t.Fatalf("unexpected number of logs[0] returned, have %d want %d", have, want)
614+
}
615+
if have, want := len(logs[1]), 2; have != want {
616+
t.Fatalf("unexpected number of logs[1] returned, have %d want %d", have, want)
617+
}
618+
619+
// Fill in log fields so we can compare their rlp encoding
620+
if err := types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, 0, body.Transactions); err != nil {
621+
t.Fatal(err)
622+
}
623+
for i, pr := range receipts {
624+
for j, pl := range pr.Logs {
625+
rlpHave, err := rlp.EncodeToBytes(newFullLogRLP(logs[i][j]))
626+
if err != nil {
627+
t.Fatal(err)
628+
}
629+
rlpWant, err := rlp.EncodeToBytes(newFullLogRLP(pl))
630+
if err != nil {
631+
t.Fatal(err)
632+
}
633+
if !bytes.Equal(rlpHave, rlpWant) {
634+
t.Fatalf("receipt #%d: receipt mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant))
635+
}
636+
}
637+
}
638+
}
639+
532640
func TestDeriveLogFields(t *testing.T) {
533641
// Create a few transactions to have receipts for
534642
to2 := common.HexToAddress("0x2")

0 commit comments

Comments
 (0)