Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: eth get block receipts #6421

Merged
merged 3 commits into from
Oct 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/node/rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ func aliasETHAPI(rpcServer *jsonrpc.RPCServer) {
rpcServer.AliasMethod("eth_getMessageCidByTransactionHash", "Filecoin.EthGetMessageCidByTransactionHash")
rpcServer.AliasMethod("eth_getTransactionCount", "Filecoin.EthGetTransactionCount")
rpcServer.AliasMethod("eth_getTransactionReceipt", "Filecoin.EthGetTransactionReceipt")
rpcServer.AliasMethod("eth_getBlockReceipts", "Filecoin.EthGetBlockReceipts")
rpcServer.AliasMethod("eth_getTransactionByBlockHashAndIndex", "Filecoin.EthGetTransactionByBlockHashAndIndex")
rpcServer.AliasMethod("eth_getTransactionByBlockNumberAndIndex", "Filecoin.EthGetTransactionByBlockNumberAndIndex")

Expand Down
8 changes: 8 additions & 0 deletions app/submodule/eth/dummy.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ func (e *ethAPIDummy) EthGetTransactionByHash(ctx context.Context, txHash *types
return nil, ErrModuleDisabled
}

func (e *ethAPIDummy) EthGetBlockReceiptsLimited(ctx context.Context, blkParam types.EthBlockNumberOrHash, limit abi.ChainEpoch) ([]*types.EthTxReceipt, error) {
return nil, ErrModuleDisabled
}

func (e *ethAPIDummy) EthGetBlockReceipts(ctx context.Context, blkParam types.EthBlockNumberOrHash) ([]*types.EthTxReceipt, error) {
return nil, ErrModuleDisabled
}

func (e *ethAPIDummy) EthGetTransactionByHashLimited(ctx context.Context, txHash *types.EthHash, limit abi.ChainEpoch) (*types.EthTx, error) {
return nil, ErrModuleDisabled
}
Expand Down
111 changes: 90 additions & 21 deletions app/submodule/eth/eth_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import (
"github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2"
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors"
)

const maxEthFeeHistoryRewardPercentiles = 100
Expand All @@ -47,9 +46,10 @@ var ErrUnsupported = errors.New("unsupported method")

func newEthAPI(em *EthSubModule) (*ethAPI, error) {
a := &ethAPI{
em: em,
chain: em.chainModule.API(),
mpool: em.mpoolModule.API(),
em: em,
chain: em.chainModule.API(),
mpool: em.mpoolModule.API(),
EthEventHandler: em.ethEventAPI,
}

dbPath := filepath.Join(a.em.sqlitePath, "txhash.db")
Expand Down Expand Up @@ -95,10 +95,12 @@ func newEthAPI(em *EthSubModule) (*ethAPI, error) {
}

type ethAPI struct {
em *EthSubModule
chain v1.IChain
mpool v1.IMessagePool
ethTxHashManager *ethTxHashManager
em *EthSubModule
chain v1.IChain
mpool v1.IMessagePool
ethTxHashManager *ethTxHashManager
EthEventHandler *ethEventAPI
MaxFilterHeightRange abi.ChainEpoch

EthBlkCache *arc.ARCCache[cid.Cid, *types.EthBlock] // caches blocks by their CID but blocks only have the transaction hashes
EthBlkTxCache *arc.ARCCache[cid.Cid, *types.EthBlock] // caches blocks along with full transaction payload by their CID
Expand Down Expand Up @@ -279,12 +281,12 @@ func (a *ethAPI) EthGetBlockByNumber(ctx context.Context, blkParam string, fullT
// Return nil for null rounds
return nil, nil
}
return nil, xerrors.Errorf("failed to get tipset: %w", err)
return nil, fmt.Errorf("failed to get tipset: %w", err)
}
// Create an Ethereum block from the Filecoin tipset
block, err := newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.em.chainModule.MessageStore, a.em.chainModule.Stmgr)
if err != nil {
return nil, xerrors.Errorf("failed to create Ethereum block: %w", err)
return nil, fmt.Errorf("failed to create Ethereum block: %w", err)
}

return &block, nil
Expand Down Expand Up @@ -403,14 +405,14 @@ func (a *ethAPI) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*
func (a *ethAPI) EthGetTransactionCount(ctx context.Context, sender types.EthAddress, blkParam types.EthBlockNumberOrHash) (types.EthUint64, error) {
addr, err := sender.ToFilecoinAddress()
if err != nil {
return types.EthUint64(0), xerrors.Errorf("invalid address: %w", err)
return types.EthUint64(0), fmt.Errorf("invalid address: %w", err)

}
// Handle "pending" block parameter separately
if blkParam.PredefinedBlock != nil && *blkParam.PredefinedBlock == "pending" {
nonce, err := a.mpool.MpoolGetNonce(ctx, addr)
if err != nil {
return types.EthUint64(0), xerrors.Errorf("failed to get nonce from mpool: %w", err)
return types.EthUint64(0), fmt.Errorf("failed to get nonce from mpool: %w", err)
}
return types.EthUint64(nonce), nil
}
Expand All @@ -427,7 +429,7 @@ func (a *ethAPI) EthGetTransactionCount(ctx context.Context, sender types.EthAdd
if errors.Is(err, types.ErrActorNotFound) {
return 0, nil
}
return 0, xerrors.Errorf("failed to lookup actor %s: %w", sender, err)
return 0, fmt.Errorf("failed to lookup actor %s: %w", sender, err)
}

// Handle EVM actor case
Expand Down Expand Up @@ -464,7 +466,12 @@ func (a *ethAPI) EthGetTransactionReceiptLimited(ctx context.Context, txHash typ
}

msgLookup, err := a.chain.StateSearchMsg(ctx, types.EmptyTSK, c, limit, true)
if err != nil || msgLookup == nil {
if err != nil {
return nil, fmt.Errorf("failed to lookup Eth Txn %s as %s: %w", txHash, c, err)
}
if msgLookup == nil {
// This is the best we can do. In theory, we could have just not indexed this
// transaction, but there's no way to check that here.
return nil, nil
}

Expand All @@ -473,15 +480,20 @@ func (a *ethAPI) EthGetTransactionReceiptLimited(ctx context.Context, txHash typ
return nil, nil
}

var events []types.Event
if rct := msgLookup.Receipt; rct.EventsRoot != nil {
events, err = a.chain.ChainGetEvents(ctx, *rct.EventsRoot)
if err != nil {
return nil, nil
}
ts, err := a.em.chainModule.ChainReader.GetTipSet(ctx, msgLookup.TipSet)
if err != nil {
return nil, fmt.Errorf("failed to lookup tipset %s when constructing the eth txn receipt: %w", msgLookup.TipSet, err)
}

// The tx is located in the parent tipset
parentTS, err := a.em.chainModule.ChainReader.GetTipSet(ctx, ts.Parents())
if err != nil {
return nil, fmt.Errorf("failed to lookup tipset %s when constructing the eth txn receipt: %w", ts.Parents(), err)
}

receipt, err := newEthTxReceipt(ctx, tx, msgLookup, events, a.chain, a.em.chainModule.ChainReader)
baseFee := parentTS.Blocks()[0].ParentBaseFee

receipt, err := newEthTxReceipt(ctx, tx, baseFee, msgLookup.Receipt, a.EthEventHandler)
if err != nil {
return nil, nil
}
Expand All @@ -493,6 +505,63 @@ func (a *ethAPI) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHa
return types.EthTx{}, ErrUnsupported
}

func (a *ethAPI) EthGetBlockReceipts(ctx context.Context, blockParam types.EthBlockNumberOrHash) ([]*types.EthTxReceipt, error) {
return a.EthGetBlockReceiptsLimited(ctx, blockParam, constants.LookbackNoLimit)
}

func (a *ethAPI) EthGetBlockReceiptsLimited(ctx context.Context, blockParam types.EthBlockNumberOrHash, limit abi.ChainEpoch) ([]*types.EthTxReceipt, error) {
ts, err := getTipsetByEthBlockNumberOrHash(ctx, a.em.chainModule.ChainReader, blockParam)
if err != nil {
return nil, fmt.Errorf("failed to get tipset: %w", err)
}

tsCid, err := ts.Key().Cid()
if err != nil {
return nil, fmt.Errorf("failed to get tipset key cid: %w", err)
}

blkHash, err := types.EthHashFromCid(tsCid)
if err != nil {
return nil, fmt.Errorf("failed to parse eth hash from cid: %w", err)
}

// Execute the tipset to get the receipts, messages, and events
_, msgs, receipts, err := executeTipset(ctx, ts, a.em.chainModule.MessageStore, a.em.chainModule.Stmgr)
if err != nil {
return nil, fmt.Errorf("failed to execute tipset: %w", err)
}

// Load the state tree
state, err := a.em.chainModule.ChainReader.GetTipSetState(ctx, ts)
if err != nil {
return nil, fmt.Errorf("failed to get state view: %w", err)
}

baseFee := ts.Blocks()[0].ParentBaseFee

ethReceipts := make([]*types.EthTxReceipt, 0, len(msgs))
for i, msg := range msgs {
msg := msg

tx, err := newEthTx(ctx, state, ts.Height(), tsCid, msg.Cid(), i, a.ethTxHashManager.messageStore)
if err != nil {
return nil, fmt.Errorf("failed to create EthTx: %w", err)
}

receipt, err := newEthTxReceipt(ctx, tx, baseFee, receipts[i], a.EthEventHandler)
if err != nil {
return nil, fmt.Errorf("failed to create Eth receipt: %w", err)
}

// Set the correct Ethereum block hash
receipt.BlockHash = blkHash

ethReceipts = append(ethReceipts, &receipt)
}

return ethReceipts, nil
}

func (a *ethAPI) EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum types.EthUint64, txIndex types.EthUint64) (types.EthTx, error) {
return types.EthTx{}, ErrUnsupported
}
Expand Down
Loading
Loading