Skip to content

Commit

Permalink
fix(eth): complete eth_getBlockReceipts implementation
Browse files Browse the repository at this point in the history
Co-authored-by: Rod Vagg <rod@vagg.org>
  • Loading branch information
aarshkshah1992 and rvagg committed Sep 26, 2024
1 parent 2a75ba8 commit f33d4a2
Show file tree
Hide file tree
Showing 8 changed files with 165 additions and 212 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Lotus changelog

# UNRELEASED
Add `EthGetBlockReceipts` RPC method to retrieve transaction receipts for a specified block. This method allows users to obtain the receipts of all transactions included in a given Ethereum block. ([filecoin-project/lotus#12478](https://github.com/filecoin-project/lotus/pull/12478))
Add `EthGetBlockReceipts` RPC method to retrieve transaction receipts for a specified block. This method allows users to obtain Ethereum format receipts of all transactions included in a given tipset as specified by its Ethereum block equivalent. ([filecoin-project/lotus#12478](https://github.com/filecoin-project/lotus/pull/12478))

## ☢️ Upgrade Warnings ☢️

Expand Down
51 changes: 24 additions & 27 deletions chain/events/filter/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (
"github.com/filecoin-project/go-state-types/abi"
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"

"github.com/filecoin-project/lotus/chain/actors/adt"
cstore "github.com/filecoin-project/lotus/chain/store"
"github.com/filecoin-project/lotus/chain/types"
)
Expand Down Expand Up @@ -309,9 +308,10 @@ type EventFilterManager struct {
AddressResolver func(ctx context.Context, emitter abi.ActorID, ts *types.TipSet) (address.Address, bool)
MaxFilterResults int
EventIndex *EventIndex
mu sync.Mutex // guards mutations to filters
filters map[types.FilterID]EventFilter
currentHeight abi.ChainEpoch

mu sync.Mutex // guards mutations to filters
filters map[types.FilterID]EventFilter
currentHeight abi.ChainEpoch
}

func (m *EventFilterManager) Apply(ctx context.Context, from, to *types.TipSet) error {
Expand Down Expand Up @@ -438,43 +438,37 @@ func (m *EventFilterManager) loadExecutedMessages(ctx context.Context, msgTs, rc
return nil, xerrors.Errorf("read messages: %w", err)
}

store := m.ChainStore.ActorStore(ctx)
st := m.ChainStore.ActorStore(ctx)

arr, err := blockadt.AsArray(store, rctTs.Blocks()[0].ParentMessageReceipts)
arr, err := blockadt.AsArray(st, rctTs.Blocks()[0].ParentMessageReceipts)
if err != nil {
return nil, xerrors.Errorf("load receipts amt: %w", err)
}

receipts := make([]types.MessageReceipt, arr.Length())
for i := 0; i < len(receipts); i++ {
found, err := arr.Get(uint64(i), &receipts[i])
if err != nil {
return nil, xerrors.Errorf("load receipt: %w", err)
}
if !found {
return nil, xerrors.Errorf("receipt %d not found", i)
}
}

return LoadExecutedMessages(ctx, store, msgs, receipts)
}

func LoadExecutedMessages(ctx context.Context, store adt.Store, msgs []types.ChainMsg, receipts []types.MessageReceipt) ([]executedMessage, error) {
if len(msgs) != len(receipts) {
return nil, xerrors.Errorf("mismatching message and receipt counts (%d msgs, %d rcts)", len(msgs), len(receipts))
if uint64(len(msgs)) != arr.Length() {
return nil, xerrors.Errorf("mismatching message and receipt counts (%d msgs, %d rcts)", len(msgs), arr.Length())
}

ems := make([]executedMessage, len(msgs))

for i := 0; i < len(msgs); i++ {
ems[i].msg = msgs[i]
ems[i].rct = &receipts[i]

if receipts[i].EventsRoot == nil {
var rct types.MessageReceipt
found, err := arr.Get(uint64(i), &rct)
if err != nil {
return nil, xerrors.Errorf("load receipt: %w", err)
}
if !found {
return nil, xerrors.Errorf("receipt %d not found", i)
}
ems[i].rct = &rct

if rct.EventsRoot == nil {
continue
}

evtArr, err := amt4.LoadAMT(ctx, store, *receipts[i].EventsRoot, amt4.UseTreeBitWidth(types.EventAMTBitwidth))
evtArr, err := amt4.LoadAMT(ctx, st, *rct.EventsRoot, amt4.UseTreeBitWidth(types.EventAMTBitwidth))
if err != nil {
return nil, xerrors.Errorf("load events amt: %w", err)
}
Expand All @@ -488,13 +482,16 @@ func LoadExecutedMessages(ctx context.Context, store adt.Store, msgs []types.Cha
if err := evt.UnmarshalCBOR(bytes.NewReader(deferred.Raw)); err != nil {
return err
}

cpy := evt
ems[i].evs[int(u)] = &cpy
ems[i].evs[int(u)] = &cpy //nolint:scopelint
return nil
})

if err != nil {
return nil, xerrors.Errorf("read events: %w", err)
}

}

return ems, nil
Expand Down
39 changes: 20 additions & 19 deletions gateway/proxy_eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -683,6 +683,26 @@ func (gw *Node) EthTraceFilter(ctx context.Context, filter ethtypes.EthTraceFilt
return gw.target.EthTraceFilter(ctx, filter)
}

func (gw *Node) EthGetBlockReceipts(ctx context.Context, blkParam ethtypes.EthBlockNumberOrHash) ([]*api.EthTxReceipt, error) {
return gw.EthGetBlockReceiptsLimited(ctx, blkParam, api.LookbackNoLimit)
}

func (gw *Node) EthGetBlockReceiptsLimited(ctx context.Context, blkParam ethtypes.EthBlockNumberOrHash, limit abi.ChainEpoch) ([]*api.EthTxReceipt, error) {
if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
return nil, err
}

if limit == api.LookbackNoLimit {
limit = gw.maxMessageLookbackEpochs
}

if gw.maxMessageLookbackEpochs != api.LookbackNoLimit && limit > gw.maxMessageLookbackEpochs {
limit = gw.maxMessageLookbackEpochs
}

return gw.target.EthGetBlockReceiptsLimited(ctx, blkParam, limit)
}

func (gw *Node) addUserFilterLimited(
ctx context.Context,
callName string,
Expand Down Expand Up @@ -762,22 +782,3 @@ func newStatefulCallTracker() *statefulCallTracker {
userSubscriptions: make(map[ethtypes.EthSubscriptionID]cleanup),
}
}
func (gw *Node) EthGetBlockReceipts(ctx context.Context, blkParam ethtypes.EthBlockNumberOrHash) ([]*api.EthTxReceipt, error) {
return gw.EthGetBlockReceiptsLimited(ctx, blkParam, api.LookbackNoLimit)
}

func (gw *Node) EthGetBlockReceiptsLimited(ctx context.Context, blkParam ethtypes.EthBlockNumberOrHash, limit abi.ChainEpoch) ([]*api.EthTxReceipt, error) {
if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
return nil, err
}

if limit == api.LookbackNoLimit {
limit = gw.maxMessageLookbackEpochs
}

if gw.maxMessageLookbackEpochs != api.LookbackNoLimit && limit > gw.maxMessageLookbackEpochs {
limit = gw.maxMessageLookbackEpochs
}

return gw.target.EthGetBlockReceiptsLimited(ctx, blkParam, limit)
}
18 changes: 18 additions & 0 deletions itests/eth_deploy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,24 @@ func TestDeployment(t *testing.T) {
receipts, err := client.EthGetBlockReceipts(ctx, ethtypes.EthBlockNumberOrHash{BlockHash: &receipt.BlockHash})
require.NoError(t, err)
require.NotNil(t, receipts)
require.Greater(t, len(receipts), 0)
var matchingReceipt *api.EthTxReceipt
for _, r := range receipts {
if r.TransactionHash == receipt.TransactionHash {
require.Nil(t, matchingReceipt, "Multiple matching receipts found")
matchingReceipt = r
}
}
require.NotNil(t, matchingReceipt, "No matching receipt found")

require.NotNil(t, receipt.ContractAddress)
require.NotNil(t, matchingReceipt.ContractAddress)
require.Equal(t, *receipt.ContractAddress, *matchingReceipt.ContractAddress)
originalReceiptContractAddress := receipt.ContractAddress
receipt.ContractAddress = nil
matchingReceipt.ContractAddress = nil
require.Equal(t, receipt, matchingReceipt)
receipt.ContractAddress = originalReceiptContractAddress

// logs must be an empty array, not a nil value, to avoid tooling compatibility issues
require.Empty(t, receipt.Logs)
Expand Down
1 change: 0 additions & 1 deletion itests/eth_filter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -517,7 +517,6 @@ func TestEthGetLogsBasic(t *testing.T) {
require.NotNil(rct)

require.Len(rct.Logs, 1)

var rctLogs []*ethtypes.EthLog
for _, rctLog := range rct.Logs {
addr := &rctLog
Expand Down
12 changes: 7 additions & 5 deletions itests/fevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1148,17 +1148,19 @@ func TestEthGetBlockReceipts(t *testing.T) {
require.Len(t, blockReceipts, 3) // Ensure there are three receipts

expectedLogCounts := []int{4, 3, 3}

for i, receipt := range blockReceipts {
require.Equal(t, &contractAddr, receipt.To)
require.Equal(t, ethtypes.EthUint64(1), receipt.Status)
require.NotEmpty(t, receipt.BlockHash, "Block hash should not be empty")
require.Equal(t, expectedLogCounts[i], len(receipt.Logs), fmt.Sprintf("Transaction %d should have %d event logs", i+1, expectedLogCounts[i]))
}
if i > 0 {
require.Equal(t, blockReceipts[i-1].BlockHash, receipt.BlockHash, "All receipts should have the same block hash")
}

// Verify that all receipts have the same block hash
firstBlockHash := blockReceipts[0].BlockHash
for _, receipt := range blockReceipts {
require.Equal(t, firstBlockHash, receipt.BlockHash, "All receipts should have the same block hash")
txReceipt, err := client.EthGetTransactionReceipt(ctx, receipt.TransactionHash)
require.NoError(t, err)
require.Equal(t, txReceipt, receipt)
}
}

Expand Down
Loading

0 comments on commit f33d4a2

Please sign in to comment.