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 returns some basic information about the blockstore
|
||||||
ChainBlockstoreInfo(context.Context) (map[string]interface{}, error) //perm:read
|
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 estimates gas fee cap
|
||||||
GasEstimateFeeCap(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error) //perm:read
|
GasEstimateFeeCap(context.Context, *types.Message, int64, types.TipSetKey) (types.BigInt, error) //perm:read
|
||||||
|
|
||||||
|
@ -17,11 +17,20 @@ import (
|
|||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"github.com/filecoin-project/go-address"
|
||||||
|
"github.com/filecoin-project/go-state-types/abi"
|
||||||
"github.com/filecoin-project/go-state-types/big"
|
"github.com/filecoin-project/go-state-types/big"
|
||||||
builtintypes "github.com/filecoin-project/go-state-types/builtin"
|
builtintypes "github.com/filecoin-project/go-state-types/builtin"
|
||||||
"github.com/filecoin-project/go-state-types/builtin/v10/eam"
|
"github.com/filecoin-project/go-state-types/builtin/v10/eam"
|
||||||
|
|
||||||
"github.com/filecoin-project/lotus/build"
|
"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
|
type EthUint64 uint64
|
||||||
@ -185,14 +194,12 @@ func (c *EthCall) UnmarshalJSON(b []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type EthTxReceipt struct {
|
type EthTxReceipt struct {
|
||||||
TransactionHash EthHash `json:"transactionHash"`
|
TransactionHash EthHash `json:"transactionHash"`
|
||||||
TransactionIndex EthUint64 `json:"transactionIndex"`
|
TransactionIndex EthUint64 `json:"transactionIndex"`
|
||||||
BlockHash EthHash `json:"blockHash"`
|
BlockHash EthHash `json:"blockHash"`
|
||||||
BlockNumber EthUint64 `json:"blockNumber"`
|
BlockNumber EthUint64 `json:"blockNumber"`
|
||||||
From EthAddress `json:"from"`
|
From EthAddress `json:"from"`
|
||||||
To *EthAddress `json:"to"`
|
To *EthAddress `json:"to"`
|
||||||
// Logs
|
|
||||||
// LogsBloom
|
|
||||||
StateRoot EthHash `json:"root"`
|
StateRoot EthHash `json:"root"`
|
||||||
Status EthUint64 `json:"status"`
|
Status EthUint64 `json:"status"`
|
||||||
ContractAddress *EthAddress `json:"contractAddress"`
|
ContractAddress *EthAddress `json:"contractAddress"`
|
||||||
@ -200,10 +207,10 @@ type EthTxReceipt struct {
|
|||||||
GasUsed EthUint64 `json:"gasUsed"`
|
GasUsed EthUint64 `json:"gasUsed"`
|
||||||
EffectiveGasPrice EthBigInt `json:"effectiveGasPrice"`
|
EffectiveGasPrice EthBigInt `json:"effectiveGasPrice"`
|
||||||
LogsBloom EthBytes `json:"logsBloom"`
|
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{
|
receipt := EthTxReceipt{
|
||||||
TransactionHash: tx.Hash,
|
TransactionHash: tx.Hash,
|
||||||
TransactionIndex: tx.TransactionIndex,
|
TransactionIndex: tx.TransactionIndex,
|
||||||
@ -213,7 +220,6 @@ func NewEthTxReceipt(tx EthTx, lookup *MsgLookup, replay *InvocResult) (EthTxRec
|
|||||||
To: tx.To,
|
To: tx.To,
|
||||||
StateRoot: EmptyEthHash,
|
StateRoot: EmptyEthHash,
|
||||||
LogsBloom: []byte{0},
|
LogsBloom: []byte{0},
|
||||||
Logs: []string{},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if receipt.To == nil && lookup.Receipt.ExitCode.IsSuccess() {
|
if receipt.To == nil && lookup.Receipt.ExitCode.IsSuccess() {
|
||||||
@ -233,6 +239,41 @@ func NewEthTxReceipt(tx EthTx, lookup *MsgLookup, replay *InvocResult) (EthTxRec
|
|||||||
receipt.Status = 0
|
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)
|
receipt.GasUsed = EthUint64(lookup.Receipt.GasUsed)
|
||||||
|
|
||||||
// TODO: handle CumulativeGasUsed
|
// 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)
|
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.
|
// ChainGetGenesis mocks base method.
|
||||||
func (m *MockFullNode) ChainGetGenesis(arg0 context.Context) (*types.TipSet, error) {
|
func (m *MockFullNode) ChainGetGenesis(arg0 context.Context) (*types.TipSet, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
|
@ -123,6 +123,8 @@ type FullNodeStruct struct {
|
|||||||
|
|
||||||
ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) `perm:"read"`
|
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"`
|
ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) `perm:"read"`
|
||||||
|
|
||||||
ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, 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
|
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) {
|
func (s *FullNodeStruct) ChainGetGenesis(p0 context.Context) (*types.TipSet, error) {
|
||||||
if s.Internal.ChainGetGenesis == nil {
|
if s.Internal.ChainGetGenesis == nil {
|
||||||
return nil, ErrNotSupported
|
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)
|
* [ChainExport](#ChainExport)
|
||||||
* [ChainGetBlock](#ChainGetBlock)
|
* [ChainGetBlock](#ChainGetBlock)
|
||||||
* [ChainGetBlockMessages](#ChainGetBlockMessages)
|
* [ChainGetBlockMessages](#ChainGetBlockMessages)
|
||||||
|
* [ChainGetEvents](#ChainGetEvents)
|
||||||
* [ChainGetGenesis](#ChainGetGenesis)
|
* [ChainGetGenesis](#ChainGetGenesis)
|
||||||
* [ChainGetMessage](#ChainGetMessage)
|
* [ChainGetMessage](#ChainGetMessage)
|
||||||
* [ChainGetMessagesInTipset](#ChainGetMessagesInTipset)
|
* [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
|
||||||
ChainGetGenesis returns the genesis tipset.
|
ChainGetGenesis returns the genesis tipset.
|
||||||
|
|
||||||
@ -2703,7 +2735,21 @@ Response:
|
|||||||
"effectiveGasPrice": "0x0",
|
"effectiveGasPrice": "0x0",
|
||||||
"logsBloom": "0x07",
|
"logsBloom": "0x07",
|
||||||
"logs": [
|
"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"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"math"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
@ -24,6 +25,7 @@ import (
|
|||||||
"golang.org/x/xerrors"
|
"golang.org/x/xerrors"
|
||||||
|
|
||||||
"github.com/filecoin-project/go-address"
|
"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/go-state-types/abi"
|
||||||
"github.com/filecoin-project/specs-actors/actors/util/adt"
|
"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
|
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 {
|
func (a *ChainAPI) ChainPrune(ctx context.Context, opts api.PruneOpts) error {
|
||||||
pruner, ok := a.BaseBlockstore.(interface {
|
pruner, ok := a.BaseBlockstore.(interface {
|
||||||
PruneChain(opts api.PruneOpts) error
|
PruneChain(opts api.PruneOpts) error
|
||||||
|
@ -59,7 +59,6 @@ type EthModuleAPI interface {
|
|||||||
EthCall(ctx context.Context, tx api.EthCall, blkParam string) (api.EthBytes, error)
|
EthCall(ctx context.Context, tx api.EthCall, blkParam string) (api.EthBytes, error)
|
||||||
EthMaxPriorityFeePerGas(ctx context.Context) (api.EthBigInt, error)
|
EthMaxPriorityFeePerGas(ctx context.Context) (api.EthBigInt, error)
|
||||||
EthSendRawTransaction(ctx context.Context, rawTx api.EthBytes) (api.EthHash, error)
|
EthSendRawTransaction(ctx context.Context, rawTx api.EthBytes) (api.EthHash, error)
|
||||||
// EthFeeHistory(ctx context.Context, blkCount string)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type EthEventAPI interface {
|
type EthEventAPI interface {
|
||||||
@ -237,7 +236,29 @@ func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash api.Eth
|
|||||||
return nil, nil
|
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 {
|
if err != nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@ -1242,13 +1263,6 @@ type filterTipSetCollector interface {
|
|||||||
TakeCollectedTipSets(context.Context) []types.TipSetKey
|
TakeCollectedTipSets(context.Context) []types.TipSetKey
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
ethTopic1 = "topic1"
|
|
||||||
ethTopic2 = "topic2"
|
|
||||||
ethTopic3 = "topic3"
|
|
||||||
ethTopic4 = "topic4"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ethFilterResultFromEvents(evs []*filter.CollectedEvent) (*api.EthFilterResult, error) {
|
func ethFilterResultFromEvents(evs []*filter.CollectedEvent) (*api.EthFilterResult, error) {
|
||||||
res := &api.EthFilterResult{}
|
res := &api.EthFilterResult{}
|
||||||
for _, ev := range evs {
|
for _, ev := range evs {
|
||||||
@ -1263,7 +1277,7 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent) (*api.EthFilterResu
|
|||||||
|
|
||||||
for _, entry := range ev.Entries {
|
for _, entry := range ev.Entries {
|
||||||
hash := api.EthHashData(entry.Value)
|
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)
|
log.Topics = append(log.Topics, hash)
|
||||||
} else {
|
} else {
|
||||||
log.Data = append(log.Data, hash)
|
log.Data = append(log.Data, hash)
|
||||||
|
Loading…
Reference in New Issue
Block a user