mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
ethclient/lightclient: add transaction and receipt functions
This commit is contained in:
parent
fe7568de29
commit
ef80de057a
5 changed files with 270 additions and 23 deletions
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/beacon/config"
|
"github.com/ethereum/go-ethereum/beacon/config"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethclient/lightclient"
|
"github.com/ethereum/go-ethereum/ethclient/lightclient"
|
||||||
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||||
|
|
@ -110,7 +111,11 @@ func sync(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Fatalf("Could not create RPC client: %v", err)
|
utils.Fatalf("Could not create RPC client: %v", err)
|
||||||
}
|
}
|
||||||
client := lightclient.NewClient(config.MakeLightClientConfig(ctx), memorydb.New(), rpcClient)
|
genesis := utils.MakeGenesis(ctx)
|
||||||
|
if genesis == nil {
|
||||||
|
genesis = core.DefaultGenesisBlock()
|
||||||
|
}
|
||||||
|
client := lightclient.NewClient(config.MakeLightClientConfig(ctx), genesis.Config, memorydb.New(), rpcClient)
|
||||||
client.Start()
|
client.Start()
|
||||||
|
|
||||||
headCh := make(chan *types.Header, 1)
|
headCh := make(chan *types.Header, 1)
|
||||||
|
|
@ -140,6 +145,11 @@ loop:
|
||||||
} else {
|
} else {
|
||||||
log.Error("TransactionCount", "hash", head.Hash(), "error", err)
|
log.Error("TransactionCount", "hash", head.Hash(), "error", err)
|
||||||
}
|
}
|
||||||
|
if receipts, err := client.BlockReceipts(ctx, rpc.BlockNumberOrHashWithHash(head.Hash(), false)); err == nil {
|
||||||
|
log.Info("BlockReceipts", "hash", head.Hash(), "len(receipts)", len(receipts))
|
||||||
|
} else {
|
||||||
|
log.Error("BlockReceipts", "hash", head.Hash(), "error", err)
|
||||||
|
}
|
||||||
testState := func(addr common.Address) {
|
testState := func(addr common.Address) {
|
||||||
if balance, err := client.BalanceAt(ctx, addr, big.NewInt(int64(rpc.LatestBlockNumber))); err == nil {
|
if balance, err := client.BalanceAt(ctx, addr, big.NewInt(int64(rpc.LatestBlockNumber))); err == nil {
|
||||||
log.Info("BalanceAt ", "address", addr, "balance", balance)
|
log.Info("BalanceAt ", "address", addr, "balance", balance)
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/accounts/scwallet"
|
"github.com/ethereum/go-ethereum/accounts/scwallet"
|
||||||
"github.com/ethereum/go-ethereum/accounts/usbwallet"
|
"github.com/ethereum/go-ethereum/accounts/usbwallet"
|
||||||
"github.com/ethereum/go-ethereum/beacon/blsync"
|
"github.com/ethereum/go-ethereum/beacon/blsync"
|
||||||
|
"github.com/ethereum/go-ethereum/beacon/config"
|
||||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
|
@ -227,7 +228,7 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||||
// Start blsync mode.
|
// Start blsync mode.
|
||||||
srv := rpc.NewServer()
|
srv := rpc.NewServer()
|
||||||
srv.RegisterName("engine", catalyst.NewConsensusAPI(eth))
|
srv.RegisterName("engine", catalyst.NewConsensusAPI(eth))
|
||||||
blsyncer := blsync.NewClient(ctx)
|
blsyncer := blsync.NewClient(config.MakeLightClientConfig(ctx))
|
||||||
blsyncer.SetEngineRPC(rpc.DialInProc(srv))
|
blsyncer.SetEngineRPC(rpc.DialInProc(srv))
|
||||||
stack.RegisterLifecycle(blsyncer)
|
stack.RegisterLifecycle(blsyncer)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ type canonicalChain struct {
|
||||||
recentTail uint64 // if recent != nil then recent hashes are available from recentTail to head
|
recentTail uint64 // if recent != nil then recent hashes are available from recentTail to head
|
||||||
tailFetchCh chan struct{}
|
tailFetchCh chan struct{}
|
||||||
finalized *lru.Cache[uint64, common.Hash] // finalized but not recent hashes
|
finalized *lru.Cache[uint64, common.Hash] // finalized but not recent hashes
|
||||||
requests *requestMap[uint64, common.Hash] // requested; neither recent nor finalized
|
requests *requestMap[uint64, common.Hash] // requested; neither recent nor cached finalized
|
||||||
}
|
}
|
||||||
|
|
||||||
func newCanonicalChain(headTracker *light.HeadTracker, blocksAndHeaders *blocksAndHeaders, newHeadCb func(common.Hash)) *canonicalChain {
|
func newCanonicalChain(headTracker *light.HeadTracker, blocksAndHeaders *blocksAndHeaders, newHeadCb func(common.Hash)) *canonicalChain {
|
||||||
|
|
@ -111,17 +111,10 @@ func (c *canonicalChain) tailFetcher() { //TODO stop
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *canonicalChain) getHash(ctx context.Context, number uint64) (common.Hash, error) {
|
func (c *canonicalChain) getHash(ctx context.Context, number uint64) (common.Hash, error) {
|
||||||
c.lock.Lock()
|
if hash, ok := c.getCachedHash(number); ok {
|
||||||
if hash, ok := c.recent[number]; ok {
|
|
||||||
c.lock.Unlock()
|
|
||||||
return hash, nil
|
|
||||||
}
|
|
||||||
if hash, ok := c.finalized.Get(number); ok {
|
|
||||||
c.lock.Unlock()
|
|
||||||
return hash, nil
|
return hash, nil
|
||||||
}
|
}
|
||||||
req := c.requests.request(number)
|
req := c.requests.request(number)
|
||||||
c.lock.Unlock()
|
|
||||||
select {
|
select {
|
||||||
case c.tailFetchCh <- struct{}{}:
|
case c.tailFetchCh <- struct{}{}:
|
||||||
default:
|
default:
|
||||||
|
|
@ -130,6 +123,16 @@ func (c *canonicalChain) getHash(ctx context.Context, number uint64) (common.Has
|
||||||
return req.getResult(ctx)
|
return req.getResult(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *canonicalChain) getCachedHash(number uint64) (common.Hash, bool) {
|
||||||
|
c.lock.Lock()
|
||||||
|
hash, ok := c.recent[number]
|
||||||
|
c.lock.Unlock()
|
||||||
|
if ok {
|
||||||
|
return hash, true
|
||||||
|
}
|
||||||
|
return c.finalized.Get(number)
|
||||||
|
}
|
||||||
|
|
||||||
func (c *canonicalChain) setHead(head *btypes.ExecutionHeader) bool {
|
func (c *canonicalChain) setHead(head *btypes.ExecutionHeader) bool {
|
||||||
c.lock.Lock()
|
c.lock.Lock()
|
||||||
defer c.lock.Unlock()
|
defer c.lock.Unlock()
|
||||||
|
|
@ -139,6 +142,8 @@ func (c *canonicalChain) setHead(head *btypes.ExecutionHeader) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if c.recent == nil || c.head == nil || c.head.BlockNumber()+1 != headNum || c.head.BlockHash() != head.ParentHash() {
|
if c.recent == nil || c.head == nil || c.head.BlockNumber()+1 != headNum || c.head.BlockHash() != head.ParentHash() {
|
||||||
|
// initialize recent canonical hash map when first head is added or when
|
||||||
|
// it is not a descendant of the previous head
|
||||||
c.recent = make(map[uint64]common.Hash)
|
c.recent = make(map[uint64]common.Hash)
|
||||||
if headNum > 0 {
|
if headNum > 0 {
|
||||||
c.recent[headNum-1] = head.ParentHash()
|
c.recent[headNum-1] = head.ParentHash()
|
||||||
|
|
@ -237,6 +242,27 @@ func (c *canonicalChain) blockNumberToHash(ctx context.Context, number *big.Int)
|
||||||
return c.getHash(ctx, num)
|
return c.getHash(ctx, num)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *canonicalChain) blockNumberOrHashToHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (common.Hash, error) {
|
||||||
|
if blockNrOrHash.BlockNumber != nil {
|
||||||
|
return c.blockNumberToHash(ctx, big.NewInt(int64(*blockNrOrHash.BlockNumber)))
|
||||||
|
}
|
||||||
|
hash := *blockNrOrHash.BlockHash
|
||||||
|
if blockNrOrHash.RequireCanonical {
|
||||||
|
header, err := c.blocksAndHeaders.getHeader(ctx, hash)
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
chash, err := c.getHash(ctx, header.Number.Uint64())
|
||||||
|
if err != nil {
|
||||||
|
return common.Hash{}, err
|
||||||
|
}
|
||||||
|
if chash != hash {
|
||||||
|
return common.Hash{}, errors.New("hash is not currently canonical")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hash, nil
|
||||||
|
}
|
||||||
|
|
||||||
type blocksAndHeaders struct {
|
type blocksAndHeaders struct {
|
||||||
client *rpc.Client
|
client *rpc.Client
|
||||||
headerCache *lru.Cache[common.Hash, *types.Header]
|
headerCache *lru.Cache[common.Hash, *types.Header]
|
||||||
|
|
|
||||||
|
|
@ -33,14 +33,17 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
config config.LightClientConfig
|
clConfig config.LightClientConfig
|
||||||
|
elConfig *params.ChainConfig
|
||||||
scheduler *request.Scheduler
|
scheduler *request.Scheduler
|
||||||
canonicalChain *canonicalChain
|
canonicalChain *canonicalChain
|
||||||
blocksAndHeaders *blocksAndHeaders
|
blocksAndHeaders *blocksAndHeaders
|
||||||
|
txAndReceipts *txAndReceipts
|
||||||
state *lightState
|
state *lightState
|
||||||
headSubLock ssync.Mutex
|
headSubLock ssync.Mutex
|
||||||
headSubs map[*headSub]struct{}
|
headSubs map[*headSub]struct{}
|
||||||
|
|
@ -48,27 +51,30 @@ type Client struct {
|
||||||
headFetchCounter int
|
headFetchCounter int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(config config.LightClientConfig, db ethdb.KeyValueStore, rpcClient *rpc.Client) *Client {
|
func NewClient(clConfig config.LightClientConfig, elConfig *params.ChainConfig, db ethdb.KeyValueStore, rpcClient *rpc.Client) *Client {
|
||||||
// create data structures
|
// create data structures
|
||||||
var (
|
var (
|
||||||
committeeChain = light.NewCommitteeChain(db, config.ChainConfig, config.SignerThreshold, config.EnforceTime)
|
committeeChain = light.NewCommitteeChain(db, clConfig.ChainConfig, clConfig.SignerThreshold, clConfig.EnforceTime)
|
||||||
headTracker = light.NewHeadTracker(committeeChain, config.SignerThreshold)
|
headTracker = light.NewHeadTracker(committeeChain, clConfig.SignerThreshold)
|
||||||
)
|
)
|
||||||
// set up scheduler and sync modules
|
// set up scheduler and sync modules
|
||||||
//chainHeadFeed := new(event.Feed)
|
//chainHeadFeed := new(event.Feed)
|
||||||
scheduler := request.NewScheduler()
|
scheduler := request.NewScheduler()
|
||||||
blocksAndHeaders := newBlocksAndHeaders(rpcClient)
|
|
||||||
client := &Client{
|
client := &Client{
|
||||||
config: config,
|
clConfig: clConfig,
|
||||||
scheduler: scheduler,
|
elConfig: elConfig,
|
||||||
blocksAndHeaders: blocksAndHeaders,
|
scheduler: scheduler,
|
||||||
headSubs: make(map[*headSub]struct{}),
|
headSubs: make(map[*headSub]struct{}),
|
||||||
}
|
}
|
||||||
|
blocksAndHeaders := newBlocksAndHeaders(rpcClient)
|
||||||
canonicalChain := newCanonicalChain(headTracker, blocksAndHeaders, client.newHead)
|
canonicalChain := newCanonicalChain(headTracker, blocksAndHeaders, client.newHead)
|
||||||
|
txAndReceipts := newTxAndReceipts(rpcClient, canonicalChain, blocksAndHeaders, elConfig)
|
||||||
|
client.blocksAndHeaders = blocksAndHeaders
|
||||||
|
client.txAndReceipts = txAndReceipts
|
||||||
client.canonicalChain = canonicalChain
|
client.canonicalChain = canonicalChain
|
||||||
client.state = newLightState(rpcClient, canonicalChain, blocksAndHeaders)
|
client.state = newLightState(rpcClient, canonicalChain, blocksAndHeaders)
|
||||||
|
|
||||||
checkpointInit := sync.NewCheckpointInit(committeeChain, config.Checkpoint)
|
checkpointInit := sync.NewCheckpointInit(committeeChain, clConfig.Checkpoint)
|
||||||
forwardSync := sync.NewForwardUpdateSync(committeeChain)
|
forwardSync := sync.NewForwardUpdateSync(committeeChain)
|
||||||
headSync := sync.NewHeadSync(headTracker, committeeChain)
|
headSync := sync.NewHeadSync(headTracker, committeeChain)
|
||||||
scheduler.RegisterTarget(headTracker)
|
scheduler.RegisterTarget(headTracker)
|
||||||
|
|
@ -80,10 +86,12 @@ func NewClient(config config.LightClientConfig, db ethdb.KeyValueStore, rpcClien
|
||||||
return client
|
return client
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO return ethereum.NotFound error properly
|
||||||
|
|
||||||
func (c *Client) Start() {
|
func (c *Client) Start() {
|
||||||
c.scheduler.Start()
|
c.scheduler.Start()
|
||||||
for _, url := range c.config.ApiUrls {
|
for _, url := range c.clConfig.ApiUrls {
|
||||||
beaconApi := api.NewBeaconLightApi(url, c.config.CustomHeader)
|
beaconApi := api.NewBeaconLightApi(url, c.clConfig.CustomHeader)
|
||||||
c.scheduler.RegisterServer(request.NewServer(api.NewApiServer(beaconApi), &mclock.System{}))
|
c.scheduler.RegisterServer(request.NewServer(api.NewApiServer(beaconApi), &mclock.System{}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -224,3 +232,11 @@ func (c *Client) NonceAt(ctx context.Context, account common.Address, blockNumbe
|
||||||
}
|
}
|
||||||
return proof.Nonce, nil
|
return proof.Nonce, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) BlockReceipts(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) ([]*types.Receipt, error) {
|
||||||
|
hash, err := c.canonicalChain.blockNumberOrHashToHash(ctx, blockNrOrHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return c.txAndReceipts.getBlockReceipts(ctx, hash)
|
||||||
|
}
|
||||||
|
|
|
||||||
194
ethclient/lightclient/transactions.go
Normal file
194
ethclient/lightclient/transactions.go
Normal file
|
|
@ -0,0 +1,194 @@
|
||||||
|
// Copyright 2024 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 lightclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum"
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/misc/eip4844"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
|
)
|
||||||
|
|
||||||
|
type txAndReceipts struct {
|
||||||
|
client *rpc.Client
|
||||||
|
canonicalChain *canonicalChain
|
||||||
|
blocksAndHeaders *blocksAndHeaders
|
||||||
|
elConfig *params.ChainConfig
|
||||||
|
receiptsCache *lru.Cache[common.Hash, types.Receipts]
|
||||||
|
receiptsRequests *requestMap[common.Hash, types.Receipts]
|
||||||
|
txPosCache *lru.Cache[common.Hash, txInBlock]
|
||||||
|
}
|
||||||
|
|
||||||
|
type txInBlock struct {
|
||||||
|
blockNumber uint64
|
||||||
|
blockHash common.Hash // only considered valid if the block is canonical
|
||||||
|
index uint
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTxAndReceipts(client *rpc.Client, canonicalChain *canonicalChain, blocksAndHeaders *blocksAndHeaders, elConfig *params.ChainConfig) *txAndReceipts {
|
||||||
|
t := &txAndReceipts{
|
||||||
|
client: client,
|
||||||
|
canonicalChain: canonicalChain,
|
||||||
|
blocksAndHeaders: blocksAndHeaders,
|
||||||
|
elConfig: elConfig,
|
||||||
|
receiptsCache: lru.NewCache[common.Hash, types.Receipts](10),
|
||||||
|
txPosCache: lru.NewCache[common.Hash, txInBlock](10000),
|
||||||
|
}
|
||||||
|
t.receiptsRequests = newRequestMap[common.Hash, types.Receipts](t.requestBlockReceipts)
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *txAndReceipts) getTxByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) {
|
||||||
|
if pos, ok := t.txPosCache.Get(txHash); ok {
|
||||||
|
if hash, ok := t.canonicalChain.getCachedHash(pos.blockNumber); ok && hash == pos.blockHash {
|
||||||
|
if block, ok := t.blocksAndHeaders.blockCache.Get(pos.blockHash); ok {
|
||||||
|
return block.Transactions()[pos.index], false, nil //TODO index range check
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t.requestTxByHash(ctx, txHash)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *txAndReceipts) getReceiptByTxHash(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||||
|
if pos, ok := t.txPosCache.Get(txHash); ok {
|
||||||
|
if hash, ok := t.canonicalChain.getCachedHash(pos.blockNumber); ok && hash == pos.blockHash {
|
||||||
|
if receipts, ok := t.receiptsCache.Get(pos.blockHash); ok {
|
||||||
|
return receipts[pos.index], nil //TODO index range check
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t.requestReceiptByTxHash(ctx, txHash)
|
||||||
|
//TODO validate position
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *txAndReceipts) getBlockReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
|
||||||
|
if receipts, ok := t.receiptsCache.Get(blockHash); ok {
|
||||||
|
return receipts, nil
|
||||||
|
}
|
||||||
|
request := t.receiptsRequests.request(blockHash)
|
||||||
|
block, err := t.blocksAndHeaders.getBlock(ctx, blockHash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
receipts, err := request.getResult(ctx)
|
||||||
|
if err == nil {
|
||||||
|
receipts, err = t.validateBlockReceipts(block, receipts)
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
t.receiptsCache.Add(blockHash, receipts)
|
||||||
|
}
|
||||||
|
request.release()
|
||||||
|
return receipts, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *txAndReceipts) validateBlockReceipts(block *types.Block, receipts types.Receipts) (types.Receipts, error) {
|
||||||
|
// verify consensus fields agains receipts hash in block
|
||||||
|
var hash common.Hash
|
||||||
|
if len(receipts) == 0 {
|
||||||
|
hash = types.EmptyReceiptsHash
|
||||||
|
} else {
|
||||||
|
hash = types.DeriveSha(receipts, trie.NewStackTrie(nil))
|
||||||
|
}
|
||||||
|
if hash != block.ReceiptHash() {
|
||||||
|
return nil, errors.New("invalid receipts hash")
|
||||||
|
}
|
||||||
|
// copy verified consensus fields
|
||||||
|
newReceipts := make(types.Receipts, len(receipts))
|
||||||
|
for i, receipt := range receipts {
|
||||||
|
enc, err := receipt.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newReceipt := &types.Receipt{}
|
||||||
|
if err := newReceipt.UnmarshalBinary(enc); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
newReceipts[i] = newReceipt
|
||||||
|
}
|
||||||
|
// derive non-consensus fields again
|
||||||
|
var blobGasPrice *big.Int
|
||||||
|
if block.ExcessBlobGas() != nil {
|
||||||
|
blobGasPrice = eip4844.CalcBlobFee(*block.ExcessBlobGas())
|
||||||
|
}
|
||||||
|
if err := newReceipts.DeriveFields(t.elConfig, block.Hash(), block.NumberU64(), block.Time(), block.BaseFee(), blobGasPrice, block.Transactions()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// compare the JSON encoding of received and derived versions
|
||||||
|
jsonReceived, err := json.Marshal(receipts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
jsonDerived, err := json.Marshal(receipts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if !bytes.Equal(jsonReceived, jsonDerived) {
|
||||||
|
return nil, errors.New("received and derived receipts do not match")
|
||||||
|
}
|
||||||
|
return newReceipts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *txAndReceipts) requestTxByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
|
||||||
|
var json *rpcTransaction
|
||||||
|
err = t.client.CallContext(ctx, &json, "eth_getTransactionByHash", hash)
|
||||||
|
if err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
} else if json == nil {
|
||||||
|
return nil, false, ethereum.NotFound
|
||||||
|
} else if _, r, _ := json.tx.RawSignatureValues(); r == nil {
|
||||||
|
return nil, false, errors.New("server returned transaction without signature")
|
||||||
|
}
|
||||||
|
if json.From != nil && json.BlockHash != nil {
|
||||||
|
setSenderFromServer(json.tx, *json.From, *json.BlockHash)
|
||||||
|
}
|
||||||
|
return json.tx, json.BlockNumber == nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *txAndReceipts) requestReceiptByTxHash(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||||
|
var r *types.Receipt
|
||||||
|
err := t.client.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash)
|
||||||
|
if err == nil && r == nil {
|
||||||
|
return nil, ethereum.NotFound
|
||||||
|
}
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *txAndReceipts) requestBlockReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
|
||||||
|
var r []*types.Receipt
|
||||||
|
err := t.client.CallContext(ctx, &r, "eth_getBlockReceipts", blockHash)
|
||||||
|
if err == nil && r == nil {
|
||||||
|
return nil, ethereum.NotFound
|
||||||
|
}
|
||||||
|
return types.Receipts(r), err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *txAndReceipts) addBlockTxs(block *types.Block) {
|
||||||
|
blockNumber, blockHash := block.NumberU64(), block.Hash()
|
||||||
|
for i, tx := range block.Transactions() {
|
||||||
|
t.txPosCache.Add(tx.Hash(), txInBlock{blockNumber: blockNumber, blockHash: blockHash, index: uint(i)})
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue