Eth JSON-RPC API: return logs in eth_getTransactionReceipt.
This commit is contained in:
parent
7c2dcc8db6
commit
32385a97cd
@ -181,6 +181,9 @@ type FullNode interface {
|
||||
// ChainBlockstoreInfo returns some basic information about the blockstore
|
||||
ChainBlockstoreInfo(context.Context) (map[string]interface{}, error) //perm:read
|
||||
|
||||
// ChainGetEvents returns the events under an event AMT root CID.
|
||||
ChainGetEvents(context.Context, cid.Cid) ([]types.Event, error) //perm:read
|
||||
|
||||
// GasEstimateFeeCap estimates gas fee cap
|
||||
GasEstimateFeeCap(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error) //perm:read
|
||||
|
||||
|
@ -17,11 +17,20 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/go-state-types/big"
|
||||
builtintypes "github.com/filecoin-project/go-state-types/builtin"
|
||||
"github.com/filecoin-project/go-state-types/builtin/v10/eam"
|
||||
|
||||
"github.com/filecoin-project/lotus/build"
|
||||
"github.com/filecoin-project/lotus/chain/types"
|
||||
)
|
||||
|
||||
var (
|
||||
EthTopic1 = "topic1"
|
||||
EthTopic2 = "topic2"
|
||||
EthTopic3 = "topic3"
|
||||
EthTopic4 = "topic4"
|
||||
)
|
||||
|
||||
type EthUint64 uint64
|
||||
@ -185,14 +194,12 @@ func (c *EthCall) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
|
||||
type EthTxReceipt struct {
|
||||
TransactionHash EthHash `json:"transactionHash"`
|
||||
TransactionIndex EthUint64 `json:"transactionIndex"`
|
||||
BlockHash EthHash `json:"blockHash"`
|
||||
BlockNumber EthUint64 `json:"blockNumber"`
|
||||
From EthAddress `json:"from"`
|
||||
To *EthAddress `json:"to"`
|
||||
// Logs
|
||||
// LogsBloom
|
||||
TransactionHash EthHash `json:"transactionHash"`
|
||||
TransactionIndex EthUint64 `json:"transactionIndex"`
|
||||
BlockHash EthHash `json:"blockHash"`
|
||||
BlockNumber EthUint64 `json:"blockNumber"`
|
||||
From EthAddress `json:"from"`
|
||||
To *EthAddress `json:"to"`
|
||||
StateRoot EthHash `json:"root"`
|
||||
Status EthUint64 `json:"status"`
|
||||
ContractAddress *EthAddress `json:"contractAddress"`
|
||||
@ -200,10 +207,10 @@ type EthTxReceipt struct {
|
||||
GasUsed EthUint64 `json:"gasUsed"`
|
||||
EffectiveGasPrice EthBigInt `json:"effectiveGasPrice"`
|
||||
LogsBloom EthBytes `json:"logsBloom"`
|
||||
Logs []string `json:"logs"`
|
||||
Logs []EthLog `json:"logs"`
|
||||
}
|
||||
|
||||
func NewEthTxReceipt(tx EthTx, lookup *MsgLookup, replay *InvocResult) (EthTxReceipt, error) {
|
||||
func NewEthTxReceipt(tx EthTx, lookup *MsgLookup, replay *InvocResult, events []types.Event, addressResolver func(id abi.ActorID) (address.Address, bool, error)) (EthTxReceipt, error) {
|
||||
receipt := EthTxReceipt{
|
||||
TransactionHash: tx.Hash,
|
||||
TransactionIndex: tx.TransactionIndex,
|
||||
@ -213,7 +220,6 @@ func NewEthTxReceipt(tx EthTx, lookup *MsgLookup, replay *InvocResult) (EthTxRec
|
||||
To: tx.To,
|
||||
StateRoot: EmptyEthHash,
|
||||
LogsBloom: []byte{0},
|
||||
Logs: []string{},
|
||||
}
|
||||
|
||||
if receipt.To == nil && lookup.Receipt.ExitCode.IsSuccess() {
|
||||
@ -233,6 +239,41 @@ func NewEthTxReceipt(tx EthTx, lookup *MsgLookup, replay *InvocResult) (EthTxRec
|
||||
receipt.Status = 0
|
||||
}
|
||||
|
||||
if len(events) > 0 {
|
||||
receipt.Logs = make([]EthLog, 0, len(events))
|
||||
for i, evt := range events {
|
||||
l := EthLog{
|
||||
Removed: false,
|
||||
LogIndex: EthUint64(i),
|
||||
TransactionIndex: tx.TransactionIndex,
|
||||
TransactionHash: tx.Hash,
|
||||
BlockHash: tx.BlockHash,
|
||||
BlockNumber: tx.BlockNumber,
|
||||
}
|
||||
|
||||
for _, entry := range evt.Entries {
|
||||
hash := EthHashData(entry.Value)
|
||||
if entry.Key == EthTopic1 || entry.Key == EthTopic2 || entry.Key == EthTopic3 || entry.Key == EthTopic4 {
|
||||
l.Topics = append(l.Topics, hash)
|
||||
} else {
|
||||
l.Data = append(l.Data, hash)
|
||||
}
|
||||
}
|
||||
|
||||
f4addr, ok, err := addressResolver(evt.Emitter)
|
||||
if err != nil || !ok {
|
||||
return EthTxReceipt{}, xerrors.Errorf("failed to resolve predictable address: %w", err)
|
||||
}
|
||||
|
||||
l.Address, err = EthAddressFromFilecoinAddress(f4addr)
|
||||
if err != nil {
|
||||
return EthTxReceipt{}, xerrors.Errorf("failed to translate to Ethereum address: %w", err)
|
||||
}
|
||||
|
||||
receipt.Logs = append(receipt.Logs, l)
|
||||
}
|
||||
}
|
||||
|
||||
receipt.GasUsed = EthUint64(lookup.Receipt.GasUsed)
|
||||
|
||||
// TODO: handle CumulativeGasUsed
|
||||
|
@ -183,6 +183,21 @@ func (mr *MockFullNodeMockRecorder) ChainGetBlockMessages(arg0, arg1 interface{}
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainGetBlockMessages", reflect.TypeOf((*MockFullNode)(nil).ChainGetBlockMessages), arg0, arg1)
|
||||
}
|
||||
|
||||
// ChainGetEvents mocks base method.
|
||||
func (m *MockFullNode) ChainGetEvents(arg0 context.Context, arg1 cid.Cid) ([]types.Event, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ChainGetEvents", arg0, arg1)
|
||||
ret0, _ := ret[0].([]types.Event)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ChainGetEvents indicates an expected call of ChainGetEvents.
|
||||
func (mr *MockFullNodeMockRecorder) ChainGetEvents(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ChainGetEvents", reflect.TypeOf((*MockFullNode)(nil).ChainGetEvents), arg0, arg1)
|
||||
}
|
||||
|
||||
// ChainGetGenesis mocks base method.
|
||||
func (m *MockFullNode) ChainGetGenesis(arg0 context.Context) (*types.TipSet, error) {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -123,6 +123,8 @@ type FullNodeStruct struct {
|
||||
|
||||
ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) `perm:"read"`
|
||||
|
||||
ChainGetEvents func(p0 context.Context, p1 cid.Cid) ([]types.Event, error) `perm:"read"`
|
||||
|
||||
ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) `perm:"read"`
|
||||
|
||||
ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) `perm:"read"`
|
||||
@ -1330,6 +1332,17 @@ func (s *FullNodeStub) ChainGetBlockMessages(p0 context.Context, p1 cid.Cid) (*B
|
||||
return nil, ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) ChainGetEvents(p0 context.Context, p1 cid.Cid) ([]types.Event, error) {
|
||||
if s.Internal.ChainGetEvents == nil {
|
||||
return *new([]types.Event), ErrNotSupported
|
||||
}
|
||||
return s.Internal.ChainGetEvents(p0, p1)
|
||||
}
|
||||
|
||||
func (s *FullNodeStub) ChainGetEvents(p0 context.Context, p1 cid.Cid) ([]types.Event, error) {
|
||||
return *new([]types.Event), ErrNotSupported
|
||||
}
|
||||
|
||||
func (s *FullNodeStruct) ChainGetGenesis(p0 context.Context) (*types.TipSet, error) {
|
||||
if s.Internal.ChainGetGenesis == nil {
|
||||
return nil, ErrNotSupported
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -15,6 +15,7 @@
|
||||
* [ChainExport](#ChainExport)
|
||||
* [ChainGetBlock](#ChainGetBlock)
|
||||
* [ChainGetBlockMessages](#ChainGetBlockMessages)
|
||||
* [ChainGetEvents](#ChainGetEvents)
|
||||
* [ChainGetGenesis](#ChainGetGenesis)
|
||||
* [ChainGetMessage](#ChainGetMessage)
|
||||
* [ChainGetMessagesInTipset](#ChainGetMessagesInTipset)
|
||||
@ -612,6 +613,37 @@ Response:
|
||||
}
|
||||
```
|
||||
|
||||
### ChainGetEvents
|
||||
ChainGetEvents returns the events under an event AMT root CID.
|
||||
|
||||
|
||||
Perms: read
|
||||
|
||||
Inputs:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"/": "bafy2bzacea3wsdh6y3a36tb3skempjoxqpuyompjbmfeyf34fi3uy6uue42v4"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
Response:
|
||||
```json
|
||||
[
|
||||
{
|
||||
"Emitter": 1000,
|
||||
"Entries": [
|
||||
{
|
||||
"Flags": 7,
|
||||
"Key": "string value",
|
||||
"Value": "Ynl0ZSBhcnJheQ=="
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### ChainGetGenesis
|
||||
ChainGetGenesis returns the genesis tipset.
|
||||
|
||||
@ -2703,7 +2735,21 @@ Response:
|
||||
"effectiveGasPrice": "0x0",
|
||||
"logsBloom": "0x07",
|
||||
"logs": [
|
||||
"string value"
|
||||
{
|
||||
"address": "0x0707070707070707070707070707070707070707",
|
||||
"data": [
|
||||
"0x0707070707070707070707070707070707070707070707070707070707070707"
|
||||
],
|
||||
"topics": [
|
||||
"0x0707070707070707070707070707070707070707070707070707070707070707"
|
||||
],
|
||||
"removed": true,
|
||||
"logIndex": "0x5",
|
||||
"transactionIndex": "0x5",
|
||||
"transactionHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
|
||||
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
|
||||
"blockNumber": "0x5"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -24,6 +25,7 @@ import (
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/filecoin-project/go-address"
|
||||
amt4 "github.com/filecoin-project/go-amt-ipld/v4"
|
||||
"github.com/filecoin-project/go-state-types/abi"
|
||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
||||
|
||||
@ -654,6 +656,33 @@ func (a *ChainAPI) ChainBlockstoreInfo(ctx context.Context) (map[string]interfac
|
||||
return info.Info(), nil
|
||||
}
|
||||
|
||||
// ChainGetEvents returns the events under an event AMT root CID.
|
||||
//
|
||||
// TODO (raulk) make copies of this logic elsewhere use this (e.g. itests, CLI, events filter).
|
||||
func (a *ChainAPI) ChainGetEvents(ctx context.Context, root cid.Cid) ([]types.Event, error) {
|
||||
store := cbor.NewCborStore(a.ExposedBlockstore)
|
||||
evtArr, err := amt4.LoadAMT(ctx, store, root, amt4.UseTreeBitWidth(5))
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("load events amt: %w", err)
|
||||
}
|
||||
|
||||
ret := make([]types.Event, 0, evtArr.Len())
|
||||
var evt types.Event
|
||||
err = evtArr.ForEach(ctx, func(u uint64, deferred *cbg.Deferred) error {
|
||||
if u > math.MaxInt {
|
||||
return xerrors.Errorf("too many events")
|
||||
}
|
||||
if err := evt.UnmarshalCBOR(bytes.NewReader(deferred.Raw)); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ret = append(ret, evt)
|
||||
return nil
|
||||
})
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (a *ChainAPI) ChainPrune(ctx context.Context, opts api.PruneOpts) error {
|
||||
pruner, ok := a.BaseBlockstore.(interface {
|
||||
PruneChain(opts api.PruneOpts) error
|
||||
|
@ -59,7 +59,6 @@ type EthModuleAPI interface {
|
||||
EthCall(ctx context.Context, tx api.EthCall, blkParam string) (api.EthBytes, error)
|
||||
EthMaxPriorityFeePerGas(ctx context.Context) (api.EthBigInt, error)
|
||||
EthSendRawTransaction(ctx context.Context, rawTx api.EthBytes) (api.EthHash, error)
|
||||
// EthFeeHistory(ctx context.Context, blkCount string)
|
||||
}
|
||||
|
||||
type EthEventAPI interface {
|
||||
@ -237,7 +236,29 @@ func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash api.Eth
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
receipt, err := api.NewEthTxReceipt(tx, msgLookup, replay)
|
||||
var events []types.Event
|
||||
if rct := replay.MsgRct; rct != nil && rct.EventsRoot != nil {
|
||||
events, err = a.ChainAPI.ChainGetEvents(ctx, *rct.EventsRoot)
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
receipt, err := api.NewEthTxReceipt(tx, msgLookup, replay, events, func(id abi.ActorID) (address.Address, bool, error) {
|
||||
addr, err := address.NewIDAddress(uint64(id))
|
||||
if err != nil {
|
||||
return address.Undef, false, xerrors.Errorf("failed to create ID address: %w", err)
|
||||
}
|
||||
actor, err := a.StateGetActor(ctx, addr, types.EmptyTSK)
|
||||
if err != nil {
|
||||
return address.Undef, false, xerrors.Errorf("failed to load actor: %w", err)
|
||||
}
|
||||
if actor.Address == nil {
|
||||
return address.Undef, false, nil
|
||||
}
|
||||
return *actor.Address, true, nil
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
@ -1242,13 +1263,6 @@ type filterTipSetCollector interface {
|
||||
TakeCollectedTipSets(context.Context) []types.TipSetKey
|
||||
}
|
||||
|
||||
var (
|
||||
ethTopic1 = "topic1"
|
||||
ethTopic2 = "topic2"
|
||||
ethTopic3 = "topic3"
|
||||
ethTopic4 = "topic4"
|
||||
)
|
||||
|
||||
func ethFilterResultFromEvents(evs []*filter.CollectedEvent) (*api.EthFilterResult, error) {
|
||||
res := &api.EthFilterResult{}
|
||||
for _, ev := range evs {
|
||||
@ -1263,7 +1277,7 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent) (*api.EthFilterResu
|
||||
|
||||
for _, entry := range ev.Entries {
|
||||
hash := api.EthHashData(entry.Value)
|
||||
if entry.Key == ethTopic1 || entry.Key == ethTopic2 || entry.Key == ethTopic3 || entry.Key == ethTopic4 {
|
||||
if entry.Key == api.EthTopic1 || entry.Key == api.EthTopic2 || entry.Key == api.EthTopic3 || entry.Key == api.EthTopic4 {
|
||||
log.Topics = append(log.Topics, hash)
|
||||
} else {
|
||||
log.Data = append(log.Data, hash)
|
||||
|
Loading…
Reference in New Issue
Block a user