mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-23 20:17:17 +00:00
802 lines
23 KiB
Go
802 lines
23 KiB
Go
package snapshotsync
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/binary"
|
|
"fmt"
|
|
|
|
libcommon "github.com/ledgerwatch/erigon-lib/common"
|
|
"github.com/ledgerwatch/erigon-lib/common/dbg"
|
|
"github.com/ledgerwatch/erigon-lib/gointerfaces"
|
|
"github.com/ledgerwatch/erigon-lib/gointerfaces/remote"
|
|
"github.com/ledgerwatch/erigon-lib/kv"
|
|
"github.com/ledgerwatch/erigon-lib/recsplit"
|
|
|
|
"github.com/ledgerwatch/erigon/core/rawdb"
|
|
"github.com/ledgerwatch/erigon/core/types"
|
|
"github.com/ledgerwatch/erigon/rlp"
|
|
)
|
|
|
|
// BlockReader can read blocks from db and snapshots
|
|
type BlockReader struct {
|
|
}
|
|
|
|
func NewBlockReader() *BlockReader {
|
|
return &BlockReader{}
|
|
}
|
|
|
|
func (back *BlockReader) CanonicalHash(ctx context.Context, tx kv.Getter, blockHeight uint64) (libcommon.Hash, error) {
|
|
return rawdb.ReadCanonicalHash(tx, blockHeight)
|
|
}
|
|
|
|
func (back *BlockReader) Snapshots() *RoSnapshots { return nil }
|
|
|
|
func (back *BlockReader) Header(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (*types.Header, error) {
|
|
h := rawdb.ReadHeader(tx, hash, blockHeight)
|
|
return h, nil
|
|
}
|
|
|
|
func (back *BlockReader) Body(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (body *types.Body, txAmount uint32, err error) {
|
|
body, _, txAmount = rawdb.ReadBody(tx, hash, blockHeight)
|
|
return body, txAmount, nil
|
|
}
|
|
|
|
func (back *BlockReader) BodyWithTransactions(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (body *types.Body, err error) {
|
|
return rawdb.ReadBodyWithTransactions(tx, hash, blockHeight)
|
|
}
|
|
|
|
func (back *BlockReader) BodyRlp(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (bodyRlp rlp.RawValue, err error) {
|
|
body, _, err := back.Body(ctx, tx, hash, blockHeight)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bodyRlp, err = rlp.EncodeToBytes(body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bodyRlp, nil
|
|
}
|
|
|
|
func (back *BlockReader) HeaderByNumber(ctx context.Context, tx kv.Getter, blockHeight uint64) (*types.Header, error) {
|
|
h := rawdb.ReadHeaderByNumber(tx, blockHeight)
|
|
return h, nil
|
|
}
|
|
|
|
func (back *BlockReader) HeaderByHash(ctx context.Context, tx kv.Getter, hash libcommon.Hash) (*types.Header, error) {
|
|
return rawdb.ReadHeaderByHash(tx, hash)
|
|
}
|
|
|
|
func (back *BlockReader) BlockWithSenders(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (block *types.Block, senders []libcommon.Address, err error) {
|
|
canonicalHash, err := rawdb.ReadCanonicalHash(tx, blockHeight)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("requested non-canonical hash %x. canonical=%x", hash, canonicalHash)
|
|
}
|
|
if canonicalHash == hash {
|
|
block, senders, err = rawdb.ReadBlockWithSenders(tx, hash, blockHeight)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return block, senders, nil
|
|
}
|
|
|
|
return rawdb.NonCanonicalBlockWithSenders(tx, hash, blockHeight)
|
|
}
|
|
|
|
func (back *BlockReader) TxnLookup(ctx context.Context, tx kv.Getter, txnHash libcommon.Hash) (uint64, bool, error) {
|
|
n, err := rawdb.ReadTxLookupEntry(tx, txnHash)
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
if n == nil {
|
|
return 0, false, nil
|
|
}
|
|
return *n, true, nil
|
|
}
|
|
func (back *BlockReader) TxnByIdxInBlock(ctx context.Context, tx kv.Getter, blockNum uint64, i int) (txn types.Transaction, err error) {
|
|
canonicalHash, err := rawdb.ReadCanonicalHash(tx, blockNum)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var k [8 + 32]byte
|
|
binary.BigEndian.PutUint64(k[:], blockNum)
|
|
copy(k[8:], canonicalHash[:])
|
|
b, err := rawdb.ReadBodyForStorageByKey(tx, k[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if b == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
txn, err = rawdb.CanonicalTxnByID(tx, b.BaseTxId+1+uint64(i))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return txn, nil
|
|
}
|
|
|
|
type RemoteBlockReader struct {
|
|
client remote.ETHBACKENDClient
|
|
}
|
|
|
|
func (back *RemoteBlockReader) HeaderByNumber(ctx context.Context, tx kv.Getter, blockHeight uint64) (*types.Header, error) {
|
|
canonicalHash, err := rawdb.ReadCanonicalHash(tx, blockHeight)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
block, _, err := back.BlockWithSenders(ctx, tx, canonicalHash, blockHeight)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return block.Header(), nil
|
|
}
|
|
|
|
func (back *RemoteBlockReader) Snapshots() *RoSnapshots { return nil }
|
|
|
|
func (back *RemoteBlockReader) HeaderByHash(ctx context.Context, tx kv.Getter, hash libcommon.Hash) (*types.Header, error) {
|
|
blockNum := rawdb.ReadHeaderNumber(tx, hash)
|
|
if blockNum == nil {
|
|
return nil, nil
|
|
}
|
|
block, _, err := back.BlockWithSenders(ctx, tx, hash, *blockNum)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return block.Header(), nil
|
|
}
|
|
|
|
func (back *RemoteBlockReader) CanonicalHash(ctx context.Context, tx kv.Getter, blockHeight uint64) (libcommon.Hash, error) {
|
|
return rawdb.ReadCanonicalHash(tx, blockHeight)
|
|
}
|
|
|
|
func NewRemoteBlockReader(client remote.ETHBACKENDClient) *RemoteBlockReader {
|
|
return &RemoteBlockReader{client}
|
|
}
|
|
|
|
func (back *RemoteBlockReader) TxnLookup(ctx context.Context, tx kv.Getter, txnHash libcommon.Hash) (uint64, bool, error) {
|
|
reply, err := back.client.TxnLookup(ctx, &remote.TxnLookupRequest{TxnHash: gointerfaces.ConvertHashToH256(txnHash)})
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
if reply == nil {
|
|
return 0, false, nil
|
|
}
|
|
return reply.BlockNumber, true, nil
|
|
}
|
|
|
|
func (back *RemoteBlockReader) TxnByIdxInBlock(ctx context.Context, tx kv.Getter, blockNum uint64, i int) (txn types.Transaction, err error) {
|
|
canonicalHash, err := rawdb.ReadCanonicalHash(tx, blockNum)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b, err := back.BodyWithTransactions(ctx, tx, canonicalHash, blockNum)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if b == nil {
|
|
return nil, nil
|
|
}
|
|
if i < 0 {
|
|
return nil, nil
|
|
}
|
|
if len(b.Transactions) <= i {
|
|
return nil, nil
|
|
}
|
|
return b.Transactions[i], nil
|
|
}
|
|
|
|
func (back *RemoteBlockReader) BlockWithSenders(ctx context.Context, _ kv.Getter, hash libcommon.Hash, blockHeight uint64) (block *types.Block, senders []libcommon.Address, err error) {
|
|
reply, err := back.client.Block(ctx, &remote.BlockRequest{BlockHash: gointerfaces.ConvertHashToH256(hash), BlockHeight: blockHeight})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
block = &types.Block{}
|
|
err = rlp.Decode(bytes.NewReader(reply.BlockRlp), block)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
senders = make([]libcommon.Address, len(reply.Senders)/20)
|
|
for i := range senders {
|
|
senders[i].SetBytes(reply.Senders[i*20 : (i+1)*20])
|
|
}
|
|
if len(senders) == block.Transactions().Len() { //it's fine if no senders provided - they can be lazy recovered
|
|
block.SendersToTxs(senders)
|
|
}
|
|
return block, senders, nil
|
|
}
|
|
|
|
func (back *RemoteBlockReader) Header(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (*types.Header, error) {
|
|
block, _, err := back.BlockWithSenders(ctx, tx, hash, blockHeight)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if block == nil {
|
|
return nil, nil
|
|
}
|
|
return block.Header(), nil
|
|
}
|
|
func (back *RemoteBlockReader) Body(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (body *types.Body, txAmount uint32, err error) {
|
|
block, _, err := back.BlockWithSenders(ctx, tx, hash, blockHeight)
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if block == nil {
|
|
return nil, 0, nil
|
|
}
|
|
return block.Body(), uint32(len(block.Body().Transactions)), nil
|
|
}
|
|
func (back *RemoteBlockReader) BodyWithTransactions(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (body *types.Body, err error) {
|
|
block, _, err := back.BlockWithSenders(ctx, tx, hash, blockHeight)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if block == nil {
|
|
return nil, nil
|
|
}
|
|
return block.Body(), nil
|
|
}
|
|
|
|
func (back *RemoteBlockReader) BodyRlp(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (bodyRlp rlp.RawValue, err error) {
|
|
body, err := back.BodyWithTransactions(ctx, tx, hash, blockHeight)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bodyRlp, err = rlp.EncodeToBytes(body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bodyRlp, nil
|
|
}
|
|
|
|
// BlockReaderWithSnapshots can read blocks from db and snapshots
|
|
type BlockReaderWithSnapshots struct {
|
|
sn *RoSnapshots
|
|
}
|
|
|
|
func NewBlockReaderWithSnapshots(snapshots *RoSnapshots) *BlockReaderWithSnapshots {
|
|
return &BlockReaderWithSnapshots{sn: snapshots}
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) Snapshots() *RoSnapshots { return back.sn }
|
|
|
|
func (back *BlockReaderWithSnapshots) HeaderByNumber(ctx context.Context, tx kv.Getter, blockHeight uint64) (h *types.Header, err error) {
|
|
ok, err := back.sn.ViewHeaders(blockHeight, func(segment *HeaderSegment) error {
|
|
h, _, err = back.headerFromSnapshot(blockHeight, segment, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
return h, nil
|
|
}
|
|
h = rawdb.ReadHeaderByNumber(tx, blockHeight)
|
|
return h, nil
|
|
}
|
|
|
|
// HeaderByHash - will search header in all snapshots starting from recent
|
|
func (back *BlockReaderWithSnapshots) HeaderByHash(ctx context.Context, tx kv.Getter, hash libcommon.Hash) (h *types.Header, err error) {
|
|
h, err = rawdb.ReadHeaderByHash(tx, hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if h != nil {
|
|
return h, nil
|
|
}
|
|
|
|
buf := make([]byte, 128)
|
|
if err := back.sn.Headers.View(func(segments []*HeaderSegment) error {
|
|
for i := len(segments) - 1; i >= 0; i-- {
|
|
if segments[i].idxHeaderHash == nil {
|
|
continue
|
|
}
|
|
|
|
h, err = back.headerFromSnapshotByHash(hash, segments[i], buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if h != nil {
|
|
break
|
|
}
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return nil, err
|
|
}
|
|
return h, nil
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) CanonicalHash(ctx context.Context, tx kv.Getter, blockHeight uint64) (h libcommon.Hash, err error) {
|
|
ok, err := back.sn.ViewHeaders(blockHeight, func(segment *HeaderSegment) error {
|
|
header, _, err := back.headerFromSnapshot(blockHeight, segment, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if header == nil {
|
|
return nil
|
|
}
|
|
h = header.Hash()
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return h, err
|
|
}
|
|
if ok {
|
|
return h, nil
|
|
}
|
|
|
|
return rawdb.ReadCanonicalHash(tx, blockHeight)
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) Header(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (h *types.Header, err error) {
|
|
ok, err := back.sn.ViewHeaders(blockHeight, func(segment *HeaderSegment) error {
|
|
h, _, err = back.headerFromSnapshot(blockHeight, segment, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return h, err
|
|
}
|
|
if ok {
|
|
return h, nil
|
|
}
|
|
|
|
h = rawdb.ReadHeader(tx, hash, blockHeight)
|
|
return h, nil
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) BodyWithTransactions(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (body *types.Body, err error) {
|
|
var baseTxnID uint64
|
|
var txsAmount uint32
|
|
var buf []byte
|
|
ok, err := back.sn.ViewBodies(blockHeight, func(seg *BodySegment) error {
|
|
body, baseTxnID, txsAmount, buf, err = back.bodyFromSnapshot(blockHeight, seg, buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
ok, err = back.sn.ViewTxs(blockHeight, func(seg *TxnSegment) error {
|
|
txs, senders, err := back.txsFromSnapshot(baseTxnID, txsAmount, seg, buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if txs == nil {
|
|
return nil
|
|
}
|
|
body.Transactions = txs
|
|
body.SendersToTxs(senders)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
return body, nil
|
|
}
|
|
}
|
|
|
|
body, err = rawdb.ReadBodyWithTransactions(tx, hash, blockHeight)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return body, nil
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) BodyRlp(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (bodyRlp rlp.RawValue, err error) {
|
|
body, err := back.BodyWithTransactions(ctx, tx, hash, blockHeight)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bodyRlp, err = rlp.EncodeToBytes(body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return bodyRlp, nil
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) Body(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (body *types.Body, txAmount uint32, err error) {
|
|
ok, err := back.sn.ViewBodies(blockHeight, func(seg *BodySegment) error {
|
|
body, _, txAmount, _, err = back.bodyFromSnapshot(blockHeight, seg, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, 0, err
|
|
}
|
|
if ok {
|
|
return body, txAmount, nil
|
|
}
|
|
body, _, txAmount = rawdb.ReadBody(tx, hash, blockHeight)
|
|
return body, txAmount, nil
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) BlockWithSenders(ctx context.Context, tx kv.Getter, hash libcommon.Hash, blockHeight uint64) (block *types.Block, senders []libcommon.Address, err error) {
|
|
var buf []byte
|
|
var h *types.Header
|
|
ok, err := back.sn.ViewHeaders(blockHeight, func(seg *HeaderSegment) error {
|
|
h, buf, err = back.headerFromSnapshot(blockHeight, seg, buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if ok && h != nil {
|
|
var b *types.Body
|
|
var baseTxnId uint64
|
|
var txsAmount uint32
|
|
ok, err = back.sn.ViewBodies(blockHeight, func(seg *BodySegment) error {
|
|
b, baseTxnId, txsAmount, buf, err = back.bodyFromSnapshot(blockHeight, seg, buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if ok && b != nil {
|
|
if txsAmount == 0 {
|
|
block = types.NewBlockFromStorage(hash, h, nil, b.Uncles, b.Withdrawals)
|
|
if len(senders) != block.Transactions().Len() {
|
|
return block, senders, nil // no senders is fine - will recover them on the fly
|
|
}
|
|
block.SendersToTxs(senders)
|
|
return block, senders, nil
|
|
}
|
|
var txs []types.Transaction
|
|
var senders []libcommon.Address
|
|
ok, err = back.sn.ViewTxs(blockHeight, func(seg *TxnSegment) error {
|
|
txs, senders, err = back.txsFromSnapshot(baseTxnId, txsAmount, seg, buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
if ok {
|
|
block = types.NewBlockFromStorage(hash, h, txs, b.Uncles, b.Withdrawals)
|
|
if len(senders) != block.Transactions().Len() {
|
|
return block, senders, nil // no senders is fine - will recover them on the fly
|
|
}
|
|
block.SendersToTxs(senders)
|
|
return block, senders, nil
|
|
}
|
|
}
|
|
}
|
|
canonicalHash, err := rawdb.ReadCanonicalHash(tx, blockHeight)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("requested non-canonical hash %x. canonical=%x", hash, canonicalHash)
|
|
}
|
|
if canonicalHash == hash {
|
|
block, senders, err = rawdb.ReadBlockWithSenders(tx, hash, blockHeight)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
return block, senders, nil
|
|
}
|
|
return rawdb.NonCanonicalBlockWithSenders(tx, hash, blockHeight)
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) headerFromSnapshot(blockHeight uint64, sn *HeaderSegment, buf []byte) (*types.Header, []byte, error) {
|
|
if sn.idxHeaderHash == nil {
|
|
return nil, buf, nil
|
|
}
|
|
headerOffset := sn.idxHeaderHash.OrdinalLookup(blockHeight - sn.idxHeaderHash.BaseDataID())
|
|
gg := sn.seg.MakeGetter()
|
|
gg.Reset(headerOffset)
|
|
if !gg.HasNext() {
|
|
return nil, buf, nil
|
|
}
|
|
buf, _ = gg.Next(buf[:0])
|
|
if len(buf) == 0 {
|
|
return nil, buf, nil
|
|
}
|
|
h := &types.Header{}
|
|
if err := rlp.DecodeBytes(buf[1:], h); err != nil {
|
|
return nil, buf, err
|
|
}
|
|
return h, buf, nil
|
|
}
|
|
|
|
// headerFromSnapshotByHash - getting header by hash AND ensure that it has correct hash
|
|
// because HeaderByHash method will search header in all snapshots - and may request header which doesn't exists
|
|
// but because our indices are based on PerfectHashMap, no way to know is given key exists or not, only way -
|
|
// to make sure is to fetch it and compare hash
|
|
func (back *BlockReaderWithSnapshots) headerFromSnapshotByHash(hash libcommon.Hash, sn *HeaderSegment, buf []byte) (*types.Header, error) {
|
|
defer func() {
|
|
if rec := recover(); rec != nil {
|
|
panic(fmt.Errorf("%+v, snapshot: %d-%d, trace: %s", rec, sn.ranges.from, sn.ranges.to, dbg.Stack()))
|
|
}
|
|
}() // avoid crash because Erigon's core does many things
|
|
|
|
if sn.idxHeaderHash == nil {
|
|
return nil, nil
|
|
}
|
|
reader := recsplit.NewIndexReader(sn.idxHeaderHash)
|
|
localID := reader.Lookup(hash[:])
|
|
headerOffset := sn.idxHeaderHash.OrdinalLookup(localID)
|
|
gg := sn.seg.MakeGetter()
|
|
gg.Reset(headerOffset)
|
|
if !gg.HasNext() {
|
|
return nil, nil
|
|
}
|
|
buf, _ = gg.Next(buf[:0])
|
|
if len(buf) > 1 && hash[0] != buf[0] {
|
|
return nil, nil
|
|
}
|
|
|
|
h := &types.Header{}
|
|
if err := rlp.DecodeBytes(buf[1:], h); err != nil {
|
|
return nil, err
|
|
}
|
|
if h.Hash() != hash {
|
|
return nil, nil
|
|
}
|
|
return h, nil
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) bodyFromSnapshot(blockHeight uint64, sn *BodySegment, buf []byte) (*types.Body, uint64, uint32, []byte, error) {
|
|
b, buf, err := back.bodyForStorageFromSnapshot(blockHeight, sn, buf)
|
|
if err != nil {
|
|
return nil, 0, 0, buf, err
|
|
}
|
|
|
|
body := new(types.Body)
|
|
body.Uncles = b.Uncles
|
|
var txsAmount uint32
|
|
if b.TxAmount >= 2 {
|
|
txsAmount = b.TxAmount - 2
|
|
}
|
|
return body, b.BaseTxId + 1, txsAmount, buf, nil // empty txs in the beginning and end of block
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) bodyForStorageFromSnapshot(blockHeight uint64, sn *BodySegment, buf []byte) (*types.BodyForStorage, []byte, error) {
|
|
defer func() {
|
|
if rec := recover(); rec != nil {
|
|
panic(fmt.Errorf("%+v, snapshot: %d-%d, trace: %s", rec, sn.ranges.from, sn.ranges.to, dbg.Stack()))
|
|
}
|
|
}() // avoid crash because Erigon's core does many things
|
|
|
|
if sn.idxBodyNumber == nil {
|
|
return nil, buf, nil
|
|
}
|
|
bodyOffset := sn.idxBodyNumber.OrdinalLookup(blockHeight - sn.idxBodyNumber.BaseDataID())
|
|
|
|
gg := sn.seg.MakeGetter()
|
|
gg.Reset(bodyOffset)
|
|
if !gg.HasNext() {
|
|
return nil, buf, nil
|
|
}
|
|
buf, _ = gg.Next(buf[:0])
|
|
if len(buf) == 0 {
|
|
return nil, buf, nil
|
|
}
|
|
b := &types.BodyForStorage{}
|
|
reader := bytes.NewReader(buf)
|
|
if err := rlp.Decode(reader, b); err != nil {
|
|
return nil, buf, err
|
|
}
|
|
|
|
if b.BaseTxId < sn.idxBodyNumber.BaseDataID() {
|
|
return nil, buf, fmt.Errorf(".idx file has wrong baseDataID? %d<%d, %s", b.BaseTxId, sn.idxBodyNumber.BaseDataID(), sn.seg.FilePath())
|
|
}
|
|
return b, buf, nil
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) txsFromSnapshot(baseTxnID uint64, txsAmount uint32, txsSeg *TxnSegment, buf []byte) (txs []types.Transaction, senders []libcommon.Address, err error) {
|
|
defer func() {
|
|
if rec := recover(); rec != nil {
|
|
panic(fmt.Errorf("%+v, snapshot: %d-%d, trace: %s", rec, txsSeg.ranges.from, txsSeg.ranges.to, dbg.Stack()))
|
|
}
|
|
}() // avoid crash because Erigon's core does many things
|
|
|
|
if txsSeg.IdxTxnHash == nil {
|
|
return nil, nil, nil
|
|
}
|
|
if baseTxnID < txsSeg.IdxTxnHash.BaseDataID() {
|
|
return nil, nil, fmt.Errorf(".idx file has wrong baseDataID? %d<%d, %s", baseTxnID, txsSeg.IdxTxnHash.BaseDataID(), txsSeg.Seg.FilePath())
|
|
}
|
|
|
|
txs = make([]types.Transaction, txsAmount)
|
|
senders = make([]libcommon.Address, txsAmount)
|
|
if txsAmount == 0 {
|
|
return txs, senders, nil
|
|
}
|
|
txnOffset := txsSeg.IdxTxnHash.OrdinalLookup(baseTxnID - txsSeg.IdxTxnHash.BaseDataID())
|
|
gg := txsSeg.Seg.MakeGetter()
|
|
gg.Reset(txnOffset)
|
|
reader := bytes.NewReader(buf)
|
|
stream := rlp.NewStream(reader, 0)
|
|
for i := uint32(0); i < txsAmount; i++ {
|
|
if !gg.HasNext() {
|
|
return nil, nil, nil
|
|
}
|
|
buf, _ = gg.Next(buf[:0])
|
|
if len(buf) < 1+20 {
|
|
return nil, nil, fmt.Errorf("segment %s has too short record: len(buf)=%d < 21", txsSeg.Seg.FilePath(), len(buf))
|
|
}
|
|
senders[i].SetBytes(buf[1 : 1+20])
|
|
txRlp := buf[1+20:]
|
|
reader.Reset(txRlp)
|
|
stream.Reset(reader, 0)
|
|
txs[i], err = types.DecodeTransaction(stream)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
txs[i].SetSender(senders[i])
|
|
}
|
|
|
|
return txs, senders, nil
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) txnByID(txnID uint64, sn *TxnSegment, buf []byte) (txn types.Transaction, err error) {
|
|
offset := sn.IdxTxnHash.OrdinalLookup(txnID - sn.IdxTxnHash.BaseDataID())
|
|
gg := sn.Seg.MakeGetter()
|
|
gg.Reset(offset)
|
|
if !gg.HasNext() {
|
|
return nil, nil
|
|
}
|
|
buf, _ = gg.Next(buf[:0])
|
|
sender, txnRlp := buf[1:1+20], buf[1+20:]
|
|
|
|
txn, err = types.DecodeTransaction(rlp.NewStream(bytes.NewReader(txnRlp), uint64(len(txnRlp))))
|
|
if err != nil {
|
|
return
|
|
}
|
|
txn.SetSender(*(*libcommon.Address)(sender)) // see: https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer
|
|
return
|
|
}
|
|
|
|
func (back *BlockReaderWithSnapshots) txnByHash(txnHash libcommon.Hash, segments []*TxnSegment, buf []byte) (txn types.Transaction, blockNum, txnID uint64, err error) {
|
|
for i := len(segments) - 1; i >= 0; i-- {
|
|
sn := segments[i]
|
|
if sn.IdxTxnHash == nil || sn.IdxTxnHash2BlockNum == nil {
|
|
continue
|
|
}
|
|
|
|
reader := recsplit.NewIndexReader(sn.IdxTxnHash)
|
|
txnId := reader.Lookup(txnHash[:])
|
|
offset := sn.IdxTxnHash.OrdinalLookup(txnId)
|
|
gg := sn.Seg.MakeGetter()
|
|
gg.Reset(offset)
|
|
// first byte txnHash check - reducing false-positives 256 times. Allows don't store and don't calculate full hash of entity - when checking many snapshots.
|
|
if !gg.MatchPrefix([]byte{txnHash[0]}) {
|
|
continue
|
|
}
|
|
buf, _ = gg.Next(buf[:0])
|
|
senderByte, txnRlp := buf[1:1+20], buf[1+20:]
|
|
sender := *(*libcommon.Address)(senderByte)
|
|
|
|
txn, err = types.DecodeTransaction(rlp.NewStream(bytes.NewReader(txnRlp), uint64(len(txnRlp))))
|
|
if err != nil {
|
|
return
|
|
}
|
|
|
|
txn.SetSender(sender) // see: https://tip.golang.org/ref/spec#Conversions_from_slice_to_array_pointer
|
|
|
|
reader2 := recsplit.NewIndexReader(sn.IdxTxnHash2BlockNum)
|
|
blockNum = reader2.Lookup(txnHash[:])
|
|
|
|
// final txnHash check - completely avoid false-positives
|
|
if txn.Hash() == txnHash {
|
|
return
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// TxnByIdxInBlock - doesn't include system-transactions in the begin/end of block
|
|
// return nil if 0 < i < body.TxAmount
|
|
func (back *BlockReaderWithSnapshots) TxnByIdxInBlock(ctx context.Context, tx kv.Getter, blockNum uint64, i int) (txn types.Transaction, err error) {
|
|
var b *types.BodyForStorage
|
|
ok, err := back.sn.ViewBodies(blockNum, func(segment *BodySegment) error {
|
|
b, _, err = back.bodyForStorageFromSnapshot(blockNum, segment, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if b == nil {
|
|
return nil
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if ok {
|
|
// if block has no transactions, or requested txNum out of non-system transactions length
|
|
if b.TxAmount == 2 || i == -1 || i >= int(b.TxAmount-2) {
|
|
return nil, nil
|
|
}
|
|
|
|
ok, err = back.sn.Txs.ViewSegment(blockNum, func(segment *TxnSegment) error {
|
|
// +1 because block has system-txn in the beginning of block
|
|
txn, err = back.txnByID(b.BaseTxId+1+uint64(i), segment, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if txn == nil {
|
|
return nil
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if ok {
|
|
return txn, nil
|
|
}
|
|
return nil, nil
|
|
}
|
|
|
|
canonicalHash, err := rawdb.ReadCanonicalHash(tx, blockNum)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var k [8 + 32]byte
|
|
binary.BigEndian.PutUint64(k[:], blockNum)
|
|
copy(k[8:], canonicalHash[:])
|
|
b, err = rawdb.ReadBodyForStorageByKey(tx, k[:])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if b == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
txn, err = rawdb.CanonicalTxnByID(tx, b.BaseTxId+1+uint64(i))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return txn, nil
|
|
}
|
|
|
|
// TxnLookup - find blockNumber and txnID by txnHash
|
|
func (back *BlockReaderWithSnapshots) TxnLookup(ctx context.Context, tx kv.Getter, txnHash libcommon.Hash) (uint64, bool, error) {
|
|
n, err := rawdb.ReadTxLookupEntry(tx, txnHash)
|
|
if err != nil {
|
|
return 0, false, err
|
|
}
|
|
if n != nil {
|
|
return *n, true, nil
|
|
}
|
|
|
|
var txn types.Transaction
|
|
var blockNum uint64
|
|
if err := back.sn.Txs.View(func(segments []*TxnSegment) error {
|
|
txn, blockNum, _, err = back.txnByHash(txnHash, segments, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if txn == nil {
|
|
return nil
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
return 0, false, err
|
|
}
|
|
if txn == nil {
|
|
return 0, false, nil
|
|
}
|
|
return blockNum, true, nil
|
|
}
|