mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 18:32:23 +00:00
ethclient/lightclient: TransactionReader implemented
This commit is contained in:
parent
ef80de057a
commit
b31680fb19
3 changed files with 80 additions and 4 deletions
|
|
@ -265,6 +265,7 @@ func (c *canonicalChain) blockNumberOrHashToHash(ctx context.Context, blockNrOrH
|
|||
|
||||
type blocksAndHeaders struct {
|
||||
client *rpc.Client
|
||||
txAndReceipts *txAndReceipts
|
||||
headerCache *lru.Cache[common.Hash, *types.Header]
|
||||
headerRequests *requestMap[common.Hash, *types.Header]
|
||||
payloadHeaderCache *lru.Cache[common.Hash, *btypes.ExecutionHeader]
|
||||
|
|
@ -358,6 +359,7 @@ func (b *blocksAndHeaders) getBlock(ctx context.Context, hash common.Hash) (*typ
|
|||
b.headerCache.Add(hash, header)
|
||||
b.headerRequests.tryDeliver(hash, header)
|
||||
b.blockCache.Add(hash, block)
|
||||
b.txAndReceipts.cacheBlockTxPositions(block)
|
||||
}
|
||||
req.release()
|
||||
return block, err
|
||||
|
|
|
|||
|
|
@ -69,6 +69,7 @@ func NewClient(clConfig config.LightClientConfig, elConfig *params.ChainConfig,
|
|||
blocksAndHeaders := newBlocksAndHeaders(rpcClient)
|
||||
canonicalChain := newCanonicalChain(headTracker, blocksAndHeaders, client.newHead)
|
||||
txAndReceipts := newTxAndReceipts(rpcClient, canonicalChain, blocksAndHeaders, elConfig)
|
||||
blocksAndHeaders.txAndReceipts = txAndReceipts
|
||||
client.blocksAndHeaders = blocksAndHeaders
|
||||
client.txAndReceipts = txAndReceipts
|
||||
client.canonicalChain = canonicalChain
|
||||
|
|
@ -100,6 +101,8 @@ func (c *Client) Stop() {
|
|||
c.scheduler.Stop()
|
||||
}
|
||||
|
||||
// ChainReader interface
|
||||
|
||||
func (c *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
|
||||
return c.blocksAndHeaders.getBlock(ctx, hash)
|
||||
}
|
||||
|
|
@ -204,6 +207,18 @@ func (h *headSub) Err() <-chan error {
|
|||
return h.errCh
|
||||
}
|
||||
|
||||
// TransactionReader interface
|
||||
|
||||
func (c *Client) TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) {
|
||||
return c.txAndReceipts.getTxByHash(ctx, txHash)
|
||||
}
|
||||
|
||||
func (c *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||
return c.txAndReceipts.getReceiptByTxHash(ctx, txHash)
|
||||
}
|
||||
|
||||
// ChainStateReader interface {
|
||||
|
||||
func (c *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
proof, _, err := c.state.getProof(ctx, blockNumber, account, nil, false)
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -70,7 +70,22 @@ func (t *txAndReceipts) getTxByHash(ctx context.Context, txHash common.Hash) (tx
|
|||
}
|
||||
}
|
||||
}
|
||||
return t.requestTxByHash(ctx, txHash)
|
||||
tx, isPending, err = t.requestTxByHash(ctx, txHash)
|
||||
if err == nil && !isPending {
|
||||
receipt, err := t.requestReceiptByTxHash(ctx, txHash)
|
||||
if err == ethereum.NotFound {
|
||||
return tx, false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
if head := t.canonicalChain.getHead(); head == nil ||
|
||||
!receipt.BlockNumber.IsUint64() || head.BlockNumber() < receipt.BlockNumber.Uint64() {
|
||||
// consider it pending if it's reported to be included higher than the light chain head
|
||||
isPending = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *txAndReceipts) getReceiptByTxHash(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||
|
|
@ -81,8 +96,52 @@ func (t *txAndReceipts) getReceiptByTxHash(ctx context.Context, txHash common.Ha
|
|||
}
|
||||
}
|
||||
}
|
||||
return t.requestReceiptByTxHash(ctx, txHash)
|
||||
//TODO validate position
|
||||
receipt, err := t.requestReceiptByTxHash(ctx, txHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// check if it indeed belongs to the requested transaction
|
||||
if receipt.TxHash != txHash {
|
||||
return nil, errors.New("receipt references another transaction")
|
||||
}
|
||||
// check if its inclusion position is canonical
|
||||
if !receipt.BlockNumber.IsUint64() {
|
||||
return nil, errors.New("receipt references non-canonical block")
|
||||
}
|
||||
blockNumber := receipt.BlockNumber.Uint64()
|
||||
if head := t.canonicalChain.getHead(); head == nil || head.BlockNumber() < blockNumber {
|
||||
// consider it pending if it's reported to be included higher than the light chain head
|
||||
return nil, ethereum.NotFound
|
||||
}
|
||||
canonicalHash, err := t.canonicalChain.getHash(ctx, blockNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if receipt.BlockHash != canonicalHash {
|
||||
return nil, errors.New("receipt references non-canonical block")
|
||||
}
|
||||
// check if it is the actual canonical receipt at the given position
|
||||
receipts, err := t.getBlockReceipts(ctx, receipt.BlockHash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if receipt.TransactionIndex >= uint(len(receipts)) {
|
||||
return nil, errors.New("receipt references out-of-range transaction index")
|
||||
}
|
||||
// compare the JSON encoding of received and canonical versions
|
||||
jsonReceived, err := json.Marshal(receipt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
jsonCanonical, err := json.Marshal(receipts[receipt.TransactionIndex])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !bytes.Equal(jsonReceived, jsonCanonical) {
|
||||
return nil, errors.New("received and derived receipts do not match")
|
||||
}
|
||||
t.txPosCache.Add(receipt.TxHash, txInBlock{blockNumber: blockNumber, blockHash: receipt.BlockHash, index: receipt.TransactionIndex})
|
||||
return receipt, err
|
||||
}
|
||||
|
||||
func (t *txAndReceipts) getBlockReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
|
||||
|
|
@ -186,7 +245,7 @@ func (t *txAndReceipts) requestBlockReceipts(ctx context.Context, blockHash comm
|
|||
return types.Receipts(r), err
|
||||
}
|
||||
|
||||
func (t *txAndReceipts) addBlockTxs(block *types.Block) {
|
||||
func (t *txAndReceipts) cacheBlockTxPositions(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