Merge branch 'release/v1.20.0' into asr/merge-release-into-master

This commit is contained in:
Aayush 2023-02-03 09:11:38 -05:00
commit f291572b4a
93 changed files with 4203 additions and 2237 deletions

View File

@ -680,6 +680,12 @@ workflows:
- build - build
suite: itest-eth_block_hash suite: itest-eth_block_hash
target: "./itests/eth_block_hash_test.go" target: "./itests/eth_block_hash_test.go"
- test:
name: test-itest-eth_config
requires:
- build
suite: itest-eth_config
target: "./itests/eth_config_test.go"
- test: - test:
name: test-itest-eth_deploy name: test-itest-eth_deploy
requires: requires:

View File

@ -16,6 +16,7 @@ import (
datatransfer "github.com/filecoin-project/go-data-transfer" datatransfer "github.com/filecoin-project/go-data-transfer"
"github.com/filecoin-project/go-fil-markets/retrievalmarket" "github.com/filecoin-project/go-fil-markets/retrievalmarket"
"github.com/filecoin-project/go-fil-markets/storagemarket" "github.com/filecoin-project/go-fil-markets/storagemarket"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
"github.com/filecoin-project/go-state-types/builtin/v8/paych" "github.com/filecoin-project/go-state-types/builtin/v8/paych"
@ -831,11 +832,14 @@ type FullNode interface {
// - logs: notify new event logs that match a criteria // - logs: notify new event logs that match a criteria
// params contains additional parameters used with the log event type // params contains additional parameters used with the log event type
// The client will receive a stream of EthSubscriptionResponse values until EthUnsubscribe is called. // The client will receive a stream of EthSubscriptionResponse values until EthUnsubscribe is called.
EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) //perm:write EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) //perm:write
// Unsubscribe from a websocket subscription // Unsubscribe from a websocket subscription
EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) //perm:write EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) //perm:write
// Returns the client version
Web3ClientVersion(ctx context.Context) (string, error) //perm:read
// CreateBackup creates node backup onder the specified file name. The // CreateBackup creates node backup onder the specified file name. The
// method requires that the lotus daemon is running with the // method requires that the lotus daemon is running with the
// LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that // LOTUS_BACKUP_BASE_PATH environment variable set to some path, and that
@ -846,6 +850,12 @@ type FullNode interface {
RaftLeader(ctx context.Context) (peer.ID, error) //perm:read RaftLeader(ctx context.Context) (peer.ID, error) //perm:read
} }
// reverse interface to the client, called after EthSubscribe
type EthSubscriber interface {
// note: the parameter is ethtypes.EthSubscriptionResponse serialized as json object
EthSubscription(ctx context.Context, r jsonrpc.RawParams) error //rpc_method:eth_subscription notify:true
}
type StorageAsk struct { type StorageAsk struct {
Response *storagemarket.StorageAsk Response *storagemarket.StorageAsk

View File

@ -7,6 +7,7 @@ import (
blocks "github.com/ipfs/go-libipfs/blocks" blocks "github.com/ipfs/go-libipfs/blocks"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/builtin/v9/miner" "github.com/filecoin-project/go-state-types/builtin/v9/miner"
"github.com/filecoin-project/go-state-types/dline" "github.com/filecoin-project/go-state-types/dline"
@ -78,6 +79,8 @@ type Gateway interface {
EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error) EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error)
EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error)
EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error)
EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error)
EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error)
EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error)
EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*EthTxReceipt, error) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*EthTxReceipt, error)
EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error)
@ -102,6 +105,7 @@ type Gateway interface {
EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error)
EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error)
EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error)
EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error)
EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error)
Web3ClientVersion(ctx context.Context) (string, error)
} }

View File

@ -35,10 +35,10 @@ func NewFullNodeRPCV0(ctx context.Context, addr string, requestHeader http.Heade
} }
// NewFullNodeRPCV1 creates a new http jsonrpc client. // NewFullNodeRPCV1 creates a new http jsonrpc client.
func NewFullNodeRPCV1(ctx context.Context, addr string, requestHeader http.Header) (api.FullNode, jsonrpc.ClientCloser, error) { func NewFullNodeRPCV1(ctx context.Context, addr string, requestHeader http.Header, opts ...jsonrpc.Option) (api.FullNode, jsonrpc.ClientCloser, error) {
var res v1api.FullNodeStruct var res v1api.FullNodeStruct
closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin", closer, err := jsonrpc.NewMergeClient(ctx, addr, "Filecoin",
api.GetInternalStructs(&res), requestHeader, jsonrpc.WithErrors(api.RPCErrors)) api.GetInternalStructs(&res), requestHeader, append([]jsonrpc.Option{jsonrpc.WithErrors(api.RPCErrors)}, opts...)...)
return &res, closer, err return &res, closer, err
} }

View File

@ -41,4 +41,6 @@ func CreateEthRPCAliases(as apitypes.Aliaser) {
as.AliasMethod("net_version", "Filecoin.NetVersion") as.AliasMethod("net_version", "Filecoin.NetVersion")
as.AliasMethod("net_listening", "Filecoin.NetListening") as.AliasMethod("net_listening", "Filecoin.NetListening")
as.AliasMethod("web3_clientVersion", "Filecoin.Web3ClientVersion")
} }

View File

@ -23,6 +23,7 @@ import (
bitfield "github.com/filecoin-project/go-bitfield" bitfield "github.com/filecoin-project/go-bitfield"
datatransfer "github.com/filecoin-project/go-data-transfer" datatransfer "github.com/filecoin-project/go-data-transfer"
retrievalmarket "github.com/filecoin-project/go-fil-markets/retrievalmarket" retrievalmarket "github.com/filecoin-project/go-fil-markets/retrievalmarket"
jsonrpc "github.com/filecoin-project/go-jsonrpc"
auth "github.com/filecoin-project/go-jsonrpc/auth" auth "github.com/filecoin-project/go-jsonrpc/auth"
abi "github.com/filecoin-project/go-state-types/abi" abi "github.com/filecoin-project/go-state-types/abi"
big "github.com/filecoin-project/go-state-types/big" big "github.com/filecoin-project/go-state-types/big"
@ -1388,18 +1389,18 @@ func (mr *MockFullNodeMockRecorder) EthSendRawTransaction(arg0, arg1 interface{}
} }
// EthSubscribe mocks base method. // EthSubscribe mocks base method.
func (m *MockFullNode) EthSubscribe(arg0 context.Context, arg1 string, arg2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { func (m *MockFullNode) EthSubscribe(arg0 context.Context, arg1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthSubscribe", arg0, arg1, arg2) ret := m.ctrl.Call(m, "EthSubscribe", arg0, arg1)
ret0, _ := ret[0].(<-chan ethtypes.EthSubscriptionResponse) ret0, _ := ret[0].(ethtypes.EthSubscriptionID)
ret1, _ := ret[1].(error) ret1, _ := ret[1].(error)
return ret0, ret1 return ret0, ret1
} }
// EthSubscribe indicates an expected call of EthSubscribe. // EthSubscribe indicates an expected call of EthSubscribe.
func (mr *MockFullNodeMockRecorder) EthSubscribe(arg0, arg1, arg2 interface{}) *gomock.Call { func (mr *MockFullNodeMockRecorder) EthSubscribe(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthSubscribe", reflect.TypeOf((*MockFullNode)(nil).EthSubscribe), arg0, arg1, arg2) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthSubscribe", reflect.TypeOf((*MockFullNode)(nil).EthSubscribe), arg0, arg1)
} }
// EthUninstallFilter mocks base method. // EthUninstallFilter mocks base method.
@ -4096,3 +4097,18 @@ func (mr *MockFullNodeMockRecorder) WalletVerify(arg0, arg1, arg2, arg3 interfac
mr.mock.ctrl.T.Helper() mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WalletVerify", reflect.TypeOf((*MockFullNode)(nil).WalletVerify), arg0, arg1, arg2, arg3) return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WalletVerify", reflect.TypeOf((*MockFullNode)(nil).WalletVerify), arg0, arg1, arg2, arg3)
} }
// Web3ClientVersion mocks base method.
func (m *MockFullNode) Web3ClientVersion(arg0 context.Context) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Web3ClientVersion", arg0)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// Web3ClientVersion indicates an expected call of Web3ClientVersion.
func (mr *MockFullNodeMockRecorder) Web3ClientVersion(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Web3ClientVersion", reflect.TypeOf((*MockFullNode)(nil).Web3ClientVersion), arg0)
}

View File

@ -22,6 +22,7 @@ import (
"github.com/filecoin-project/go-fil-markets/piecestore" "github.com/filecoin-project/go-fil-markets/piecestore"
"github.com/filecoin-project/go-fil-markets/retrievalmarket" "github.com/filecoin-project/go-fil-markets/retrievalmarket"
"github.com/filecoin-project/go-fil-markets/storagemarket" "github.com/filecoin-project/go-fil-markets/storagemarket"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-jsonrpc/auth" "github.com/filecoin-project/go-jsonrpc/auth"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/builtin/v8/paych" "github.com/filecoin-project/go-state-types/builtin/v8/paych"
@ -49,20 +50,25 @@ import (
var ErrNotSupported = xerrors.New("method not supported") var ErrNotSupported = xerrors.New("method not supported")
type ChainIOStruct struct { type ChainIOStruct struct {
Internal struct { Internal ChainIOMethods
}
type ChainIOMethods struct {
ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) `` ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) ``
ChainPutObj func(p0 context.Context, p1 blocks.Block) error `` ChainPutObj func(p0 context.Context, p1 blocks.Block) error ``
ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) `` ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) ``
}
} }
type ChainIOStub struct { type ChainIOStub struct {
} }
type CommonStruct struct { type CommonStruct struct {
Internal struct { Internal CommonMethods
}
type CommonMethods struct {
AuthNew func(p0 context.Context, p1 []auth.Permission) ([]byte, error) `perm:"admin"` AuthNew func(p0 context.Context, p1 []auth.Permission) ([]byte, error) `perm:"admin"`
AuthVerify func(p0 context.Context, p1 string) ([]auth.Permission, error) `perm:"read"` AuthVerify func(p0 context.Context, p1 string) ([]auth.Permission, error) `perm:"read"`
@ -84,7 +90,6 @@ type CommonStruct struct {
StartTime func(p0 context.Context) (time.Time, error) `perm:"read"` StartTime func(p0 context.Context) (time.Time, error) `perm:"read"`
Version func(p0 context.Context) (APIVersion, error) `perm:"read"` Version func(p0 context.Context) (APIVersion, error) `perm:"read"`
}
} }
type CommonStub struct { type CommonStub struct {
@ -95,8 +100,10 @@ type CommonNetStruct struct {
NetStruct NetStruct
Internal struct { Internal CommonNetMethods
} }
type CommonNetMethods struct {
} }
type CommonNetStub struct { type CommonNetStub struct {
@ -105,12 +112,26 @@ type CommonNetStub struct {
NetStub NetStub
} }
type EthSubscriberStruct struct {
Internal EthSubscriberMethods
}
type EthSubscriberMethods struct {
EthSubscription func(p0 context.Context, p1 jsonrpc.RawParams) error `notify:"true"rpc_method:"eth_subscription"`
}
type EthSubscriberStub struct {
}
type FullNodeStruct struct { type FullNodeStruct struct {
CommonStruct CommonStruct
NetStruct NetStruct
Internal struct { Internal FullNodeMethods
}
type FullNodeMethods struct {
ChainBlockstoreInfo func(p0 context.Context) (map[string]interface{}, error) `perm:"read"` ChainBlockstoreInfo func(p0 context.Context) (map[string]interface{}, error) `perm:"read"`
ChainCheckBlockstore func(p0 context.Context) error `perm:"admin"` ChainCheckBlockstore func(p0 context.Context) error `perm:"admin"`
@ -281,7 +302,7 @@ type FullNodeStruct struct {
EthSendRawTransaction func(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) `perm:"read"` EthSendRawTransaction func(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) `perm:"read"`
EthSubscribe func(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) `perm:"write"` EthSubscribe func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) `perm:"write"`
EthUninstallFilter func(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) `perm:"write"` EthUninstallFilter func(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) `perm:"write"`
@ -582,7 +603,8 @@ type FullNodeStruct struct {
WalletValidateAddress func(p0 context.Context, p1 string) (address.Address, error) `perm:"read"` WalletValidateAddress func(p0 context.Context, p1 string) (address.Address, error) `perm:"read"`
WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"` WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"`
}
Web3ClientVersion func(p0 context.Context) (string, error) `perm:"read"`
} }
type FullNodeStub struct { type FullNodeStub struct {
@ -592,7 +614,10 @@ type FullNodeStub struct {
} }
type GatewayStruct struct { type GatewayStruct struct {
Internal struct { Internal GatewayMethods
}
type GatewayMethods struct {
ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) `` ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) ``
ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) `` ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) ``
@ -655,6 +680,8 @@ type GatewayStruct struct {
EthGetLogs func(p0 context.Context, p1 *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) `` EthGetLogs func(p0 context.Context, p1 *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) ``
EthGetMessageCidByTransactionHash func(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) ``
EthGetStorageAt func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) `` EthGetStorageAt func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) ``
EthGetTransactionByBlockHashAndIndex func(p0 context.Context, p1 ethtypes.EthHash, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) `` EthGetTransactionByBlockHashAndIndex func(p0 context.Context, p1 ethtypes.EthHash, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) ``
@ -665,6 +692,8 @@ type GatewayStruct struct {
EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) `` EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) ``
EthGetTransactionHashByCid func(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) ``
EthGetTransactionReceipt func(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) `` EthGetTransactionReceipt func(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) ``
EthMaxPriorityFeePerGas func(p0 context.Context) (ethtypes.EthBigInt, error) `` EthMaxPriorityFeePerGas func(p0 context.Context) (ethtypes.EthBigInt, error) ``
@ -679,7 +708,7 @@ type GatewayStruct struct {
EthSendRawTransaction func(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) `` EthSendRawTransaction func(p0 context.Context, p1 ethtypes.EthBytes) (ethtypes.EthHash, error) ``
EthSubscribe func(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) `` EthSubscribe func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) ``
EthUninstallFilter func(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) `` EthUninstallFilter func(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) ``
@ -736,14 +765,18 @@ type GatewayStruct struct {
Version func(p0 context.Context) (APIVersion, error) `` Version func(p0 context.Context) (APIVersion, error) ``
WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `` WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) ``
}
Web3ClientVersion func(p0 context.Context) (string, error) ``
} }
type GatewayStub struct { type GatewayStub struct {
} }
type NetStruct struct { type NetStruct struct {
Internal struct { Internal NetMethods
}
type NetMethods struct {
ID func(p0 context.Context) (peer.ID, error) `perm:"read"` ID func(p0 context.Context) (peer.ID, error) `perm:"read"`
NetAddrsListen func(p0 context.Context) (peer.AddrInfo, error) `perm:"read"` NetAddrsListen func(p0 context.Context) (peer.AddrInfo, error) `perm:"read"`
@ -791,16 +824,17 @@ type NetStruct struct {
NetSetLimit func(p0 context.Context, p1 string, p2 NetLimit) error `perm:"admin"` NetSetLimit func(p0 context.Context, p1 string, p2 NetLimit) error `perm:"admin"`
NetStat func(p0 context.Context, p1 string) (NetStat, error) `perm:"read"` NetStat func(p0 context.Context, p1 string) (NetStat, error) `perm:"read"`
}
} }
type NetStub struct { type NetStub struct {
} }
type SignableStruct struct { type SignableStruct struct {
Internal struct { Internal SignableMethods
}
type SignableMethods struct {
Sign func(p0 context.Context, p1 SignFunc) error `` Sign func(p0 context.Context, p1 SignFunc) error ``
}
} }
type SignableStub struct { type SignableStub struct {
@ -811,7 +845,10 @@ type StorageMinerStruct struct {
NetStruct NetStruct
Internal struct { Internal StorageMinerMethods
}
type StorageMinerMethods struct {
ActorAddress func(p0 context.Context) (address.Address, error) `perm:"read"` ActorAddress func(p0 context.Context) (address.Address, error) `perm:"read"`
ActorAddressConfig func(p0 context.Context) (AddressConfig, error) `perm:"read"` ActorAddressConfig func(p0 context.Context) (AddressConfig, error) `perm:"read"`
@ -1075,7 +1112,6 @@ type StorageMinerStruct struct {
WorkerJobs func(p0 context.Context) (map[uuid.UUID][]storiface.WorkerJob, error) `perm:"admin"` WorkerJobs func(p0 context.Context) (map[uuid.UUID][]storiface.WorkerJob, error) `perm:"admin"`
WorkerStats func(p0 context.Context) (map[uuid.UUID]storiface.WorkerStats, error) `perm:"admin"` WorkerStats func(p0 context.Context) (map[uuid.UUID]storiface.WorkerStats, error) `perm:"admin"`
}
} }
type StorageMinerStub struct { type StorageMinerStub struct {
@ -1085,7 +1121,10 @@ type StorageMinerStub struct {
} }
type WalletStruct struct { type WalletStruct struct {
Internal struct { Internal WalletMethods
}
type WalletMethods struct {
WalletDelete func(p0 context.Context, p1 address.Address) error `perm:"admin"` WalletDelete func(p0 context.Context, p1 address.Address) error `perm:"admin"`
WalletExport func(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) `perm:"admin"` WalletExport func(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) `perm:"admin"`
@ -1099,14 +1138,16 @@ type WalletStruct struct {
WalletNew func(p0 context.Context, p1 types.KeyType) (address.Address, error) `perm:"admin"` WalletNew func(p0 context.Context, p1 types.KeyType) (address.Address, error) `perm:"admin"`
WalletSign func(p0 context.Context, p1 address.Address, p2 []byte, p3 MsgMeta) (*crypto.Signature, error) `perm:"admin"` WalletSign func(p0 context.Context, p1 address.Address, p2 []byte, p3 MsgMeta) (*crypto.Signature, error) `perm:"admin"`
}
} }
type WalletStub struct { type WalletStub struct {
} }
type WorkerStruct struct { type WorkerStruct struct {
Internal struct { Internal WorkerMethods
}
type WorkerMethods struct {
AddPiece func(p0 context.Context, p1 storiface.SectorRef, p2 []abi.UnpaddedPieceSize, p3 abi.UnpaddedPieceSize, p4 storiface.Data) (storiface.CallID, error) `perm:"admin"` AddPiece func(p0 context.Context, p1 storiface.SectorRef, p2 []abi.UnpaddedPieceSize, p3 abi.UnpaddedPieceSize, p4 storiface.Data) (storiface.CallID, error) `perm:"admin"`
DataCid func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data) (storiface.CallID, error) `perm:"admin"` DataCid func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data) (storiface.CallID, error) `perm:"admin"`
@ -1180,7 +1221,6 @@ type WorkerStruct struct {
Version func(p0 context.Context) (Version, error) `perm:"admin"` Version func(p0 context.Context) (Version, error) `perm:"admin"`
WaitQuiet func(p0 context.Context) error `perm:"admin"` WaitQuiet func(p0 context.Context) error `perm:"admin"`
}
} }
type WorkerStub struct { type WorkerStub struct {
@ -1340,6 +1380,17 @@ func (s *CommonStub) Version(p0 context.Context) (APIVersion, error) {
return *new(APIVersion), ErrNotSupported return *new(APIVersion), ErrNotSupported
} }
func (s *EthSubscriberStruct) EthSubscription(p0 context.Context, p1 jsonrpc.RawParams) error {
if s.Internal.EthSubscription == nil {
return ErrNotSupported
}
return s.Internal.EthSubscription(p0, p1)
}
func (s *EthSubscriberStub) EthSubscription(p0 context.Context, p1 jsonrpc.RawParams) error {
return ErrNotSupported
}
func (s *FullNodeStruct) ChainBlockstoreInfo(p0 context.Context) (map[string]interface{}, error) { func (s *FullNodeStruct) ChainBlockstoreInfo(p0 context.Context) (map[string]interface{}, error) {
if s.Internal.ChainBlockstoreInfo == nil { if s.Internal.ChainBlockstoreInfo == nil {
return *new(map[string]interface{}), ErrNotSupported return *new(map[string]interface{}), ErrNotSupported
@ -2275,15 +2326,15 @@ func (s *FullNodeStub) EthSendRawTransaction(p0 context.Context, p1 ethtypes.Eth
return *new(ethtypes.EthHash), ErrNotSupported return *new(ethtypes.EthHash), ErrNotSupported
} }
func (s *FullNodeStruct) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { func (s *FullNodeStruct) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
if s.Internal.EthSubscribe == nil { if s.Internal.EthSubscribe == nil {
return nil, ErrNotSupported return *new(ethtypes.EthSubscriptionID), ErrNotSupported
} }
return s.Internal.EthSubscribe(p0, p1, p2) return s.Internal.EthSubscribe(p0, p1)
} }
func (s *FullNodeStub) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { func (s *FullNodeStub) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
return nil, ErrNotSupported return *new(ethtypes.EthSubscriptionID), ErrNotSupported
} }
func (s *FullNodeStruct) EthUninstallFilter(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) { func (s *FullNodeStruct) EthUninstallFilter(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) {
@ -3936,6 +3987,17 @@ func (s *FullNodeStub) WalletVerify(p0 context.Context, p1 address.Address, p2 [
return false, ErrNotSupported return false, ErrNotSupported
} }
func (s *FullNodeStruct) Web3ClientVersion(p0 context.Context) (string, error) {
if s.Internal.Web3ClientVersion == nil {
return "", ErrNotSupported
}
return s.Internal.Web3ClientVersion(p0)
}
func (s *FullNodeStub) Web3ClientVersion(p0 context.Context) (string, error) {
return "", ErrNotSupported
}
func (s *GatewayStruct) ChainGetBlockMessages(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) { func (s *GatewayStruct) ChainGetBlockMessages(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) {
if s.Internal.ChainGetBlockMessages == nil { if s.Internal.ChainGetBlockMessages == nil {
return nil, ErrNotSupported return nil, ErrNotSupported
@ -4277,6 +4339,17 @@ func (s *GatewayStub) EthGetLogs(p0 context.Context, p1 *ethtypes.EthFilterSpec)
return nil, ErrNotSupported return nil, ErrNotSupported
} }
func (s *GatewayStruct) EthGetMessageCidByTransactionHash(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) {
if s.Internal.EthGetMessageCidByTransactionHash == nil {
return nil, ErrNotSupported
}
return s.Internal.EthGetMessageCidByTransactionHash(p0, p1)
}
func (s *GatewayStub) EthGetMessageCidByTransactionHash(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) {
return nil, ErrNotSupported
}
func (s *GatewayStruct) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) { func (s *GatewayStruct) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) {
if s.Internal.EthGetStorageAt == nil { if s.Internal.EthGetStorageAt == nil {
return *new(ethtypes.EthBytes), ErrNotSupported return *new(ethtypes.EthBytes), ErrNotSupported
@ -4332,6 +4405,17 @@ func (s *GatewayStub) EthGetTransactionCount(p0 context.Context, p1 ethtypes.Eth
return *new(ethtypes.EthUint64), ErrNotSupported return *new(ethtypes.EthUint64), ErrNotSupported
} }
func (s *GatewayStruct) EthGetTransactionHashByCid(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) {
if s.Internal.EthGetTransactionHashByCid == nil {
return nil, ErrNotSupported
}
return s.Internal.EthGetTransactionHashByCid(p0, p1)
}
func (s *GatewayStub) EthGetTransactionHashByCid(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) {
return nil, ErrNotSupported
}
func (s *GatewayStruct) EthGetTransactionReceipt(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) { func (s *GatewayStruct) EthGetTransactionReceipt(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) {
if s.Internal.EthGetTransactionReceipt == nil { if s.Internal.EthGetTransactionReceipt == nil {
return nil, ErrNotSupported return nil, ErrNotSupported
@ -4409,15 +4493,15 @@ func (s *GatewayStub) EthSendRawTransaction(p0 context.Context, p1 ethtypes.EthB
return *new(ethtypes.EthHash), ErrNotSupported return *new(ethtypes.EthHash), ErrNotSupported
} }
func (s *GatewayStruct) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { func (s *GatewayStruct) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
if s.Internal.EthSubscribe == nil { if s.Internal.EthSubscribe == nil {
return nil, ErrNotSupported return *new(ethtypes.EthSubscriptionID), ErrNotSupported
} }
return s.Internal.EthSubscribe(p0, p1, p2) return s.Internal.EthSubscribe(p0, p1)
} }
func (s *GatewayStub) EthSubscribe(p0 context.Context, p1 string, p2 *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { func (s *GatewayStub) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
return nil, ErrNotSupported return *new(ethtypes.EthSubscriptionID), ErrNotSupported
} }
func (s *GatewayStruct) EthUninstallFilter(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) { func (s *GatewayStruct) EthUninstallFilter(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) {
@ -4728,6 +4812,17 @@ func (s *GatewayStub) WalletBalance(p0 context.Context, p1 address.Address) (typ
return *new(types.BigInt), ErrNotSupported return *new(types.BigInt), ErrNotSupported
} }
func (s *GatewayStruct) Web3ClientVersion(p0 context.Context) (string, error) {
if s.Internal.Web3ClientVersion == nil {
return "", ErrNotSupported
}
return s.Internal.Web3ClientVersion(p0)
}
func (s *GatewayStub) Web3ClientVersion(p0 context.Context) (string, error) {
return "", ErrNotSupported
}
func (s *NetStruct) ID(p0 context.Context) (peer.ID, error) { func (s *NetStruct) ID(p0 context.Context) (peer.ID, error) {
if s.Internal.ID == nil { if s.Internal.ID == nil {
return *new(peer.ID), ErrNotSupported return *new(peer.ID), ErrNotSupported
@ -6942,6 +7037,7 @@ func (s *WorkerStub) WaitQuiet(p0 context.Context) error {
var _ ChainIO = new(ChainIOStruct) var _ ChainIO = new(ChainIOStruct)
var _ Common = new(CommonStruct) var _ Common = new(CommonStruct)
var _ CommonNet = new(CommonNetStruct) var _ CommonNet = new(CommonNetStruct)
var _ EthSubscriber = new(EthSubscriberStruct)
var _ FullNode = new(FullNodeStruct) var _ FullNode = new(FullNodeStruct)
var _ Gateway = new(GatewayStruct) var _ Gateway = new(GatewayStruct)
var _ Net = new(NetStruct) var _ Net = new(NetStruct)

View File

@ -39,7 +39,10 @@ type FullNodeStruct struct {
NetStruct NetStruct
Internal struct { Internal FullNodeMethods
}
type FullNodeMethods struct {
BeaconGetEntry func(p0 context.Context, p1 abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"` BeaconGetEntry func(p0 context.Context, p1 abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"`
ChainDeleteObj func(p0 context.Context, p1 cid.Cid) error `perm:"admin"` ChainDeleteObj func(p0 context.Context, p1 cid.Cid) error `perm:"admin"`
@ -415,7 +418,6 @@ type FullNodeStruct struct {
WalletValidateAddress func(p0 context.Context, p1 string) (address.Address, error) `perm:"read"` WalletValidateAddress func(p0 context.Context, p1 string) (address.Address, error) `perm:"read"`
WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"` WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"`
}
} }
type FullNodeStub struct { type FullNodeStub struct {
@ -425,7 +427,10 @@ type FullNodeStub struct {
} }
type GatewayStruct struct { type GatewayStruct struct {
Internal struct { Internal GatewayMethods
}
type GatewayMethods struct {
ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*api.BlockMessages, error) `` ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*api.BlockMessages, error) ``
ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) `` ChainGetMessage func(p0 context.Context, p1 cid.Cid) (*types.Message, error) ``
@ -489,7 +494,6 @@ type GatewayStruct struct {
Version func(p0 context.Context) (api.APIVersion, error) `` Version func(p0 context.Context) (api.APIVersion, error) ``
WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `` WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) ``
}
} }
type GatewayStub struct { type GatewayStub struct {

Binary file not shown.

View File

@ -53,24 +53,24 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet
}, { }, {
Network: "butterflynet", Network: "butterflynet",
Version: 10, Version: 10,
ManifestCid: MustParseCid("bafy2bzaced2wq4k4i2deknam6ehbynaoo37bhysud7eze7su3ftlaggwwjuje"), ManifestCid: MustParseCid("bafy2bzacedsgi3wpyd46hbktrleolnlepzsm6k466fcrxuc7keht4guokgxiy"),
Actors: map[string]cid.Cid{ Actors: map[string]cid.Cid{
"account": MustParseCid("bafk2bzacebd5zetyjtragjwrv2nqktct6u2pmsi4eifbanovxohx3a7lszjxi"), "account": MustParseCid("bafk2bzacebz7dm3vcuqtzzcf5jwvcubz6ecqk52t5rsd36fyzka2iosy4l4ro"),
"cron": MustParseCid("bafk2bzacecrszortqkc7har77ssgajglymv6ftrqvmdko5h2yqqh5k2qospl2"), "cron": MustParseCid("bafk2bzacea6qtj2wkbe2wq5vxc2knlnj3vdvk4bkjp36xtazgzhnegd2uaj7m"),
"datacap": MustParseCid("bafk2bzacecapjnxnyw4talwqv5ajbtbkzmzqiosztj5cb3sortyp73ndjl76e"), "datacap": MustParseCid("bafk2bzacedwxkx3jz7qwv5iwozadz7t5hhw5dtlgdxuwqxdp6oqguas7nakjk"),
"eam": MustParseCid("bafk2bzacebsvtqzp7g7vpufbyqrwwcpuo2yu3y7kenm7auidyiwzcv6jdw724"), "eam": MustParseCid("bafk2bzacedoegh4idwvhjqahfnbqq6aqzgccgjwumudx6ihfut36ng57or7fi"),
"ethaccount": MustParseCid("bafk2bzacedl4pmkfxkzoqajs6im3ranmopozsmxjcxsnk3kwvd3vv7mfwwrf4"), "ethaccount": MustParseCid("bafk2bzacebn6l3x7d2i6lv72osbgcl4a37imexh5ou5kvbmj56taetwcyyhgq"),
"evm": MustParseCid("bafk2bzacedx5wdyaihi22pwqqqtfxmuwh5acem46mzaep3znmhh5bsuqmxogq"), "evm": MustParseCid("bafk2bzaced5gaxg5mz3hho473aszx5brgjriicqgrcbqctnyyn2e6vcxv3ule"),
"init": MustParseCid("bafk2bzacecbxp66q3ytjkg37nyv4rmzezbfaigvx4i5yhvqbm5gg4amjeaias"), "init": MustParseCid("bafk2bzacecbo6ggprh7sz3oy6uu5raykwngqmnjdsiijdrgp4glet3mb65ywo"),
"multisig": MustParseCid("bafk2bzacecjltag3mn75dsnmrmopjow27buxqhabissowayqlmavrcfetqswc"), "multisig": MustParseCid("bafk2bzacecmu3bhbg4rh5sqbagjlvrpb6ip5k3pngq22a33ok44yuhk75zenq"),
"paymentchannel": MustParseCid("bafk2bzacednzxg263eqbl2imwz3uhujov63tjkffieyl4hl3dhrgxyhwep6hc"), "paymentchannel": MustParseCid("bafk2bzacebth7fqe5xts6hbm7m6n733qcu6b6atd7ur6l7jhddferjgpxdy4s"),
"placeholder": MustParseCid("bafk2bzaceaamp2a35vpfml4skap4dffklzae2urcm34mtwwce2lvhaons3a5y"), "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"),
"reward": MustParseCid("bafk2bzacectp23cxsbbdrr3uggnw7f263qll5wkkfzqhn5yq37ae2ehdjdzri"), "reward": MustParseCid("bafk2bzaceds7hy7v77k2jsbkfob7b2qor6v5s2oancfxhkuuzwqqg6kxk27xe"),
"storagemarket": MustParseCid("bafk2bzacea45ko3ezkpeujsniovncwnizc4wsxd7kyckskhs7gvzwthzb2mqe"), "storagemarket": MustParseCid("bafk2bzacebqi6ylwfmack3hfzw6eej7r6gwlbxzo33tdkfkpof7wg7h54pjtw"),
"storageminer": MustParseCid("bafk2bzaced74qthwrl3gahcf7o3vrdrodbcqhlplh6fykbgy5sd2iyouhq44c"), "storageminer": MustParseCid("bafk2bzacedsxpkqqiycn5tjydycnlqer4544mpqvtwfamwyq6hwz7yjqd3iry"),
"storagepower": MustParseCid("bafk2bzaceduksv6wqthr5fgp7mx5prv6gzul2oozf3svrjbuggc4bgokdxgfy"), "storagepower": MustParseCid("bafk2bzacedssirrse7ufxti6capgf2qufb6y3oatv2fnnnh7xrgp47x3hfox4"),
"system": MustParseCid("bafk2bzacebe6j2ius6clbbr7dypsg54jzmn5xablzunph7ebedw6yhwla4cj2"), "system": MustParseCid("bafk2bzacea2lod7lxod72voxyik5btpzmpvduddr4hwshcsyyy257izh6kut4"),
"verifiedregistry": MustParseCid("bafk2bzacebu4joy25gneu2qv3qfm3ktakzalndjrbhekeqrqk3zhotv6nyy2g"), "verifiedregistry": MustParseCid("bafk2bzacebss7ol4ay6nkg7r3c2355aqpku4yvqipyh3sgdrxkhsrssrsaaig"),
}, },
}, { }, {
Network: "calibrationnet", Network: "calibrationnet",
@ -110,24 +110,24 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet
}, { }, {
Network: "calibrationnet", Network: "calibrationnet",
Version: 10, Version: 10,
ManifestCid: MustParseCid("bafy2bzacearpwvmcqlailxyq2d2wtzmtudxqhvfot77tbdqotek5qiq5hyhzg"), ManifestCid: MustParseCid("bafy2bzacec4ilfymf3sorrfxp67ruwbax3a4mbqzic63vy2xlfh3ulky3bxvs"),
Actors: map[string]cid.Cid{ Actors: map[string]cid.Cid{
"account": MustParseCid("bafk2bzacea7zmrdz2rjbzlbmrmx3ko6pm3cbyqxxgogiqldsccbqffuok7m6s"), "account": MustParseCid("bafk2bzacecupizfd6xbts7blvn3ozouy3f2gtehwl7qohjks54nsomtzs3aki"),
"cron": MustParseCid("bafk2bzacec7bxugi7ouh75nglycy7qwdq7e2hnku3w6yafq4fwdwvvq2mtrl2"), "cron": MustParseCid("bafk2bzacedry7eqweymdnybq5jm5slizm67v4ffhv7zqiw2jwevr7ijv25gjc"),
"datacap": MustParseCid("bafk2bzacedii4stmlo3ccdff7eevcolmgnuxy5ftkzbzwtkqa4iinlfzq4mei"), "datacap": MustParseCid("bafk2bzacebq6vigteuwchk7si6y45ogrfu2zpxjbo4a54btnbhp3rc3ifghx6"),
"eam": MustParseCid("bafk2bzacedykxiyewqijj5nksr7qi6o4wu5yz4rezb747ntql4rpidyfdpes4"), "eam": MustParseCid("bafk2bzacebounosssmuaz35xpyuupvijbcwqyaumbeztqmigbihfw2ysbnx4w"),
"ethaccount": MustParseCid("bafk2bzacecgbcbh3uk7olcfdqo44no5nxxayeqnycdznrlekqigbifor2revm"), "ethaccount": MustParseCid("bafk2bzacebi2ymbi5wo2o3rp2x6cqo55vroixngmpbdcs7el4rq4hvacyzsqy"),
"evm": MustParseCid("bafk2bzaceau5n66rabegik55kymni6uyk7n7jb5eymfywybs543yifpl7du2m"), "evm": MustParseCid("bafk2bzaceapklwjzdzkmnfprn5wsjdzjnueuw2ke4kixq46gnbwjncns4dleu"),
"init": MustParseCid("bafk2bzacea7lxnvgxupwwgoxlmwtrca75w73qabe324wnwx43qranbgf5zdqo"), "init": MustParseCid("bafk2bzaced7u4zpkxh5ecjo2emwsrk3vnickhmkxy22garqf766nbxcewymy6"),
"multisig": MustParseCid("bafk2bzacear5eu5gpbjlroqkmsgpqerzc4aemp2uqcaeq7s2h4ur4ucgpzesg"), "multisig": MustParseCid("bafk2bzacedlunqzd3mxslb7zej5fsti2jxredfhtcqqxepng67t4zfiv6lwlc"),
"paymentchannel": MustParseCid("bafk2bzacecwxuruxawcru7xfcx3rmt4hmhlfh4hi6jvfumerazz6jpvfmxxcw"), "paymentchannel": MustParseCid("bafk2bzacea4z2yi33rfiiutkmqko33fslikmeqgypkiam5cqpeylyp3oup552"),
"placeholder": MustParseCid("bafk2bzaceaamp2a35vpfml4skap4dffklzae2urcm34mtwwce2lvhaons3a5y"), "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"),
"reward": MustParseCid("bafk2bzacebk4syfvyk7kbxelk7ajo4vuxcc24k5ry52mvi3qtadlucy2vqlay"), "reward": MustParseCid("bafk2bzacea4dnvun5vwzunhgepejrknukx2di2kmo3x4akz6rollq5icsrl3m"),
"storagemarket": MustParseCid("bafk2bzaced2rfzwup3jlwovblx2y7q64w6mshbtn2nmampi4zfd3b4oplkp5c"), "storagemarket": MustParseCid("bafk2bzaceafoon3fsl756rbrih4upar3ayi6746gaj756bk56thncpotl4coa"),
"storageminer": MustParseCid("bafk2bzacecden66gfmmgylmr47myn4murqmbt3ycyxqayn54yzhcsda32rp3m"), "storageminer": MustParseCid("bafk2bzacea3dj37h74ue2jtief3bj2shxagigygcm2h6purgp42mr6swwfdiw"),
"storagepower": MustParseCid("bafk2bzacebxvco3shuhdnzjmmme3olbffdgpab7j3onfncksi762k3agjhzaa"), "storagepower": MustParseCid("bafk2bzacebmodckd4tustgfmeilcfi3ovd4wzxz2hnd6vyhkq7hgiojiy3cc6"),
"system": MustParseCid("bafk2bzacednnhpk5kno67bkomiohweglryqvgnqz4cbks6eomidai677fat5w"), "system": MustParseCid("bafk2bzacebpqirxha42noejsk5miv5kip44eay6lm63pxt26xhlwdmn7tnqaq"),
"verifiedregistry": MustParseCid("bafk2bzaceawecz24xbz7robn7ck7k2mprkewvup6q346whbfiybcrvy63qcsa"), "verifiedregistry": MustParseCid("bafk2bzaceczf7qrddwt5kh3gvro25wpls346tanffeatk7nsczjnwb7jtd454"),
}, },
}, { }, {
Network: "caterpillarnet", Network: "caterpillarnet",
@ -176,24 +176,24 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet
}, { }, {
Network: "caterpillarnet", Network: "caterpillarnet",
Version: 10, Version: 10,
ManifestCid: MustParseCid("bafy2bzacebxr4uvnf5g3373shjzbaca6pf4th6nnfubytjfbrlxcpvbjw4ane"), ManifestCid: MustParseCid("bafy2bzacec36gpvghhgjwa5ya3ocxg33pct2vddegeixgkpqsc6eiyajdjkii"),
Actors: map[string]cid.Cid{ Actors: map[string]cid.Cid{
"account": MustParseCid("bafk2bzacedfms6w3ghqtljpgsfuiqa6ztjx7kcuin6myjezj6rypj3zjbqms6"), "account": MustParseCid("bafk2bzacebcl3xlk7i6t5tau2rfgxft5pu6qzvjqxo6zs5guerz3xarxztyv6"),
"cron": MustParseCid("bafk2bzaceaganmlpozvy4jywigs46pfrtdmhjjey6uyhpurplqbasojsislba"), "cron": MustParseCid("bafk2bzacebhoqjvaxtzj3k4tz7c4vtt4or4u3h3jhwxlh3t4l6by2ota3s7by"),
"datacap": MustParseCid("bafk2bzacebafqqe3wv5ytkfwmqzbmchgem66pw6yq6rl7w6vlhqsbkxnisswq"), "datacap": MustParseCid("bafk2bzaceb7ttn3d43yb7l5ok5rjgr7325jb6ds4air7mivgoyzp5p4gwgrq4"),
"eam": MustParseCid("bafk2bzacedwk5eqczflcsuisqsyeomgkpg54olojjq2ieb2ozu5s45wfwluti"), "eam": MustParseCid("bafk2bzacebobuasaev75fge6xg6bekrdvnyox4h7iluupt4wqq2n4expha2oe"),
"ethaccount": MustParseCid("bafk2bzaceburkmtd63nmzxpux5rcxsbqr6x5didl2ce7al32g4tqrvo4pjz2i"), "ethaccount": MustParseCid("bafk2bzaceaghtv45mm6qx3yrxwy7zz7x7mqj4n4lzw4hx7zxzlij6dcxxuv4c"),
"evm": MustParseCid("bafk2bzacedbroioygjnbjtc7ykcjjs4wfbwnaa6gkzubi7c5enifoqqqu66s6"), "evm": MustParseCid("bafk2bzacecu7xpnpw27jquvnpfv4rseaal477ml4ouxy37eo7wymgfzkexllg"),
"init": MustParseCid("bafk2bzaced23r54kwuebl7t6mdantbby5qpfduxwxfryeliof2enyqzhokix6"), "init": MustParseCid("bafk2bzacea2rnkho4nliqvisiqgtqx66c4xneagpgj52tyqa64grxadggylbk"),
"multisig": MustParseCid("bafk2bzacebcn3rib6j6jvclys7dkf62hco45ssgamczkrtzt6xyewd6gt3mtu"), "multisig": MustParseCid("bafk2bzacebak6spthfa23cyqjmpgkgku4gg4egdn2zc6vkikbh5ongadzakma"),
"paymentchannel": MustParseCid("bafk2bzacecvas4leo44pqdguj22nnwqoqdgwajzrpm5d6ltkehc37ni6p6doq"), "paymentchannel": MustParseCid("bafk2bzaceb3tib72pwze2rov72ldwlfv3otes3tejgnfpbrzahwb5xi7slhqm"),
"placeholder": MustParseCid("bafk2bzaceaamp2a35vpfml4skap4dffklzae2urcm34mtwwce2lvhaons3a5y"), "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"),
"reward": MustParseCid("bafk2bzacebiizh4ohvv6p4uxjusoygex4wxcgvudqmdl2fsh6ft6s2zt4tz6q"), "reward": MustParseCid("bafk2bzaceak3n3orgdraub4bqiy3paio77hu4laaqv4vf7wmwv4ybl5ahgi5o"),
"storagemarket": MustParseCid("bafk2bzacedhkidshm7w2sqlw7izvaieyhkvmyhfsem6t6qfnkh7dnwqe56po2"), "storagemarket": MustParseCid("bafk2bzacearo7sog7yqbrwyws5o3lbsdsjf2cp5vsoxc4u3s5atgjtwzzh65s"),
"storageminer": MustParseCid("bafk2bzacedcmsibwfwhkp3sabmbyjmhqibyhjf3wwst7u5bkb2k6xpun3xevg"), "storageminer": MustParseCid("bafk2bzacecrzmjrbqjwknnkybdexspb6gfu4q6dvtaeguxl26yuytsjc3w7ws"),
"storagepower": MustParseCid("bafk2bzacecrgnpypxnxzgglhlitaallfee3dl4ejy3y63knl7llnwba4ycf7i"), "storagepower": MustParseCid("bafk2bzaceavlmlu4mt2u7xwnnzf6vwdmh2yo76aauujwlgsbfhafjgxb4zgtg"),
"system": MustParseCid("bafk2bzacecl7gizbe52xj6sfm5glubkhrdblmzuwlid6lxrwr5zhcmv4dl2ew"), "system": MustParseCid("bafk2bzacec35rgzpiaa4n3r5bzgssk33bhfgozjvgunbwax32dooqqokfe6ag"),
"verifiedregistry": MustParseCid("bafk2bzacebzndvdqtdck2y35smcxezldgh6nm6rbkj3g3fmiknsgg2uah235y"), "verifiedregistry": MustParseCid("bafk2bzacebjfkrzihgzlb2jecgm5seoqwf5e656zc22vjoyclioru6vdy2bnm"),
}, },
}, { }, {
Network: "devnet", Network: "devnet",
@ -233,24 +233,24 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet
}, { }, {
Network: "devnet", Network: "devnet",
Version: 10, Version: 10,
ManifestCid: MustParseCid("bafy2bzacebixrjysarwxdadewlllfp4rwfoejxstwdutghghei54uvuuxlsbq"), ManifestCid: MustParseCid("bafy2bzacebemt6rdgtsj5vhv2iimbdvm5g4xllgl7nugxvuuthsedncmfakww"),
Actors: map[string]cid.Cid{ Actors: map[string]cid.Cid{
"account": MustParseCid("bafk2bzacebb5txxkfexeaxa2th3rckxsxchzyss3ijgqbicf265h7rre2rvhm"), "account": MustParseCid("bafk2bzaceajmds6zbz235lbjjhv3mxc6x3kqudjkxedh5xqvlemh3f6xslz76"),
"cron": MustParseCid("bafk2bzacecotn4gwluhamoqwnzgbg7ogehv26o5xnhjzltnzfv6utrlyanzek"), "cron": MustParseCid("bafk2bzaceabbv5ej2yd3n7txl3s4pahknb4in5dok57hzwfheqnk6k67zegbk"),
"datacap": MustParseCid("bafk2bzacea4hket2srrtbewkf3tip6ellwpxdfbrzt5u47y57i2k6iojqqgba"), "datacap": MustParseCid("bafk2bzaceayzgq7qpuc5pr4lrh6k3xnvmirlmutffplmgur4pvcaynpxlkph6"),
"eam": MustParseCid("bafk2bzacecxm2gr6tevzzan6oqp6aiqydjm5b7eo34mlzo5jdm7mnlbbueikq"), "eam": MustParseCid("bafk2bzacecrand7mp7q3jm3u5dpqm4o24ki3pk3uzcw4zonjcowq4rxwptsis"),
"ethaccount": MustParseCid("bafk2bzacedh4y3zvtgft3i6ift4rpptgr2dx67pvenowvq7yaspuf25gqgcdc"), "ethaccount": MustParseCid("bafk2bzacecpwfxpvqiyiisbfw45v5ottcstxu2vifji3xswxt3jzk4vcrs4g4"),
"evm": MustParseCid("bafk2bzacec26myls7vg6anr5yjbb2r75dryhdzwlwnrhjcyuhahlaoxdrua6i"), "evm": MustParseCid("bafk2bzaceajrtntc5urxkwbzdu3khi2eqvarnfx6vh7luqt33gn6z4a4kjkj6"),
"init": MustParseCid("bafk2bzacedof2ckc6w2qboxzxv4w67njcug4ut4cq3nnlrfybzsvlgnp4kt24"), "init": MustParseCid("bafk2bzaced6npj5zrjb3lxhgtsq4st66dvde56nftbvchmpid3rcazfvnqkpk"),
"multisig": MustParseCid("bafk2bzacec4eqajjqhl53tnkbs7glu7njlbtlditi7lxhvw33ezmxk6jae46y"), "multisig": MustParseCid("bafk2bzacealhbd4slci4o76dpdurkkk3q5busopediwfh7uis4hfh7tzghzni"),
"paymentchannel": MustParseCid("bafk2bzacec6nvdprqja7dy3qp5islebbbh2ifiyg2p7arbe6pocjhfe6xwkfy"), "paymentchannel": MustParseCid("bafk2bzacebvpkvrihus53sdyutsjsbpfefe5gd2amfb6zkztdfp6g2m4ubqrk"),
"placeholder": MustParseCid("bafk2bzaceaamp2a35vpfml4skap4dffklzae2urcm34mtwwce2lvhaons3a5y"), "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"),
"reward": MustParseCid("bafk2bzacecqaoqksjotl4wwsqt2wf6kqv6s372afi3r5on4bqj3u3a44px2rm"), "reward": MustParseCid("bafk2bzaceaftaidppnno2dzhpxl5vyti5mcmdhvheieanwvptgacuj5ozzloe"),
"storagemarket": MustParseCid("bafk2bzaceb7yefqlzyoxkgoug5k4kizy63izrg5udartw5l4d6j53xjwdxbg4"), "storagemarket": MustParseCid("bafk2bzacea75td2k2cdwc2o4kotdods2thomhcoqg5rf62ty6gkuxojknziae"),
"storageminer": MustParseCid("bafk2bzaceagmuxcgdj65yuvtfrcup5viwkhhhlzslpdd4j6v6qxmhxtcssc6u"), "storageminer": MustParseCid("bafk2bzaceapj5q7egywl3zovwcm4hpbvr4vjtoshj57ncqg3srzseweyclvug"),
"storagepower": MustParseCid("bafk2bzacedt2qu6ykj3bjsfhchg2gxvc6asfb7c4tmranl76n4ojut5d6sgqm"), "storagepower": MustParseCid("bafk2bzacebbraebsoin6hhmr4na56st4gyg7yd7p2ry2igegnvws7deq32hec"),
"system": MustParseCid("bafk2bzacebp4ysxqv4cy633pgdxjlbwkwqkokc2fgez77y73abpt5hkthczn6"), "system": MustParseCid("bafk2bzacedtw3mq5zyxxbnybnjegqyrz3ufiboeoipyzynlk6zgyumvl3267g"),
"verifiedregistry": MustParseCid("bafk2bzaceb7odugx7meltvt2gra4vogn2g6avbgysivvdccldylusjcfsnfhy"), "verifiedregistry": MustParseCid("bafk2bzacecaqciqoky2z7win5rkzd3gkgpa3345adjyiidmg4swmw5celeb3a"),
}, },
}, { }, {
Network: "hyperspace", Network: "hyperspace",
@ -296,28 +296,6 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet
"system": MustParseCid("bafk2bzacedo2hfopt6gy52goj7fot5qwzhtnysmgo7h25crq4clpugkerjabk"), "system": MustParseCid("bafk2bzacedo2hfopt6gy52goj7fot5qwzhtnysmgo7h25crq4clpugkerjabk"),
"verifiedregistry": MustParseCid("bafk2bzacea7rfkjrixaidksnmjehglmavyt56nyeu3sfxu2e3dcpf62oab6tw"), "verifiedregistry": MustParseCid("bafk2bzacea7rfkjrixaidksnmjehglmavyt56nyeu3sfxu2e3dcpf62oab6tw"),
}, },
}, {
Network: "hyperspace",
Version: 10,
ManifestCid: MustParseCid("bafy2bzaced6hc7ujjmypg6mkrxdmf32oh2udhmhpmwkqyxusdkxoi2uoodyxg"),
Actors: map[string]cid.Cid{
"account": MustParseCid("bafk2bzacecim7uybic2qprbkjhowg7qkniv4zywj5h5g4u4ss72urco2akzuo"),
"cron": MustParseCid("bafk2bzaceahgq64awp4f7li3hdgimc4upqvdvltpmeywckvens33umcxt424a"),
"datacap": MustParseCid("bafk2bzacebkxn52ttooaslkwncijk3bgd3tm2zw7vijdhwvg2cxnxbrzmmq5e"),
"eam": MustParseCid("bafk2bzaceaftiqwpx6dcjfqxyq7pazn2p55diukf32pz74755vj7pgg5joexw"),
"ethaccount": MustParseCid("bafk2bzacealn5enbxyxbfs7gbsjbyma2zk3bcr7okvflxhpr753d4eh6ixooa"),
"evm": MustParseCid("bafk2bzacea6etsvrqejjl7uej5dxlswja5gxzqyggsjjvg27timvtiedf7nsg"),
"init": MustParseCid("bafk2bzacec55gyyaqjrw7zughywocgwcjvv6k5fijjpjw4xgckuqz6pjtff5a"),
"multisig": MustParseCid("bafk2bzaceblozbdzybdivvjdiid4jwm2jc6x5a66sunh2vvwsqba6wzqmr7i6"),
"paymentchannel": MustParseCid("bafk2bzacealcyke5a6n24efs6qe4iikynpk2twqssyugy7jcyf6p6shgw2iwa"),
"placeholder": MustParseCid("bafk2bzaceaamp2a35vpfml4skap4dffklzae2urcm34mtwwce2lvhaons3a5y"),
"reward": MustParseCid("bafk2bzacebafzaqhwsm3nmsfwcd6ngvx6ev6zlcpyfljqh4kb77vok6opban6"),
"storagemarket": MustParseCid("bafk2bzacecrjfg4p7fxznsdkoobs4po2ve3ywixrirrk6netgxh63qqaefamg"),
"storageminer": MustParseCid("bafk2bzaceb3ctd4atxwhdkmlg4i63zxo5aopknlj7l5kaiqr22xpcmico6vg4"),
"storagepower": MustParseCid("bafk2bzacecvcix3ugopvby2vah5wwiu5cqjedwzwkanmr34kdoc4f3o6p7nsq"),
"system": MustParseCid("bafk2bzacedo2hfopt6gy52goj7fot5qwzhtnysmgo7h25crq4clpugkerjabk"),
"verifiedregistry": MustParseCid("bafk2bzacea7rfkjrixaidksnmjehglmavyt56nyeu3sfxu2e3dcpf62oab6tw"),
},
}, { }, {
Network: "mainnet", Network: "mainnet",
Version: 8, Version: 8,
@ -356,24 +334,24 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet
}, { }, {
Network: "mainnet", Network: "mainnet",
Version: 10, Version: 10,
ManifestCid: MustParseCid("bafy2bzacea5vylkbby7rb42fknkk4g4byhj7hkqlxp4z4urydi3vlpwsgllik"), ManifestCid: MustParseCid("bafy2bzacec2ggeabyyl2cjaqmcpnyvjirrrm6bfc7d73q4pekm27hybzdqs3q"),
Actors: map[string]cid.Cid{ Actors: map[string]cid.Cid{
"account": MustParseCid("bafk2bzacedsn6i2flkpk6sb4iuejo7gfl5n6fhsdawggtbsihlrrjtvs7oepu"), "account": MustParseCid("bafk2bzacebdlwwnd57vd2444wrfe5amtf2f6htqj3hdh4fxblywdbynrurhgy"),
"cron": MustParseCid("bafk2bzacecw4guere7ba2canyi2622lw52b5qbn7iubckcp5cwlmx2kw7qqwy"), "cron": MustParseCid("bafk2bzacecxmaxh272zhgmayfg7btvq5lshv2cd7njkarlbbikba4otiaboyg"),
"datacap": MustParseCid("bafk2bzaceat2ncckd2jjjqcovd3ib4sylwff7jk7rlk6gr5d2gmrrc7isrmu2"), "datacap": MustParseCid("bafk2bzaceb64wicovvrjzaotvs64hmdtvolw4l6qanwp5tk56okzqbfttxck2"),
"eam": MustParseCid("bafk2bzacebbpu5smjrjqpkrvvlhcpk23yvlovlndqmwzhfz5kuuph54tdw732"), "eam": MustParseCid("bafk2bzacedxs56zywfumzcv7i5fwluku2qevg54cuiuwi5d3pavf3rilfu33g"),
"ethaccount": MustParseCid("bafk2bzacedmwzkbytxfn7exmxxosomvix4mpyxrmupeqw45aofqmdq5q7mgqe"), "ethaccount": MustParseCid("bafk2bzacecepsmgsucfebvbwf5vebm7j6zeqaz3ub52warxqobqeymh5vdjik"),
"evm": MustParseCid("bafk2bzacechkf43lmddynxmc35hvz5kwr3fdxrbg6fxbcvysfsihgiopbrb7o"), "evm": MustParseCid("bafk2bzacecf6arqbso67nmrhcsjvyradrbbv7hs2noand27fyr4nfilms4znu"),
"init": MustParseCid("bafk2bzacec6276d7ls3hhuqibqorn3yp45mv7hroczf3bgb6jkhmbb2zqt3bw"), "init": MustParseCid("bafk2bzaceat2xcem5lko5ot4mmrowtm6ehx5klw7c4ss4vxma55tyfvvxwlge"),
"multisig": MustParseCid("bafk2bzaceahggxrnjj3w3cgtko54srssqyhcs4x6y55ytego6jf2owg5piw3y"), "multisig": MustParseCid("bafk2bzacebz43omxi5vtkidhsxroqtgkpxtftdj6poew3744fayfftgdebe4y"),
"paymentchannel": MustParseCid("bafk2bzaceaobaqjamso57bkjv3n4ilv7lfropgrncnnej666w3tegmr4cfgve"), "paymentchannel": MustParseCid("bafk2bzaceds4ob3ev2ie2vorhfomddd44otqfau4d4eogfofjjbjjx2h27nh2"),
"placeholder": MustParseCid("bafk2bzaceaamp2a35vpfml4skap4dffklzae2urcm34mtwwce2lvhaons3a5y"), "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"),
"reward": MustParseCid("bafk2bzacecqet4s7abe4owznq2wtdefe2z2w5isbde2gj7u3hwgf54di4r7hy"), "reward": MustParseCid("bafk2bzaced7xvqx7n6426lls4ao54exn63pv73m7makxf7ygb575roqxhjuuw"),
"storagemarket": MustParseCid("bafk2bzacebgk2q2ktrqauzop6ha4pcq5gpf6g24hprxnp6wdmlzf724e5sx7i"), "storagemarket": MustParseCid("bafk2bzaceb5piewkvdj4ee6b4qzhimixjzee5z3hsuwdjksncpvef7sgaw6rw"),
"storageminer": MustParseCid("bafk2bzacecqrm4tlmzci7vilmcchr4lq2e6yyrlhy6ofbuecjna2phmbq4h2a"), "storageminer": MustParseCid("bafk2bzaceacukfushmnsqtdvtdyx2in6o2el7jq46qo7iaxgwytel4oz5srv4"),
"storagepower": MustParseCid("bafk2bzaceco674a5e5lpv5leui65bljxzgyc2ypdquaow55iuckmq5rvsghr6"), "storagepower": MustParseCid("bafk2bzacedi6z45jcms5guns4qxi6rs2e2prc6mpnhkr4klljrra3ayfburss"),
"system": MustParseCid("bafk2bzacedlt3zcsbw2vucbydptbcfudw5y5pkhhxe26m7pjod6rkxkuzn52w"), "system": MustParseCid("bafk2bzacedy7ssu2hez3nu7bi4j6ucojty4sfaublxlxhfd3tkgzyrm5sdxbq"),
"verifiedregistry": MustParseCid("bafk2bzacea2eehyf7h3m6ydh46piu2gtr4fawpqzh3brtmybgi2tyxf5nwj6m"), "verifiedregistry": MustParseCid("bafk2bzacecjgudirfyzyroq3xhf2bldl636w7prexcvo7v3xqdijzcom4rgry"),
}, },
}, { }, {
Network: "testing", Network: "testing",
@ -413,24 +391,24 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet
}, { }, {
Network: "testing", Network: "testing",
Version: 10, Version: 10,
ManifestCid: MustParseCid("bafy2bzacea7tbn4p232ecrjvlp2uvpci5pexqjqq2vpv4t5ihktpja2zsj3ek"), ManifestCid: MustParseCid("bafy2bzacedhivj4zbumou6d3242p3ecqhlqfcjfskdv46uzjchlj3ve23xyoa"),
Actors: map[string]cid.Cid{ Actors: map[string]cid.Cid{
"account": MustParseCid("bafk2bzaceds3iy5qjgr3stoywxt4uxvhybca23q7d2kxhitedgudrkhxaxa6o"), "account": MustParseCid("bafk2bzacea3vbptmow72euwh2meu2sgwxrxjxg53qba6xxrknltr6j7bgnlhg"),
"cron": MustParseCid("bafk2bzacebxp4whb4ocqxnbvqlz3kckarabtyvhjbhqvrdwhejuffwactyiss"), "cron": MustParseCid("bafk2bzaceclbrnwfgolv5icdknexv3oi3ujzpt5stqabiyshwhtvnijacysjg"),
"datacap": MustParseCid("bafk2bzacedepm3zas6vqryruwiz7d3axkneo7v66q65gf2dlpfd53pjlycrg4"), "datacap": MustParseCid("bafk2bzacebt2gym3ot447unemieakxfep3u2m2zxiqlssbacu3ifsyhtulz4m"),
"eam": MustParseCid("bafk2bzacea2uascrtv6xnsqlxyf3tcf4onpgrs7frh55p6dnrdeum2uup7wx4"), "eam": MustParseCid("bafk2bzacedmnvhfvt7qc5w3mfr54ikrpwox54ddkxkxp5qka24xudj4vkggjs"),
"ethaccount": MustParseCid("bafk2bzacecbhz4ipg773lsovgpjysm6fxl2i7y2wuxadqnt4s4vm3nd2qodb4"), "ethaccount": MustParseCid("bafk2bzacebnh3oadihryhwgo73ooesgk3x2eg4g5gorps463iirilm5ur4q7w"),
"evm": MustParseCid("bafk2bzaceabwn4i62od3i4qkuj5zx4vn5w5cbcl53tqnszk6kl43bfl55hl6c"), "evm": MustParseCid("bafk2bzacecw2i5bsjymtdblvxh5xte3htff4przqaek673cw5z7ommaptdmqq"),
"init": MustParseCid("bafk2bzacebqym5i5eciyyyzsimu73z6bkffpm5hzjpx3gwcm64pm2fh7okrja"), "init": MustParseCid("bafk2bzacebo6n4pwpwayjsc7cbrmmjy6l6om3wzx5jdldni4wl47a4x4jeazo"),
"multisig": MustParseCid("bafk2bzacecmlyngek7qvj5ezaaitadrycapup3mbty4ijlzun6g23tcoysxle"), "multisig": MustParseCid("bafk2bzacecl4mc5esjwfcoirhdeqhms4qquafam4ut424hj2mo3gqzb47n2rs"),
"paymentchannel": MustParseCid("bafk2bzacedspin4hxpgnxkjen3hsxpcc52oc5q4ypukl4qq6vaytcgmmi7hl4"), "paymentchannel": MustParseCid("bafk2bzacedsmvdirjuywbg5xz7r5u2pxew7ye4kpy2toksv5nba7dzkcsmu3i"),
"placeholder": MustParseCid("bafk2bzaceaamp2a35vpfml4skap4dffklzae2urcm34mtwwce2lvhaons3a5y"), "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"),
"reward": MustParseCid("bafk2bzacecmumnnqkqnoa23hhsfgwccwvmksr2q65tznbves6x2a6fhwvtm7a"), "reward": MustParseCid("bafk2bzaceakq4np44ltnscgff7h3a6s6ao2d43vwx66tce5r57r2amw42pl5i"),
"storagemarket": MustParseCid("bafk2bzacea2re4nxba7mtlrwdxabu2i3l2fwbuw2veb4p7qbvrsaocgablqvi"), "storagemarket": MustParseCid("bafk2bzacebskzlyhvhrdheslyrez3p4sccr5t42xnqophnvj775roskwzoic4"),
"storageminer": MustParseCid("bafk2bzacecixm7d7d5ltsp6mubzw5s3fv335cjuwwy7oqovujn3xlyk6twivs"), "storageminer": MustParseCid("bafk2bzacecx2fs3ra4ydxvwq6oh73esqy2xjqhwsnfrdl5ctbg26zem77zy3u"),
"storagepower": MustParseCid("bafk2bzaced5lqpftacjsflcgfwlm32gzckpi3ndj3kd3prtqqi2lfj3uhl2je"), "storagepower": MustParseCid("bafk2bzacedwfnzestwv7ylleeuk3fhp6jewc4ygw3fgodsciww7gw5ilt4ony"),
"system": MustParseCid("bafk2bzaceaafqf7lwaiqx5po6b3l4dfg4xsr5qhfk3bjgoi7qke2mfy3shla4"), "system": MustParseCid("bafk2bzaceaql3e6266ixcbwcdmwuhod4tahhawlvhfkq4qzp7hnmkkybdf7zi"),
"verifiedregistry": MustParseCid("bafk2bzacec2ouguts4z335vetmdeifpk5fkqthcmrwshk7yxbw2uohddfu5lo"), "verifiedregistry": MustParseCid("bafk2bzacecibid6xpyu64kaxk2mspouajnenxlh4jkny7d6l5ht3hxg67l32u"),
}, },
}, { }, {
Network: "testing-fake-proofs", Network: "testing-fake-proofs",
@ -470,23 +448,23 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet
}, { }, {
Network: "testing-fake-proofs", Network: "testing-fake-proofs",
Version: 10, Version: 10,
ManifestCid: MustParseCid("bafy2bzacecyqfyzmw72234rvbk6vzq2omnmt3cbfezkq2h3ewnn33w42b2s62"), ManifestCid: MustParseCid("bafy2bzaceav36pezxhapk6vlgohdp6jiydk44o6xowltjnyhu3nrhpfcby5zs"),
Actors: map[string]cid.Cid{ Actors: map[string]cid.Cid{
"account": MustParseCid("bafk2bzaceds3iy5qjgr3stoywxt4uxvhybca23q7d2kxhitedgudrkhxaxa6o"), "account": MustParseCid("bafk2bzacea3vbptmow72euwh2meu2sgwxrxjxg53qba6xxrknltr6j7bgnlhg"),
"cron": MustParseCid("bafk2bzacebxp4whb4ocqxnbvqlz3kckarabtyvhjbhqvrdwhejuffwactyiss"), "cron": MustParseCid("bafk2bzaceclbrnwfgolv5icdknexv3oi3ujzpt5stqabiyshwhtvnijacysjg"),
"datacap": MustParseCid("bafk2bzacedepm3zas6vqryruwiz7d3axkneo7v66q65gf2dlpfd53pjlycrg4"), "datacap": MustParseCid("bafk2bzacebt2gym3ot447unemieakxfep3u2m2zxiqlssbacu3ifsyhtulz4m"),
"eam": MustParseCid("bafk2bzacea2uascrtv6xnsqlxyf3tcf4onpgrs7frh55p6dnrdeum2uup7wx4"), "eam": MustParseCid("bafk2bzacedmnvhfvt7qc5w3mfr54ikrpwox54ddkxkxp5qka24xudj4vkggjs"),
"ethaccount": MustParseCid("bafk2bzacecbhz4ipg773lsovgpjysm6fxl2i7y2wuxadqnt4s4vm3nd2qodb4"), "ethaccount": MustParseCid("bafk2bzacebnh3oadihryhwgo73ooesgk3x2eg4g5gorps463iirilm5ur4q7w"),
"evm": MustParseCid("bafk2bzaceabwn4i62od3i4qkuj5zx4vn5w5cbcl53tqnszk6kl43bfl55hl6c"), "evm": MustParseCid("bafk2bzacecw2i5bsjymtdblvxh5xte3htff4przqaek673cw5z7ommaptdmqq"),
"init": MustParseCid("bafk2bzacebqym5i5eciyyyzsimu73z6bkffpm5hzjpx3gwcm64pm2fh7okrja"), "init": MustParseCid("bafk2bzacebo6n4pwpwayjsc7cbrmmjy6l6om3wzx5jdldni4wl47a4x4jeazo"),
"multisig": MustParseCid("bafk2bzacecmlyngek7qvj5ezaaitadrycapup3mbty4ijlzun6g23tcoysxle"), "multisig": MustParseCid("bafk2bzacecl4mc5esjwfcoirhdeqhms4qquafam4ut424hj2mo3gqzb47n2rs"),
"paymentchannel": MustParseCid("bafk2bzacedspin4hxpgnxkjen3hsxpcc52oc5q4ypukl4qq6vaytcgmmi7hl4"), "paymentchannel": MustParseCid("bafk2bzacedsmvdirjuywbg5xz7r5u2pxew7ye4kpy2toksv5nba7dzkcsmu3i"),
"placeholder": MustParseCid("bafk2bzaceaamp2a35vpfml4skap4dffklzae2urcm34mtwwce2lvhaons3a5y"), "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"),
"reward": MustParseCid("bafk2bzacecmumnnqkqnoa23hhsfgwccwvmksr2q65tznbves6x2a6fhwvtm7a"), "reward": MustParseCid("bafk2bzaceakq4np44ltnscgff7h3a6s6ao2d43vwx66tce5r57r2amw42pl5i"),
"storagemarket": MustParseCid("bafk2bzacea2re4nxba7mtlrwdxabu2i3l2fwbuw2veb4p7qbvrsaocgablqvi"), "storagemarket": MustParseCid("bafk2bzacebskzlyhvhrdheslyrez3p4sccr5t42xnqophnvj775roskwzoic4"),
"storageminer": MustParseCid("bafk2bzacedz4mmupganqbwe6mz4636zepooh5ipxb36tybsrf6ynewrfdihl6"), "storageminer": MustParseCid("bafk2bzacebp3rj6d4g2ppngw2xp7okzqx6oapfk6xi54n3aqenadqvptlk45g"),
"storagepower": MustParseCid("bafk2bzacedcqv6k2fszpfb7zpw6q6c6fe2u7g2zefabcntp46xgv3owosgymy"), "storagepower": MustParseCid("bafk2bzacedhwtksxb6orm63doxx2bgcy6fpy5li5prjb3twsxdh75anjbmdug"),
"system": MustParseCid("bafk2bzaceaafqf7lwaiqx5po6b3l4dfg4xsr5qhfk3bjgoi7qke2mfy3shla4"), "system": MustParseCid("bafk2bzaceaql3e6266ixcbwcdmwuhod4tahhawlvhfkq4qzp7hnmkkybdf7zi"),
"verifiedregistry": MustParseCid("bafk2bzacec2ouguts4z335vetmdeifpk5fkqthcmrwshk7yxbw2uohddfu5lo"), "verifiedregistry": MustParseCid("bafk2bzacecibid6xpyu64kaxk2mspouajnenxlh4jkny7d6l5ht3hxg67l32u"),
}, },
}} }}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -23,7 +23,7 @@ var NetworkBundle = "devnet"
var BundleOverrides map[actorstypes.Version]string var BundleOverrides map[actorstypes.Version]string
var ActorDebugging = true var ActorDebugging = true
const GenesisNetworkVersion = network.Version18 const GenesisNetworkVersion = network.Version17
var UpgradeBreezeHeight = abi.ChainEpoch(-1) var UpgradeBreezeHeight = abi.ChainEpoch(-1)
@ -59,7 +59,7 @@ var UpgradeSkyrHeight = abi.ChainEpoch(-19)
var UpgradeSharkHeight = abi.ChainEpoch(-20) var UpgradeSharkHeight = abi.ChainEpoch(-20)
var UpgradeHyggeHeight = abi.ChainEpoch(-21) var UpgradeHyggeHeight = abi.ChainEpoch(30)
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{ var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
0: DrandMainnet, 0: DrandMainnet,

View File

@ -26,6 +26,7 @@ import (
var SystemActorAddr = builtin.SystemActorAddr var SystemActorAddr = builtin.SystemActorAddr
var BurntFundsActorAddr = builtin.BurntFundsActorAddr var BurntFundsActorAddr = builtin.BurntFundsActorAddr
var CronActorAddr = builtin.CronActorAddr var CronActorAddr = builtin.CronActorAddr
var EthereumAddressManagerActorAddr = builtin.EthereumAddressManagerActorAddr
var SaftAddress = makeAddress("t0122") var SaftAddress = makeAddress("t0122")
var ReserveAddress = makeAddress("t090") var ReserveAddress = makeAddress("t090")
var RootVerifierAddress = makeAddress("t080") var RootVerifierAddress = makeAddress("t080")

View File

@ -26,6 +26,7 @@ import (
var SystemActorAddr = builtin.SystemActorAddr var SystemActorAddr = builtin.SystemActorAddr
var BurntFundsActorAddr = builtin.BurntFundsActorAddr var BurntFundsActorAddr = builtin.BurntFundsActorAddr
var CronActorAddr = builtin.CronActorAddr var CronActorAddr = builtin.CronActorAddr
var EthereumAddressManagerActorAddr = builtin.EthereumAddressManagerActorAddr
var SaftAddress = makeAddress("t0122") var SaftAddress = makeAddress("t0122")
var ReserveAddress = makeAddress("t090") var ReserveAddress = makeAddress("t090")
var RootVerifierAddress = makeAddress("t080") var RootVerifierAddress = makeAddress("t080")

View File

@ -5,11 +5,13 @@ import (
"sync/atomic" "sync/atomic"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
cbor "github.com/ipfs/go-ipld-cbor"
cbg "github.com/whyrusleeping/cbor-gen" cbg "github.com/whyrusleeping/cbor-gen"
"go.opencensus.io/stats" "go.opencensus.io/stats"
"go.opencensus.io/trace" "go.opencensus.io/trace"
"golang.org/x/xerrors" "golang.org/x/xerrors"
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"
actorstypes "github.com/filecoin-project/go-state-types/actors" actorstypes "github.com/filecoin-project/go-state-types/actors"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
@ -105,6 +107,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context,
LookbackState: stmgr.LookbackStateGetterForTipset(sm, ts), LookbackState: stmgr.LookbackStateGetterForTipset(sm, ts),
TipSetGetter: stmgr.TipSetGetterForTipset(sm.ChainStore(), ts), TipSetGetter: stmgr.TipSetGetterForTipset(sm.ChainStore(), ts),
Tracing: vmTracing, Tracing: vmTracing,
ReturnEvents: sm.ChainStore().IsStoringEvents(),
} }
return sm.VMConstructor()(ctx, vmopt) return sm.VMConstructor()(ctx, vmopt)
@ -174,8 +177,13 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context,
return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err) return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err)
} }
var receipts []cbg.CBORMarshaler var (
processedMsgs := make(map[cid.Cid]struct{}) receipts []*types.MessageReceipt
storingEvents = sm.ChainStore().IsStoringEvents()
events [][]types.Event
processedMsgs = make(map[cid.Cid]struct{})
)
for _, b := range bms { for _, b := range bms {
penalty := types.NewInt(0) penalty := types.NewInt(0)
gasReward := big.Zero() gasReward := big.Zero()
@ -194,6 +202,11 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context,
gasReward = big.Add(gasReward, r.GasCosts.MinerTip) gasReward = big.Add(gasReward, r.GasCosts.MinerTip)
penalty = big.Add(penalty, r.GasCosts.MinerPenalty) penalty = big.Add(penalty, r.GasCosts.MinerPenalty)
if storingEvents {
// Appends nil when no events are returned to preserve positional alignment.
events = append(events, r.Events)
}
if em != nil { if em != nil {
if err := em.MessageApplied(ctx, ts, cm.Cid(), m, r, false); err != nil { if err := em.MessageApplied(ctx, ts, cm.Cid(), m, r, false); err != nil {
return cid.Undef, cid.Undef, err return cid.Undef, cid.Undef, err
@ -235,6 +248,23 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context,
return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err) return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err)
} }
// Slice will be empty if not storing events.
for i, evs := range events {
if len(evs) == 0 {
continue
}
switch root, err := t.StoreEventsAMT(ctx, sm.ChainStore(), evs); {
case err != nil:
return cid.Undef, cid.Undef, xerrors.Errorf("failed to store events amt: %w", err)
case i >= len(receipts):
return cid.Undef, cid.Undef, xerrors.Errorf("assertion failed: receipt and events array lengths inconsistent")
case receipts[i].EventsRoot == nil:
return cid.Undef, cid.Undef, xerrors.Errorf("assertion failed: VM returned events with no events root")
case root != *receipts[i].EventsRoot:
return cid.Undef, cid.Undef, xerrors.Errorf("assertion failed: returned events AMT root does not match derived")
}
}
st, err := vmi.Flush(ctx) st, err := vmi.Flush(ctx)
if err != nil { if err != nil {
return cid.Undef, cid.Undef, xerrors.Errorf("vm flush failed: %w", err) return cid.Undef, cid.Undef, xerrors.Errorf("vm flush failed: %w", err)
@ -293,4 +323,13 @@ func (t *TipSetExecutor) ExecuteTipSet(ctx context.Context,
return t.ApplyBlocks(ctx, sm, parentEpoch, pstate, fbmsgs, blks[0].Height, r, em, vmTracing, baseFee, ts) return t.ApplyBlocks(ctx, sm, parentEpoch, pstate, fbmsgs, blks[0].Height, r, em, vmTracing, baseFee, ts)
} }
func (t *TipSetExecutor) StoreEventsAMT(ctx context.Context, cs *store.ChainStore, events []types.Event) (cid.Cid, error) {
cst := cbor.NewCborStore(cs.ChainBlockstore())
objs := make([]cbg.CBORMarshaler, len(events))
for i := 0; i < len(events); i++ {
objs[i] = &events[i]
}
return amt4.FromArray(ctx, cst, objs, amt4.UseTreeBitWidth(types.EventAMTBitwidth))
}
var _ stmgr.Executor = &TipSetExecutor{} var _ stmgr.Executor = &TipSetExecutor{}

View File

@ -22,7 +22,7 @@ func AuthenticateMessage(msg *types.SignedMessage, signer address.Address) error
typ := msg.Signature.Type typ := msg.Signature.Type
switch typ { switch typ {
case crypto.SigTypeDelegated: case crypto.SigTypeDelegated:
txArgs, err := ethtypes.EthTxArgsFromMessage(&msg.Message) txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(&msg.Message)
if err != nil { if err != nil {
return xerrors.Errorf("failed to reconstruct eth transaction: %w", err) return xerrors.Errorf("failed to reconstruct eth transaction: %w", err)
} }

View File

@ -67,34 +67,28 @@ func (ei *EthTxHashLookup) UpsertHash(txHash ethtypes.EthHash, c cid.Cid) error
} }
func (ei *EthTxHashLookup) GetCidFromHash(txHash ethtypes.EthHash) (cid.Cid, error) { func (ei *EthTxHashLookup) GetCidFromHash(txHash ethtypes.EthHash) (cid.Cid, error) {
q, err := ei.db.Query("SELECT cid FROM eth_tx_hashes WHERE hash = :hash;", sql.Named("hash", txHash.String())) row := ei.db.QueryRow("SELECT cid FROM eth_tx_hashes WHERE hash = :hash;", sql.Named("hash", txHash.String()))
if err != nil {
return cid.Undef, err
}
var c string var c string
if !q.Next() { err := row.Scan(&c)
if err != nil {
if err == sql.ErrNoRows {
return cid.Undef, ErrNotFound return cid.Undef, ErrNotFound
} }
err = q.Scan(&c)
if err != nil {
return cid.Undef, err return cid.Undef, err
} }
return cid.Decode(c) return cid.Decode(c)
} }
func (ei *EthTxHashLookup) GetHashFromCid(c cid.Cid) (ethtypes.EthHash, error) { func (ei *EthTxHashLookup) GetHashFromCid(c cid.Cid) (ethtypes.EthHash, error) {
q, err := ei.db.Query("SELECT hash FROM eth_tx_hashes WHERE cid = :cid;", sql.Named("cid", c.String())) row := ei.db.QueryRow("SELECT hash FROM eth_tx_hashes WHERE cid = :cid;", sql.Named("cid", c.String()))
if err != nil {
return ethtypes.EmptyEthHash, err
}
var hashString string var hashString string
if !q.Next() { err := row.Scan(&c)
if err != nil {
if err == sql.ErrNoRows {
return ethtypes.EmptyEthHash, ErrNotFound return ethtypes.EmptyEthHash, ErrNotFound
} }
err = q.Scan(&hashString)
if err != nil {
return ethtypes.EmptyEthHash, err return ethtypes.EmptyEthHash, err
} }
return ethtypes.ParseEthHash(hashString) return ethtypes.ParseEthHash(hashString)

View File

@ -20,7 +20,12 @@ import (
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
) )
const indexed uint8 = 0x01 func isIndexedValue(b uint8) bool {
// currently we mark the full entry as indexed if either the key
// or the value are indexed; in the future we will need finer-grained
// management of indices
return b&(types.EventFlagIndexedKey|types.EventFlagIndexedValue) > 0
}
type EventFilter struct { type EventFilter struct {
id types.FilterID id types.FilterID
@ -100,18 +105,18 @@ func (f *EventFilter) CollectEvents(ctx context.Context, te *TipSetEvents, rever
continue continue
} }
decodedEntries := make([]types.EventEntry, len(ev.Entries)) entries := make([]types.EventEntry, len(ev.Entries))
for i, entry := range ev.Entries { for i, entry := range ev.Entries {
decodedEntries[i] = types.EventEntry{ entries[i] = types.EventEntry{
Flags: entry.Flags, Flags: entry.Flags,
Key: entry.Key, Key: entry.Key,
Value: decodeLogBytes(entry.Value), Value: entry.Value,
} }
} }
// event matches filter, so record it // event matches filter, so record it
cev := &CollectedEvent{ cev := &CollectedEvent{
Entries: decodedEntries, Entries: entries,
EmitterAddr: addr, EmitterAddr: addr,
EventIdx: evIdx, EventIdx: evIdx,
Reverted: revert, Reverted: revert,
@ -209,7 +214,7 @@ func (f *EventFilter) matchKeys(ees []types.EventEntry) bool {
matched := map[string]bool{} matched := map[string]bool{}
for _, ee := range ees { for _, ee := range ees {
// Skip an entry that is not indexable // Skip an entry that is not indexable
if ee.Flags&indexed != indexed { if !isIndexedValue(ee.Flags) {
continue continue
} }
@ -221,7 +226,7 @@ func (f *EventFilter) matchKeys(ees []types.EventEntry) bool {
} }
wantlist, ok := f.keys[keyname] wantlist, ok := f.keys[keyname]
if !ok { if !ok || len(wantlist) == 0 {
continue continue
} }

View File

@ -1,7 +1,6 @@
package filter package filter
import ( import (
"bytes"
"context" "context"
"database/sql" "database/sql"
"errors" "errors"
@ -11,7 +10,6 @@ import (
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
cbg "github.com/whyrusleeping/cbor-gen"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
@ -153,13 +151,6 @@ func (ei *EventIndex) CollectEvents(ctx context.Context, te *TipSetEvents, rever
return xerrors.Errorf("prepare insert entry: %w", err) return xerrors.Errorf("prepare insert entry: %w", err)
} }
isIndexedValue := func(b uint8) bool {
// currently we mark the full entry as indexed if either the key
// or the value are indexed; in the future we will need finer-grained
// management of indices
return b&(types.EventFlagIndexedKey|types.EventFlagIndexedValue) > 0
}
for msgIdx, em := range ems { for msgIdx, em := range ems {
for evIdx, ev := range em.Events() { for evIdx, ev := range em.Events() {
addr, found := addressLookups[ev.Emitter] addr, found := addressLookups[ev.Emitter]
@ -198,13 +189,12 @@ func (ei *EventIndex) CollectEvents(ctx context.Context, te *TipSetEvents, rever
} }
for _, entry := range ev.Entries { for _, entry := range ev.Entries {
value := decodeLogBytes(entry.Value)
_, err := stmtEntry.Exec( _, err := stmtEntry.Exec(
lastID, // event_id lastID, // event_id
isIndexedValue(entry.Flags), // indexed isIndexedValue(entry.Flags), // indexed
[]byte{entry.Flags}, // flags []byte{entry.Flags}, // flags
entry.Key, // key entry.Key, // key
value, // value entry.Value, // value
) )
if err != nil { if err != nil {
return xerrors.Errorf("exec insert entry: %w", err) return xerrors.Errorf("exec insert entry: %w", err)
@ -220,21 +210,6 @@ func (ei *EventIndex) CollectEvents(ctx context.Context, te *TipSetEvents, rever
return nil return nil
} }
// decodeLogBytes decodes a CBOR-serialized array into its original form.
//
// This function swallows errors and returns the original array if it failed
// to decode.
func decodeLogBytes(orig []byte) []byte {
if len(orig) == 0 {
return orig
}
decoded, err := cbg.ReadByteArray(bytes.NewReader(orig), uint64(len(orig)))
if err != nil {
return orig
}
return decoded
}
// PrefillFilter fills a filter's collection of events from the historic index // PrefillFilter fills a filter's collection of events from the historic index
func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter) error { func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter) error {
clauses := []string{} clauses := []string{}

View File

@ -202,7 +202,7 @@ func (ms *MessageSigner) dstoreKey(addr address.Address) datastore.Key {
func SigningBytes(msg *types.Message, sigType crypto.SigType) ([]byte, error) { func SigningBytes(msg *types.Message, sigType crypto.SigType) ([]byte, error) {
if sigType == crypto.SigTypeDelegated { if sigType == crypto.SigTypeDelegated {
txArgs, err := ethtypes.EthTxArgsFromMessage(msg) txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(msg)
if err != nil { if err != nil {
return nil, xerrors.Errorf("failed to reconstruct eth transaction: %w", err) return nil, xerrors.Errorf("failed to reconstruct eth transaction: %w", err)
} }

View File

@ -403,7 +403,8 @@ func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.Cha
a == builtin.CronActorAddr || a == builtin.CronActorAddr ||
a == builtin.BurntFundsActorAddr || a == builtin.BurntFundsActorAddr ||
a == builtin.SaftAddress || a == builtin.SaftAddress ||
a == builtin.ReserveAddress: a == builtin.ReserveAddress ||
a == builtin.EthereumAddressManagerActorAddr:
unCirc = big.Add(unCirc, actor.Balance) unCirc = big.Add(unCirc, actor.Balance)
@ -421,7 +422,12 @@ func (sm *StateManager) GetCirculatingSupply(ctx context.Context, height abi.Cha
circ = big.Add(circ, big.Sub(actor.Balance, lb)) circ = big.Add(circ, big.Sub(actor.Balance, lb))
unCirc = big.Add(unCirc, lb) unCirc = big.Add(unCirc, lb)
case builtin.IsAccountActor(actor.Code) || builtin.IsPaymentChannelActor(actor.Code): case builtin.IsAccountActor(actor.Code) ||
builtin.IsPaymentChannelActor(actor.Code) ||
builtin.IsEthAccountActor(actor.Code) ||
builtin.IsEvmActor(actor.Code) ||
builtin.IsPlaceholderActor(actor.Code):
circ = big.Add(circ, actor.Balance) circ = big.Add(circ, actor.Balance)
case builtin.IsStorageMinerActor(actor.Code): case builtin.IsStorageMinerActor(actor.Code):

View File

@ -126,6 +126,8 @@ type ChainStore struct {
evtTypes [1]journal.EventType evtTypes [1]journal.EventType
journal journal.Journal journal journal.Journal
storeEvents bool
cancelFn context.CancelFunc cancelFn context.CancelFunc
wg sync.WaitGroup wg sync.WaitGroup
} }
@ -680,7 +682,7 @@ func FlushValidationCache(ctx context.Context, ds dstore.Batching) error {
// If this is addressed (blockcache goes into its own sub-namespace) then // If this is addressed (blockcache goes into its own sub-namespace) then
// strings.HasPrefix(...) below can be skipped // strings.HasPrefix(...) below can be skipped
// //
//Prefix: blockValidationCacheKeyPrefix.String() // Prefix: blockValidationCacheKeyPrefix.String()
KeysOnly: true, KeysOnly: true,
}) })
if err != nil { if err != nil {
@ -1202,6 +1204,16 @@ func (cs *ChainStore) Weight(ctx context.Context, hts *types.TipSet) (types.BigI
return cs.weight(ctx, cs.StateBlockstore(), hts) return cs.weight(ctx, cs.StateBlockstore(), hts)
} }
// StoreEvents marks this ChainStore as storing events.
func (cs *ChainStore) StoreEvents(store bool) {
cs.storeEvents = store
}
// IsStoringEvents indicates if this ChainStore is storing events.
func (cs *ChainStore) IsStoringEvents() bool {
return cs.storeEvents
}
// true if ts1 wins according to the filecoin tie-break rule // true if ts1 wins according to the filecoin tie-break rule
func breakWeightTie(ts1, ts2 *types.TipSet) bool { func breakWeightTie(ts1, ts2 *types.TipSet) bool {
s := len(ts1.Blocks()) s := len(ts1.Blocks())

View File

@ -56,7 +56,44 @@ type EthTxArgs struct {
S big.Int `json:"s"` S big.Int `json:"s"`
} }
func EthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) { // EthTxFromSignedEthMessage does NOT populate:
// - BlockHash
// - BlockNumber
// - TransactionIndex
// - From
// - Hash
func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) {
if smsg.Signature.Type != typescrypto.SigTypeDelegated {
return EthTx{}, xerrors.Errorf("signature is not delegated type, is type: %d", smsg.Signature.Type)
}
txArgs, err := EthTxArgsFromUnsignedEthMessage(&smsg.Message)
if err != nil {
return EthTx{}, xerrors.Errorf("failed to convert the unsigned message: %w", err)
}
r, s, v, err := RecoverSignature(smsg.Signature)
if err != nil {
return EthTx{}, xerrors.Errorf("failed to recover signature: %w", err)
}
return EthTx{
Nonce: EthUint64(txArgs.Nonce),
ChainID: EthUint64(txArgs.ChainID),
To: txArgs.To,
Value: EthBigInt(txArgs.Value),
Type: Eip1559TxType,
Gas: EthUint64(txArgs.GasLimit),
MaxFeePerGas: EthBigInt(txArgs.MaxFeePerGas),
MaxPriorityFeePerGas: EthBigInt(txArgs.MaxPriorityFeePerGas),
V: v,
R: r,
S: s,
Input: txArgs.Input,
}, nil
}
func EthTxArgsFromUnsignedEthMessage(msg *types.Message) (EthTxArgs, error) {
var ( var (
to *EthAddress to *EthAddress
params []byte params []byte

View File

@ -25,10 +25,10 @@ import (
) )
var ( var (
EthTopic1 = "topic1" EthTopic1 = "t1"
EthTopic2 = "topic2" EthTopic2 = "t2"
EthTopic3 = "topic3" EthTopic3 = "t3"
EthTopic4 = "topic4" EthTopic4 = "t4"
) )
var ErrInvalidAddress = errors.New("invalid Filecoin Eth address") var ErrInvalidAddress = errors.New("invalid Filecoin Eth address")
@ -363,6 +363,18 @@ func (h *EthHash) UnmarshalJSON(b []byte) error {
return nil return nil
} }
func (h EthHash) String() string {
return "0x" + hex.EncodeToString(h[:])
}
// Should ONLY be used for blocks and Filecoin messages. Eth transactions expect a different hashing scheme.
func (h EthHash) ToCid() cid.Cid {
// err is always nil
mh, _ := multihash.EncodeName(h[:], "blake2b-256")
return cid.NewCidV1(cid.DagCBOR, mh)
}
func decodeHexString(s string, expectedLen int) ([]byte, error) { func decodeHexString(s string, expectedLen int) ([]byte, error) {
s = handleHexStringPrefix(s) s = handleHexStringPrefix(s)
if len(s) != expectedLen*2 { if len(s) != expectedLen*2 {
@ -420,18 +432,6 @@ func EthHashFromTxBytes(b []byte) EthHash {
return ethHash return ethHash
} }
func (h EthHash) String() string {
return "0x" + hex.EncodeToString(h[:])
}
// Should ONLY be used for blocks and Filecoin messages. Eth transactions expect a different hashing scheme.
func (h EthHash) ToCid() cid.Cid {
// err is always nil
mh, _ := multihash.EncodeName(h[:], "blake2b-256")
return cid.NewCidV1(cid.DagCBOR, mh)
}
type EthFeeHistory struct { type EthFeeHistory struct {
OldestBlock uint64 `json:"oldestBlock"` OldestBlock uint64 `json:"oldestBlock"`
BaseFeePerGas []EthBigInt `json:"baseFeePerGas"` BaseFeePerGas []EthBigInt `json:"baseFeePerGas"`
@ -441,9 +441,33 @@ type EthFeeHistory struct {
type EthFilterID EthHash type EthFilterID EthHash
func (h EthFilterID) MarshalJSON() ([]byte, error) {
return (EthHash)(h).MarshalJSON()
}
func (h *EthFilterID) UnmarshalJSON(b []byte) error {
return (*EthHash)(h).UnmarshalJSON(b)
}
func (h EthFilterID) String() string {
return (EthHash)(h).String()
}
// An opaque identifier generated by the Lotus node to refer to an active subscription. // An opaque identifier generated by the Lotus node to refer to an active subscription.
type EthSubscriptionID EthHash type EthSubscriptionID EthHash
func (h EthSubscriptionID) MarshalJSON() ([]byte, error) {
return (EthHash)(h).MarshalJSON()
}
func (h *EthSubscriptionID) UnmarshalJSON(b []byte) error {
return (*EthHash)(h).UnmarshalJSON(b)
}
func (h EthSubscriptionID) String() string {
return (EthHash)(h).String()
}
type EthFilterSpec struct { type EthFilterSpec struct {
// Interpreted as an epoch or one of "latest" for last mined block, "earliest" for first, // Interpreted as an epoch or one of "latest" for last mined block, "earliest" for first,
// "pending" for not yet committed messages. // "pending" for not yet committed messages.
@ -595,10 +619,53 @@ type EthLog struct {
BlockNumber EthUint64 `json:"blockNumber"` BlockNumber EthUint64 `json:"blockNumber"`
} }
// EthSubscribeParams handles raw jsonrpc params for eth_subscribe
type EthSubscribeParams struct {
EventType string
Params *EthSubscriptionParams
}
func (e *EthSubscribeParams) UnmarshalJSON(b []byte) error {
var params []json.RawMessage
err := json.Unmarshal(b, &params)
if err != nil {
return err
}
switch len(params) {
case 2:
err = json.Unmarshal(params[1], &e.Params)
if err != nil {
return err
}
fallthrough
case 1:
err = json.Unmarshal(params[0], &e.EventType)
if err != nil {
return err
}
default:
return xerrors.Errorf("expected 1 or 2 params, got %d", len(params))
}
return nil
}
func (e EthSubscribeParams) MarshalJSON() ([]byte, error) {
if e.Params != nil {
return json.Marshal([]interface{}{e.EventType, e.Params})
}
return json.Marshal([]interface{}{e.EventType})
}
type EthSubscriptionParams struct { type EthSubscriptionParams struct {
// List of topics to be matched. // List of topics to be matched.
// Optional, default: empty list // Optional, default: empty list
Topics EthTopicSpec `json:"topics,omitempty"` Topics EthTopicSpec `json:"topics,omitempty"`
// Actor address or a list of addresses from which event logs should originate.
// Optional, default nil.
// The JSON decoding must treat a string as equivalent to an array with one value, for example
// "0x8888f1f195afa192cfee86069858" must be decoded as [ "0x8888f1f195afa192cfee86069858" ]
Address EthAddressList `json:"address"`
} }
type EthSubscriptionResponse struct { type EthSubscriptionResponse struct {

View File

@ -93,6 +93,48 @@ func TestEthHash(t *testing.T) {
h1, err := EthHashFromCid(c) h1, err := EthHashFromCid(c)
require.Nil(t, err) require.Nil(t, err)
require.Equal(t, h, h1) require.Equal(t, h, h1)
jm, err := json.Marshal(h)
require.NoError(t, err)
require.Equal(t, hash, string(jm))
}
}
func TestEthFilterID(t *testing.T) {
testcases := []string{
`"0x013dbb9442ca9667baccc6230fcd5c1c4b2d4d2870f4bd20681d4d47cfd15184"`,
`"0xab8653edf9f51785664a643b47605a7ba3d917b5339a0724e7642c114d0e4738"`,
}
for _, hash := range testcases {
var h EthFilterID
err := h.UnmarshalJSON([]byte(hash))
require.Nil(t, err)
require.Equal(t, h.String(), strings.Replace(hash, `"`, "", -1))
jm, err := json.Marshal(h)
require.NoError(t, err)
require.Equal(t, hash, string(jm))
}
}
func TestEthSubscriptionID(t *testing.T) {
testcases := []string{
`"0x013dbb9442ca9667baccc6230fcd5c1c4b2d4d2870f4bd20681d4d47cfd15184"`,
`"0xab8653edf9f51785664a643b47605a7ba3d917b5339a0724e7642c114d0e4738"`,
}
for _, hash := range testcases {
var h EthSubscriptionID
err := h.UnmarshalJSON([]byte(hash))
require.Nil(t, err)
require.Equal(t, h.String(), strings.Replace(hash, `"`, "", -1))
jm, err := json.Marshal(h)
require.NoError(t, err)
require.Equal(t, hash, string(jm))
} }
} }

View File

@ -1,9 +1,20 @@
package types package types
import ( import (
"bytes"
"fmt"
cbg "github.com/whyrusleeping/cbor-gen"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
) )
// EventEntry flags defined in fvm_shared
const (
EventFlagIndexedKey = 0b00000001
EventFlagIndexedValue = 0b00000010
)
type Event struct { type Event struct {
// The ID of the actor that emitted this event. // The ID of the actor that emitted this event.
Emitter abi.ActorID Emitter abi.ActorID
@ -25,8 +36,23 @@ type EventEntry struct {
type FilterID [32]byte // compatible with EthHash type FilterID [32]byte // compatible with EthHash
// EventEntry flags defined in fvm_shared // DecodeEvents decodes a CBOR list of CBOR-encoded events.
const ( func DecodeEvents(input []byte) ([]Event, error) {
EventFlagIndexedKey = 0b00000001 r := bytes.NewReader(input)
EventFlagIndexedValue = 0b00000010 typ, len, err := cbg.NewCborReader(r).ReadHeader()
) if err != nil {
return nil, fmt.Errorf("failed to read events: %w", err)
}
if typ != cbg.MajArray {
return nil, fmt.Errorf("expected a CBOR list, was major type %d", typ)
}
events := make([]Event, 0, len)
for i := 0; i < int(len); i++ {
var evt Event
if err := evt.UnmarshalCBOR(r); err != nil {
return nil, fmt.Errorf("failed to parse event: %w", err)
}
events = append(events, evt)
}
return events, nil
}

View File

@ -287,6 +287,9 @@ func (x *FvmExtern) workerKeyAtLookback(ctx context.Context, minerId address.Add
type FVM struct { type FVM struct {
fvm *ffi.FVM fvm *ffi.FVM
nv network.Version nv network.Version
// returnEvents specifies whether to parse and return events when applying messages.
returnEvents bool
} }
func defaultFVMOpts(ctx context.Context, opts *VMOpts) (*ffi.FVMOpts, error) { func defaultFVMOpts(ctx context.Context, opts *VMOpts) (*ffi.FVMOpts, error) {
@ -335,10 +338,13 @@ func NewFVM(ctx context.Context, opts *VMOpts) (*FVM, error) {
return nil, xerrors.Errorf("failed to create FVM: %w", err) return nil, xerrors.Errorf("failed to create FVM: %w", err)
} }
return &FVM{ ret := &FVM{
fvm: fvm, fvm: fvm,
nv: opts.NetworkVersion, nv: opts.NetworkVersion,
}, nil returnEvents: opts.ReturnEvents,
}
return ret, nil
} }
func NewDebugFVM(ctx context.Context, opts *VMOpts) (*FVM, error) { func NewDebugFVM(ctx context.Context, opts *VMOpts) (*FVM, error) {
@ -438,10 +444,13 @@ func NewDebugFVM(ctx context.Context, opts *VMOpts) (*FVM, error) {
return nil, err return nil, err
} }
return &FVM{ ret := &FVM{
fvm: fvm, fvm: fvm,
nv: opts.NetworkVersion, nv: opts.NetworkVersion,
}, nil returnEvents: opts.ReturnEvents,
}
return ret, nil
} }
func (vm *FVM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, error) { func (vm *FVM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, error) {
@ -493,7 +502,7 @@ func (vm *FVM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet
et.Error = aerr.Error() et.Error = aerr.Error()
} }
return &ApplyRet{ applyRet := &ApplyRet{
MessageReceipt: receipt, MessageReceipt: receipt,
GasCosts: &GasOutputs{ GasCosts: &GasOutputs{
BaseFeeBurn: ret.BaseFeeBurn, BaseFeeBurn: ret.BaseFeeBurn,
@ -507,7 +516,16 @@ func (vm *FVM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet
ActorErr: aerr, ActorErr: aerr,
ExecutionTrace: et, ExecutionTrace: et,
Duration: duration, Duration: duration,
}, nil }
if vm.returnEvents && len(ret.EventsBytes) > 0 {
applyRet.Events, err = types.DecodeEvents(ret.EventsBytes)
if err != nil {
return nil, fmt.Errorf("failed to decode events returned by the FVM: %w", err)
}
}
return applyRet, nil
} }
func (vm *FVM) ApplyImplicitMessage(ctx context.Context, cmsg *types.Message) (*ApplyRet, error) { func (vm *FVM) ApplyImplicitMessage(ctx context.Context, cmsg *types.Message) (*ApplyRet, error) {
@ -565,6 +583,13 @@ func (vm *FVM) ApplyImplicitMessage(ctx context.Context, cmsg *types.Message) (*
Duration: duration, Duration: duration,
} }
if vm.returnEvents && len(ret.EventsBytes) > 0 {
applyRet.Events, err = types.DecodeEvents(ret.EventsBytes)
if err != nil {
return nil, fmt.Errorf("failed to decode events returned by the FVM: %w", err)
}
}
if ret.ExitCode != 0 { if ret.ExitCode != 0 {
return applyRet, fmt.Errorf("implicit message failed with exit code: %d and error: %w", ret.ExitCode, applyRet.ActorErr) return applyRet, fmt.Errorf("implicit message failed with exit code: %d and error: %w", ret.ExitCode, applyRet.ActorErr)
} }

View File

@ -291,7 +291,10 @@ func DumpActorState(i *ActorRegistry, act *types.Actor, b []byte) (interface{},
um := actInfo.vmActor.State() um := actInfo.vmActor.State()
if um == nil { if um == nil {
// TODO::FVM @arajasek I would like to assert that we have the empty object here if act.Code != EmptyObjectCid {
return nil, xerrors.Errorf("actor with code %s should only have empty object (%s) as its Head, instead has %s", act.Code, EmptyObjectCid, act.Head)
}
return nil, nil return nil, nil
} }
if err := um.UnmarshalCBOR(bytes.NewReader(b)); err != nil { if err := um.UnmarshalCBOR(bytes.NewReader(b)); err != nil {

View File

@ -236,6 +236,8 @@ type VMOpts struct {
LookbackState LookbackStateGetter LookbackState LookbackStateGetter
TipSetGetter TipSetGetter TipSetGetter TipSetGetter
Tracing bool Tracing bool
// ReturnEvents decodes and returns emitted events.
ReturnEvents bool
} }
func NewLegacyVM(ctx context.Context, opts *VMOpts) (*LegacyVM, error) { func NewLegacyVM(ctx context.Context, opts *VMOpts) (*LegacyVM, error) {
@ -282,6 +284,7 @@ type ApplyRet struct {
ExecutionTrace types.ExecutionTrace ExecutionTrace types.ExecutionTrace
Duration time.Duration Duration time.Duration
GasCosts *GasOutputs GasCosts *GasOutputs
Events []types.Event
} }
func (vm *LegacyVM) send(ctx context.Context, msg *types.Message, parent *Runtime, func (vm *LegacyVM) send(ctx context.Context, msg *types.Message, parent *Runtime,

View File

@ -319,11 +319,33 @@ func GetFullNodeAPIV1Single(ctx *cli.Context) (v1api.FullNode, jsonrpc.ClientClo
return v1API, closer, nil return v1API, closer, nil
} }
func GetFullNodeAPIV1(ctx *cli.Context) (v1api.FullNode, jsonrpc.ClientCloser, error) { type GetFullNodeOptions struct {
ethSubHandler api.EthSubscriber
}
type GetFullNodeOption func(*GetFullNodeOptions)
func FullNodeWithEthSubscribtionHandler(sh api.EthSubscriber) GetFullNodeOption {
return func(opts *GetFullNodeOptions) {
opts.ethSubHandler = sh
}
}
func GetFullNodeAPIV1(ctx *cli.Context, opts ...GetFullNodeOption) (v1api.FullNode, jsonrpc.ClientCloser, error) {
if tn, ok := ctx.App.Metadata["testnode-full"]; ok { if tn, ok := ctx.App.Metadata["testnode-full"]; ok {
return tn.(v1api.FullNode), func() {}, nil return tn.(v1api.FullNode), func() {}, nil
} }
var options GetFullNodeOptions
for _, opt := range opts {
opt(&options)
}
var rpcOpts []jsonrpc.Option
if options.ethSubHandler != nil {
rpcOpts = append(rpcOpts, jsonrpc.WithClientHandler("Filecoin", options.ethSubHandler), jsonrpc.WithClientHandlerAlias("eth_subscription", "Filecoin.EthSubscription"))
}
heads, err := GetRawAPIMulti(ctx, repo.FullNode, "v1") heads, err := GetRawAPIMulti(ctx, repo.FullNode, "v1")
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
@ -337,7 +359,7 @@ func GetFullNodeAPIV1(ctx *cli.Context) (v1api.FullNode, jsonrpc.ClientCloser, e
var closers []jsonrpc.ClientCloser var closers []jsonrpc.ClientCloser
for _, head := range heads { for _, head := range heads {
v1api, closer, err := client.NewFullNodeRPCV1(ctx.Context, head.addr, head.header) v1api, closer, err := client.NewFullNodeRPCV1(ctx.Context, head.addr, head.header, rpcOpts...)
if err != nil { if err != nil {
log.Warnf("Not able to establish connection to node with addr: ", head.addr) log.Warnf("Not able to establish connection to node with addr: ", head.addr)
continue continue

View File

@ -162,7 +162,9 @@ var runCmd = &cli.Command{
log.Fatalf("Cannot register the view: %v", err) log.Fatalf("Cannot register the view: %v", err)
} }
api, closer, err := lcli.GetFullNodeAPIV1(cctx) subHnd := gateway.NewEthSubHandler()
api, closer, err := lcli.GetFullNodeAPIV1(cctx, cliutil.FullNodeWithEthSubscribtionHandler(subHnd))
if err != nil { if err != nil {
return err return err
} }
@ -195,7 +197,7 @@ var runCmd = &cli.Command{
return xerrors.Errorf("failed to convert endpoint address to multiaddr: %w", err) return xerrors.Errorf("failed to convert endpoint address to multiaddr: %w", err)
} }
gwapi := gateway.NewNode(api, lookbackCap, waitLookback, rateLimit, rateLimitTimeout) gwapi := gateway.NewNode(api, subHnd, lookbackCap, waitLookback, rateLimit, rateLimitTimeout)
h, err := gateway.Handler(gwapi, api, perConnRateLimit, connPerMinute, serverOptions...) h, err := gateway.Handler(gwapi, api, perConnRateLimit, connPerMinute, serverOptions...)
if err != nil { if err != nil {
return xerrors.Errorf("failed to set up gateway HTTP handler") return xerrors.Errorf("failed to set up gateway HTTP handler")

72
cmd/lotus-shed/eth.go Normal file
View File

@ -0,0 +1,72 @@
package main
import (
"fmt"
"reflect"
"github.com/urfave/cli/v2"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/lotus/chain/types"
lcli "github.com/filecoin-project/lotus/cli"
)
var ethCmd = &cli.Command{
Name: "eth",
Description: "Ethereum compatibility related commands",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "repo",
Value: "~/.lotus",
},
},
Subcommands: []*cli.Command{
checkTipsetsCmd,
},
}
var checkTipsetsCmd = &cli.Command{
Name: "check-tipsets",
Description: "Check that eth_getBlockByNumber and eth_getBlockByHash consistently return tipsets",
Action: func(cctx *cli.Context) error {
api, closer, err := lcli.GetFullNodeAPIV1(cctx)
if err != nil {
return err
}
defer closer()
ctx := lcli.ReqContext(cctx)
head, err := api.ChainHead(ctx)
if err != nil {
return err
}
height := head.Height()
fmt.Println("Current height:", height)
for i := int64(height); i > 0; i-- {
if _, err := api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(i), types.EmptyTSK); err != nil {
fmt.Printf("[FAIL] failed to get tipset @%d from Lotus: %s\n", i, err)
continue
}
hex := fmt.Sprintf("0x%x", i)
ethBlockA, err := api.EthGetBlockByNumber(ctx, hex, false)
if err != nil {
fmt.Printf("[FAIL] failed to get tipset @%d via eth_getBlockByNumber: %s\n", i, err)
continue
}
ethBlockB, err := api.EthGetBlockByHash(ctx, ethBlockA.Hash, false)
if err != nil {
fmt.Printf("[FAIL] failed to get tipset @%d via eth_getBlockByHash: %s\n", i, err)
continue
}
if equal := reflect.DeepEqual(ethBlockA, ethBlockB); equal {
fmt.Printf("[OK] blocks received via eth_getBlockByNumber and eth_getBlockByHash for tipset @%d are identical\n", i)
} else {
fmt.Printf("[FAIL] blocks received via eth_getBlockByNumber and eth_getBlockByHash for tipset @%d are NOT identical\n", i)
}
}
return nil
},
}

View File

@ -51,6 +51,7 @@ func main() {
minerCmd, minerCmd,
mpoolStatsCmd, mpoolStatsCmd,
exportChainCmd, exportChainCmd,
ethCmd,
exportCarCmd, exportCarCmd,
consensusCmd, consensusCmd,
syncCmd, syncCmd,

View File

@ -291,6 +291,8 @@
* [WalletSignMessage](#WalletSignMessage) * [WalletSignMessage](#WalletSignMessage)
* [WalletValidateAddress](#WalletValidateAddress) * [WalletValidateAddress](#WalletValidateAddress)
* [WalletVerify](#WalletVerify) * [WalletVerify](#WalletVerify)
* [Web3](#Web3)
* [Web3ClientVersion](#Web3ClientVersion)
## ##
@ -2518,40 +2520,7 @@ Perms: write
Inputs: Inputs:
```json ```json
[ [
[ "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"
55,
105,
12,
254,
198,
193,
191,
76,
59,
146,
136,
199,
165,
215,
131,
233,
135,
49,
233,
11,
10,
76,
23,
124,
42,
55,
76,
122,
148,
39,
53,
94
]
] ]
``` ```
@ -2572,40 +2541,7 @@ Perms: write
Inputs: Inputs:
```json ```json
[ [
[ "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"
55,
105,
12,
254,
198,
193,
191,
76,
59,
146,
136,
199,
165,
215,
131,
233,
135,
49,
233,
11,
10,
76,
23,
124,
42,
55,
76,
122,
148,
39,
53,
94
]
] ]
``` ```
@ -2879,43 +2815,7 @@ Perms: write
Inputs: `null` Inputs: `null`
Response: Response: `"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"`
```json
[
55,
105,
12,
254,
198,
193,
191,
76,
59,
146,
136,
199,
165,
215,
131,
233,
135,
49,
233,
11,
10,
76,
23,
124,
42,
55,
76,
122,
148,
39,
53,
94
]
```
### EthNewFilter ### EthNewFilter
Installs a persistent filter based on given filter spec. Installs a persistent filter based on given filter spec.
@ -2936,43 +2836,7 @@ Inputs:
] ]
``` ```
Response: Response: `"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"`
```json
[
55,
105,
12,
254,
198,
193,
191,
76,
59,
146,
136,
199,
165,
215,
131,
233,
135,
49,
233,
11,
10,
76,
23,
124,
42,
55,
76,
122,
148,
39,
53,
94
]
```
### EthNewPendingTransactionFilter ### EthNewPendingTransactionFilter
Installs a persistent filter to notify when new messages arrive in the message pool. Installs a persistent filter to notify when new messages arrive in the message pool.
@ -2982,43 +2846,7 @@ Perms: write
Inputs: `null` Inputs: `null`
Response: Response: `"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"`
```json
[
55,
105,
12,
254,
198,
193,
191,
76,
59,
146,
136,
199,
165,
215,
131,
233,
135,
49,
233,
11,
10,
76,
23,
124,
42,
55,
76,
122,
148,
39,
53,
94
]
```
### EthProtocolVersion ### EthProtocolVersion
@ -3058,57 +2886,11 @@ Perms: write
Inputs: Inputs:
```json ```json
[ [
"string value", "Bw=="
{
"topics": [
[
"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"
]
]
}
] ]
``` ```
Response: Response: `"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"`
```json
{
"subscription": [
55,
105,
12,
254,
198,
193,
191,
76,
59,
146,
136,
199,
165,
215,
131,
233,
135,
49,
233,
11,
10,
76,
23,
124,
42,
55,
76,
122,
148,
39,
53,
94
],
"result": {}
}
```
### EthUninstallFilter ### EthUninstallFilter
Uninstalls a filter with given id. Uninstalls a filter with given id.
@ -3119,40 +2901,7 @@ Perms: write
Inputs: Inputs:
```json ```json
[ [
[ "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"
55,
105,
12,
254,
198,
193,
191,
76,
59,
146,
136,
199,
165,
215,
131,
233,
135,
49,
233,
11,
10,
76,
23,
124,
42,
55,
76,
122,
148,
39,
53,
94
]
] ]
``` ```
@ -3167,40 +2916,7 @@ Perms: write
Inputs: Inputs:
```json ```json
[ [
[ "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"
55,
105,
12,
254,
198,
193,
191,
76,
59,
146,
136,
199,
165,
215,
131,
233,
135,
49,
233,
11,
10,
76,
23,
124,
42,
55,
76,
122,
148,
39,
53,
94
]
] ]
``` ```
@ -9207,3 +8923,16 @@ Inputs:
Response: `true` Response: `true`
## Web3
### Web3ClientVersion
Returns the client version
Perms: read
Inputs: `null`
Response: `"string value"`

2
extern/filecoin-ffi vendored

@ -1 +1 @@
Subproject commit 86eac2161f442945bffee3fbfe7d094c20b48dd3 Subproject commit 0c792ee1d1f062377033d7d37442d18f765be467

71
gateway/eth_sub.go Normal file
View File

@ -0,0 +1,71 @@
package gateway
import (
"context"
"sync"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
)
type EthSubHandler struct {
queued map[ethtypes.EthSubscriptionID][]ethtypes.EthSubscriptionResponse
sinks map[ethtypes.EthSubscriptionID]func(context.Context, *ethtypes.EthSubscriptionResponse) error
lk sync.Mutex
}
func NewEthSubHandler() *EthSubHandler {
return &EthSubHandler{
queued: make(map[ethtypes.EthSubscriptionID][]ethtypes.EthSubscriptionResponse),
sinks: make(map[ethtypes.EthSubscriptionID]func(context.Context, *ethtypes.EthSubscriptionResponse) error),
}
}
func (e *EthSubHandler) AddSub(ctx context.Context, id ethtypes.EthSubscriptionID, sink func(context.Context, *ethtypes.EthSubscriptionResponse) error) error {
e.lk.Lock()
defer e.lk.Unlock()
for _, p := range e.queued[id] {
p := p // copy
if err := sink(ctx, &p); err != nil {
return err
}
}
delete(e.queued, id)
e.sinks[id] = sink
return nil
}
func (e *EthSubHandler) RemoveSub(id ethtypes.EthSubscriptionID) {
e.lk.Lock()
defer e.lk.Unlock()
delete(e.sinks, id)
delete(e.queued, id)
}
func (e *EthSubHandler) EthSubscription(ctx context.Context, r jsonrpc.RawParams) error {
p, err := jsonrpc.DecodeParams[ethtypes.EthSubscriptionResponse](r)
if err != nil {
return err
}
e.lk.Lock()
sink := e.sinks[p.SubscriptionID]
if sink == nil {
e.queued[p.SubscriptionID] = append(e.queued[p.SubscriptionID], p)
e.lk.Unlock()
return nil
}
e.lk.Unlock()
return sink(ctx, &p) // todo track errors and auto-unsubscribe on rpc conn close?
}
var _ api.EthSubscriber = (*EthSubHandler)(nil)

View File

@ -27,14 +27,14 @@ const perConnLimiterKey perConnLimiterKeyType = "limiter"
type filterTrackerKeyType string type filterTrackerKeyType string
const filterTrackerKey filterTrackerKeyType = "filterTracker" const statefulCallTrackerKey filterTrackerKeyType = "statefulCallTracker"
// Handler returns a gateway http.Handler, to be mounted as-is on the server. // Handler returns a gateway http.Handler, to be mounted as-is on the server.
func Handler(gwapi lapi.Gateway, api lapi.FullNode, rateLimit int64, connPerMinute int64, opts ...jsonrpc.ServerOption) (http.Handler, error) { func Handler(gwapi lapi.Gateway, api lapi.FullNode, rateLimit int64, connPerMinute int64, opts ...jsonrpc.ServerOption) (http.Handler, error) {
m := mux.NewRouter() m := mux.NewRouter()
serveRpc := func(path string, hnd interface{}) { serveRpc := func(path string, hnd interface{}) {
rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithServerErrors(lapi.RPCErrors))...) rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithReverseClient[lapi.EthSubscriberMethods]("Filecoin"), jsonrpc.WithServerErrors(lapi.RPCErrors))...)
rpcServer.Register("Filecoin", hnd) rpcServer.Register("Filecoin", hnd)
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover") rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")
@ -90,7 +90,7 @@ func (h RateLimiterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
r = r.WithContext(context.WithValue(r.Context(), perConnLimiterKey, h.limiter)) r = r.WithContext(context.WithValue(r.Context(), perConnLimiterKey, h.limiter))
// also add a filter tracker to the context // also add a filter tracker to the context
r = r.WithContext(context.WithValue(r.Context(), filterTrackerKey, newFilterTracker())) r = r.WithContext(context.WithValue(r.Context(), statefulCallTrackerKey, newStatefulCallTracker()))
h.handler.ServeHTTP(w, r) h.handler.ServeHTTP(w, r)
} }

View File

@ -12,6 +12,7 @@ import (
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-bitfield" "github.com/filecoin-project/go-bitfield"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/dline" "github.com/filecoin-project/go-state-types/dline"
"github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/go-state-types/network"
@ -93,6 +94,8 @@ type TargetAPI interface {
EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error) EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error)
EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error)
EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error)
EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error)
EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error)
EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error)
EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*api.EthTxReceipt, error) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*api.EthTxReceipt, error)
EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error)
@ -117,14 +120,16 @@ type TargetAPI interface {
EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error)
EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error)
EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error)
EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error)
EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error)
Web3ClientVersion(ctx context.Context) (string, error)
} }
var _ TargetAPI = *new(api.FullNode) // gateway depends on latest var _ TargetAPI = *new(api.FullNode) // gateway depends on latest
type Node struct { type Node struct {
target TargetAPI target TargetAPI
subHnd *EthSubHandler
lookbackCap time.Duration lookbackCap time.Duration
stateWaitLookbackLimit abi.ChainEpoch stateWaitLookbackLimit abi.ChainEpoch
rateLimiter *rate.Limiter rateLimiter *rate.Limiter
@ -141,7 +146,7 @@ var (
) )
// NewNode creates a new gateway node. // NewNode creates a new gateway node.
func NewNode(api TargetAPI, lookbackCap time.Duration, stateWaitLookbackLimit abi.ChainEpoch, rateLimit int64, rateLimitTimeout time.Duration) *Node { func NewNode(api TargetAPI, sHnd *EthSubHandler, lookbackCap time.Duration, stateWaitLookbackLimit abi.ChainEpoch, rateLimit int64, rateLimitTimeout time.Duration) *Node {
var limit rate.Limit var limit rate.Limit
if rateLimit == 0 { if rateLimit == 0 {
limit = rate.Inf limit = rate.Inf
@ -150,6 +155,7 @@ func NewNode(api TargetAPI, lookbackCap time.Duration, stateWaitLookbackLimit ab
} }
return &Node{ return &Node{
target: api, target: api,
subHnd: sHnd,
lookbackCap: lookbackCap, lookbackCap: lookbackCap,
stateWaitLookbackLimit: stateWaitLookbackLimit, stateWaitLookbackLimit: stateWaitLookbackLimit,
rateLimiter: rate.NewLimiter(limit, stateRateLimitTokens), rateLimiter: rate.NewLimiter(limit, stateRateLimitTokens),

View File

@ -89,7 +89,7 @@ func TestGatewayAPIChainGetTipSetByHeight(t *testing.T) {
tt := tt tt := tt
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
mock := &mockGatewayDepsAPI{} mock := &mockGatewayDepsAPI{}
a := NewNode(mock, DefaultLookbackCap, DefaultStateWaitLookbackLimit, 0, time.Minute) a := NewNode(mock, nil, DefaultLookbackCap, DefaultStateWaitLookbackLimit, 0, time.Minute)
// Create tipsets from genesis up to tskh and return the highest // Create tipsets from genesis up to tskh and return the highest
ts := mock.createTipSets(tt.args.tskh, tt.args.genesisTS) ts := mock.createTipSets(tt.args.tskh, tt.args.genesisTS)
@ -245,7 +245,7 @@ func TestGatewayVersion(t *testing.T) {
//stm: @GATEWAY_NODE_GET_VERSION_001 //stm: @GATEWAY_NODE_GET_VERSION_001
ctx := context.Background() ctx := context.Background()
mock := &mockGatewayDepsAPI{} mock := &mockGatewayDepsAPI{}
a := NewNode(mock, DefaultLookbackCap, DefaultStateWaitLookbackLimit, 0, time.Minute) a := NewNode(mock, nil, DefaultLookbackCap, DefaultStateWaitLookbackLimit, 0, time.Minute)
v, err := a.Version(ctx) v, err := a.Version(ctx)
require.NoError(t, err) require.NoError(t, err)
@ -256,7 +256,7 @@ func TestGatewayLimitTokensAvailable(t *testing.T) {
ctx := context.Background() ctx := context.Background()
mock := &mockGatewayDepsAPI{} mock := &mockGatewayDepsAPI{}
tokens := 3 tokens := 3
a := NewNode(mock, DefaultLookbackCap, DefaultStateWaitLookbackLimit, int64(tokens), time.Minute) a := NewNode(mock, nil, DefaultLookbackCap, DefaultStateWaitLookbackLimit, int64(tokens), time.Minute)
require.NoError(t, a.limit(ctx, tokens), "requests should not be limited when there are enough tokens available") require.NoError(t, a.limit(ctx, tokens), "requests should not be limited when there are enough tokens available")
} }
@ -264,7 +264,7 @@ func TestGatewayLimitTokensNotAvailable(t *testing.T) {
ctx := context.Background() ctx := context.Background()
mock := &mockGatewayDepsAPI{} mock := &mockGatewayDepsAPI{}
tokens := 3 tokens := 3
a := NewNode(mock, DefaultLookbackCap, DefaultStateWaitLookbackLimit, int64(1), time.Millisecond) a := NewNode(mock, nil, DefaultLookbackCap, DefaultStateWaitLookbackLimit, int64(1), time.Millisecond)
var err error var err error
// try to be rate limited // try to be rate limited
for i := 0; i <= 1000; i++ { for i := 0; i <= 1000; i++ {

View File

@ -3,12 +3,15 @@ package gateway
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/json"
"fmt" "fmt"
"sync" "sync"
"time" "time"
"github.com/ipfs/go-cid"
"golang.org/x/xerrors" "golang.org/x/xerrors"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
@ -17,6 +20,14 @@ import (
"github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/chain/types/ethtypes"
) )
func (gw *Node) Web3ClientVersion(ctx context.Context) (string, error) {
if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
return "", err
}
return gw.target.Web3ClientVersion(ctx)
}
func (gw *Node) EthAccounts(ctx context.Context) ([]ethtypes.EthAddress, error) { func (gw *Node) EthAccounts(ctx context.Context) ([]ethtypes.EthAddress, error) {
// gateway provides public API, so it can't hold user accounts // gateway provides public API, so it can't hold user accounts
return []ethtypes.EthAddress{}, nil return []ethtypes.EthAddress{}, nil
@ -141,6 +152,22 @@ func (gw *Node) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.Et
return gw.target.EthGetTransactionByHash(ctx, txHash) return gw.target.EthGetTransactionByHash(ctx, txHash)
} }
func (gw *Node) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) {
if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
return nil, err
}
return gw.target.EthGetTransactionHashByCid(ctx, cid)
}
func (gw *Node) EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) {
if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
return nil, err
}
return gw.target.EthGetMessageCidByTransactionHash(ctx, txHash)
}
func (gw *Node) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) { func (gw *Node) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) {
if err := gw.limit(ctx, stateRateLimitTokens); err != nil { if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
return 0, err return 0, err
@ -352,7 +379,7 @@ func (gw *Node) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID
return nil, err return nil, err
} }
ft := filterTrackerFromContext(ctx) ft := statefulCallFromContext(ctx)
ft.lk.Lock() ft.lk.Lock()
_, ok := ft.userFilters[id] _, ok := ft.userFilters[id]
ft.lk.Unlock() ft.lk.Unlock()
@ -369,7 +396,7 @@ func (gw *Node) EthGetFilterLogs(ctx context.Context, id ethtypes.EthFilterID) (
return nil, err return nil, err
} }
ft := filterTrackerFromContext(ctx) ft := statefulCallFromContext(ctx)
ft.lk.Lock() ft.lk.Lock()
_, ok := ft.userFilters[id] _, ok := ft.userFilters[id]
ft.lk.Unlock() ft.lk.Unlock()
@ -417,7 +444,7 @@ func (gw *Node) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID)
} }
// check if the filter belongs to this connection // check if the filter belongs to this connection
ft := filterTrackerFromContext(ctx) ft := statefulCallFromContext(ctx)
ft.lk.Lock() ft.lk.Lock()
defer ft.lk.Unlock() defer ft.lk.Unlock()
@ -434,18 +461,88 @@ func (gw *Node) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID)
return ok, nil return ok, nil
} }
func (gw *Node) EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { func (gw *Node) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
return nil, xerrors.Errorf("not implemented") // validate params
_, err := jsonrpc.DecodeParams[ethtypes.EthSubscribeParams](p)
if err != nil {
return ethtypes.EthSubscriptionID{}, xerrors.Errorf("decoding params: %w", err)
}
if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
return ethtypes.EthSubscriptionID{}, err
}
if gw.subHnd == nil {
return ethtypes.EthSubscriptionID{}, xerrors.New("subscription support not enabled")
}
ethCb, ok := jsonrpc.ExtractReverseClient[api.EthSubscriberMethods](ctx)
if !ok {
return ethtypes.EthSubscriptionID{}, xerrors.Errorf("connection doesn't support callbacks")
}
ft := statefulCallFromContext(ctx)
ft.lk.Lock()
defer ft.lk.Unlock()
if len(ft.userSubscriptions) >= EthMaxFiltersPerConn {
return ethtypes.EthSubscriptionID{}, fmt.Errorf("too many subscriptions")
}
sub, err := gw.target.EthSubscribe(ctx, p)
if err != nil {
return ethtypes.EthSubscriptionID{}, err
}
err = gw.subHnd.AddSub(ctx, sub, func(ctx context.Context, response *ethtypes.EthSubscriptionResponse) error {
outParam, err := json.Marshal(response)
if err != nil {
return err
}
return ethCb.EthSubscription(ctx, outParam)
})
if err != nil {
return ethtypes.EthSubscriptionID{}, err
}
ft.userSubscriptions[sub] = time.Now()
return sub, err
} }
func (gw *Node) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) { func (gw *Node) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) {
return false, xerrors.Errorf("not implemented") if err := gw.limit(ctx, stateRateLimitTokens); err != nil {
return false, err
}
// check if the filter belongs to this connection
ft := statefulCallFromContext(ctx)
ft.lk.Lock()
defer ft.lk.Unlock()
if _, ok := ft.userSubscriptions[id]; !ok {
return false, nil
}
ok, err := gw.target.EthUnsubscribe(ctx, id)
if err != nil {
return false, err
}
delete(ft.userSubscriptions, id)
if gw.subHnd != nil {
gw.subHnd.RemoveSub(id)
}
return ok, nil
} }
var EthMaxFiltersPerConn = 16 // todo make this configurable var EthMaxFiltersPerConn = 16 // todo make this configurable
func addUserFilterLimited(ctx context.Context, cb func() (ethtypes.EthFilterID, error)) (ethtypes.EthFilterID, error) { func addUserFilterLimited(ctx context.Context, cb func() (ethtypes.EthFilterID, error)) (ethtypes.EthFilterID, error) {
ft := filterTrackerFromContext(ctx) ft := statefulCallFromContext(ctx)
ft.lk.Lock() ft.lk.Lock()
defer ft.lk.Unlock() defer ft.lk.Unlock()
@ -463,19 +560,21 @@ func addUserFilterLimited(ctx context.Context, cb func() (ethtypes.EthFilterID,
return id, nil return id, nil
} }
func filterTrackerFromContext(ctx context.Context) *filterTracker { func statefulCallFromContext(ctx context.Context) *statefulCallTracker {
return ctx.Value(filterTrackerKey).(*filterTracker) return ctx.Value(statefulCallTrackerKey).(*statefulCallTracker)
} }
type filterTracker struct { type statefulCallTracker struct {
lk sync.Mutex lk sync.Mutex
userFilters map[ethtypes.EthFilterID]time.Time userFilters map[ethtypes.EthFilterID]time.Time
userSubscriptions map[ethtypes.EthSubscriptionID]time.Time
} }
// called per request (ws connection) // called per request (ws connection)
func newFilterTracker() *filterTracker { func newStatefulCallTracker() *statefulCallTracker {
return &filterTracker{ return &statefulCallTracker{
userFilters: make(map[ethtypes.EthFilterID]time.Time), userFilters: make(map[ethtypes.EthFilterID]time.Time),
userSubscriptions: make(map[ethtypes.EthSubscriptionID]time.Time),
} }
} }

View File

@ -259,7 +259,7 @@ func generate(path, pkg, outpkg, outfile string) error {
if len(tf) != 2 { if len(tf) != 2 {
continue continue
} }
if tf[0] != "perm" { // todo: allow more tag types if tf[0] != "perm" && tf[0] != "rpc_method" && tf[0] != "notify" { // todo: allow more tag types
continue continue
} }
info.Methods[mname].Tags[tf[0]] = tf info.Methods[mname].Tags[tf[0]] = tf
@ -302,12 +302,14 @@ type {{.Num}}Struct struct {
{{range .Include}} {{range .Include}}
{{.}}Struct {{.}}Struct
{{end}} {{end}}
Internal struct { Internal {{.Num}}Methods
}
type {{.Num}}Methods struct {
{{range .Methods}} {{range .Methods}}
{{.Num}} func({{.NamedParams}}) ({{.Results}}) `+"`"+`{{range .Tags}}{{index . 0}}:"{{index . 1}}"{{end}}`+"`"+` {{.Num}} func({{.NamedParams}}) ({{.Results}}) `+"`"+`{{range .Tags}}{{index . 0}}:"{{index . 1}}"{{end}}`+"`"+`
{{end}} {{end}}
} }
}
type {{.Num}}Stub struct { type {{.Num}}Stub struct {
{{range .Include}} {{range .Include}}

4
go.mod
View File

@ -40,11 +40,11 @@ require (
github.com/filecoin-project/go-fil-commcid v0.1.0 github.com/filecoin-project/go-fil-commcid v0.1.0
github.com/filecoin-project/go-fil-commp-hashhash v0.1.0 github.com/filecoin-project/go-fil-commp-hashhash v0.1.0
github.com/filecoin-project/go-fil-markets v1.26.0 github.com/filecoin-project/go-fil-markets v1.26.0
github.com/filecoin-project/go-jsonrpc v0.1.9 github.com/filecoin-project/go-jsonrpc v0.2.1
github.com/filecoin-project/go-legs v0.4.4 github.com/filecoin-project/go-legs v0.4.4
github.com/filecoin-project/go-padreader v0.0.1 github.com/filecoin-project/go-padreader v0.0.1
github.com/filecoin-project/go-paramfetch v0.0.4 github.com/filecoin-project/go-paramfetch v0.0.4
github.com/filecoin-project/go-state-types v0.10.0-alpha-10 github.com/filecoin-project/go-state-types v0.10.0-alpha-11
github.com/filecoin-project/go-statemachine v1.0.2 github.com/filecoin-project/go-statemachine v1.0.2
github.com/filecoin-project/go-statestore v0.2.0 github.com/filecoin-project/go-statestore v0.2.0
github.com/filecoin-project/go-storedcounter v0.1.0 github.com/filecoin-project/go-storedcounter v0.1.0

9
go.sum
View File

@ -340,8 +340,8 @@ github.com/filecoin-project/go-hamt-ipld/v2 v2.0.0/go.mod h1:7aWZdaQ1b16BVoQUYR+
github.com/filecoin-project/go-hamt-ipld/v3 v3.0.1/go.mod h1:gXpNmr3oQx8l3o7qkGyDjJjYSRX7hp/FGOStdqrWyDI= github.com/filecoin-project/go-hamt-ipld/v3 v3.0.1/go.mod h1:gXpNmr3oQx8l3o7qkGyDjJjYSRX7hp/FGOStdqrWyDI=
github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0 h1:rVVNq0x6RGQIzCo1iiJlGFm9AGIZzeifggxtKMU7zmI= github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0 h1:rVVNq0x6RGQIzCo1iiJlGFm9AGIZzeifggxtKMU7zmI=
github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0/go.mod h1:bxmzgT8tmeVQA1/gvBwFmYdT8SOFUwB3ovSUfG1Ux0g= github.com/filecoin-project/go-hamt-ipld/v3 v3.1.0/go.mod h1:bxmzgT8tmeVQA1/gvBwFmYdT8SOFUwB3ovSUfG1Ux0g=
github.com/filecoin-project/go-jsonrpc v0.1.9 h1:HRWLxo7HAWzI3xZGeFG4LZJoYpms+Q+8kwmMTLnyS3A= github.com/filecoin-project/go-jsonrpc v0.2.1 h1:xfxkfIAO300sPiV59DnxnCb4sdTtWYlRz/TsP+ByT2E=
github.com/filecoin-project/go-jsonrpc v0.1.9/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4= github.com/filecoin-project/go-jsonrpc v0.2.1/go.mod h1:jBSvPTl8V1N7gSTuCR4bis8wnQnIjHbRPpROol6iQKM=
github.com/filecoin-project/go-legs v0.4.4 h1:mpMmAOOnamaz0CV9rgeKhEWA8j9kMC+f+UGCGrxKaZo= github.com/filecoin-project/go-legs v0.4.4 h1:mpMmAOOnamaz0CV9rgeKhEWA8j9kMC+f+UGCGrxKaZo=
github.com/filecoin-project/go-legs v0.4.4/go.mod h1:JQ3hA6xpJdbR8euZ2rO0jkxaMxeidXf0LDnVuqPAe9s= github.com/filecoin-project/go-legs v0.4.4/go.mod h1:JQ3hA6xpJdbR8euZ2rO0jkxaMxeidXf0LDnVuqPAe9s=
github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak= github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak=
@ -356,8 +356,8 @@ github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psS
github.com/filecoin-project/go-state-types v0.1.6/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.6/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q=
github.com/filecoin-project/go-state-types v0.1.8/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.8/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q=
github.com/filecoin-project/go-state-types v0.1.10/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.10/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q=
github.com/filecoin-project/go-state-types v0.10.0-alpha-10 h1:QUpSayVFUADlrtzCh7SDNlbuaNSlYPBR46Nt7WpFl9I= github.com/filecoin-project/go-state-types v0.10.0-alpha-11 h1:lfrbmLXaC3vQk1gQCUwtTuY1U2ANrgDsJ7+VapBjRCo=
github.com/filecoin-project/go-state-types v0.10.0-alpha-10/go.mod h1:FPgQE05BFwZxKw/vCuIaIrzfJKo4RPQQMMPGd43dAFI= github.com/filecoin-project/go-state-types v0.10.0-alpha-11/go.mod h1:aLIas+W8BWAfpLWEPUOGMPBdhcVwoCG4pIQSQk26024=
github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig= github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig=
github.com/filecoin-project/go-statemachine v1.0.2 h1:421SSWBk8GIoCoWYYTE/d+qCWccgmRH0uXotXRDjUbc= github.com/filecoin-project/go-statemachine v1.0.2 h1:421SSWBk8GIoCoWYYTE/d+qCWccgmRH0uXotXRDjUbc=
github.com/filecoin-project/go-statemachine v1.0.2/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54= github.com/filecoin-project/go-statemachine v1.0.2/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54=
@ -823,7 +823,6 @@ github.com/ipfs/go-log/v2 v2.0.1/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBW
github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.2/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0= github.com/ipfs/go-log/v2 v2.0.3/go.mod h1:O7P1lJt27vWHhOwQmcFEvlmo49ry2VY2+JfBWFaa9+0=
github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw= github.com/ipfs/go-log/v2 v2.0.5/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
github.com/ipfs/go-log/v2 v2.0.8/go.mod h1:eZs4Xt4ZUJQFM3DlanGhy7TkwwawCZcSByscwkWG+dw=
github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/ipfs/go-log/v2 v2.1.1/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/ipfs/go-log/v2 v2.1.2-0.20200626104915-0016c0b4b3e4/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=
github.com/ipfs/go-log/v2 v2.1.2/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM= github.com/ipfs/go-log/v2 v2.1.2/go.mod h1:2v2nsGfZsvvAJz13SyFzf9ObaqwHiHxsPLEHntrv9KM=

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b5061001f61002460201b60201c565b61003d565b3373ffffffffffffffffffffffffffffffffffffffff16ff5b60848061004b6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806383197ef014602d575b600080fd5b60336035565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea26469706673582212208d48a69a112633756d84552847610df29b02ac89dd39e4e295066e99a45e809664736f6c63430008110033

View File

@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17;
contract AutoSelfDestruct {
constructor() {
destroy();
}
function destroy() public {
selfdestruct(payable(msg.sender));
}
}

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b506103ca806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c806358f5ebb614610030575b600080fd5b61004a60048036038101906100459190610123565b610060565b6040516100579190610191565b60405180910390f35b60008082604051610070906100db565b61007a91906101bb565b604051809103906000f080158015610096573d6000803e3d6000fd5b5090507f3a5c468996b00310e3e82919e3af9cce21d49c40c39a2627a9f946e1a54d886232846040516100ca9291906101d6565b60405180910390a180915050919050565b6101958061020083390190565b600080fd5b6000819050919050565b610100816100ed565b811461010b57600080fd5b50565b60008135905061011d816100f7565b92915050565b600060208284031215610139576101386100e8565b5b60006101478482850161010e565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061017b82610150565b9050919050565b61018b81610170565b82525050565b60006020820190506101a66000830184610182565b92915050565b6101b5816100ed565b82525050565b60006020820190506101d060008301846101ac565b92915050565b60006040820190506101eb6000830185610182565b6101f860208301846101ac565b939250505056fe608060405234801561001057600080fd5b506040516101953803806101958339818101604052810190610032919061007a565b80600081905550506100a7565b600080fd5b6000819050919050565b61005781610044565b811461006257600080fd5b50565b6000815190506100748161004e565b92915050565b6000602082840312156100905761008f61003f565b5b600061009e84828501610065565b91505092915050565b60e0806100b56000396000f3fe6080604052348015600f57600080fd5b506004361060325760003560e01c80638381f58a146037578063eeb4e367146051575b600080fd5b603d606b565b604051604891906091565b60405180910390f35b60576071565b604051606291906091565b60405180910390f35b60005481565b60008054905090565b6000819050919050565b608b81607a565b82525050565b600060208201905060a460008301846084565b9291505056fea2646970667358221220451c388f24a935fc5f5eef536207cbd982254ac8521d49937bb10e5079e3924164736f6c63430008110033a264697066735822122027da159d84a9bdcd5aff5755c4602f7099db638f7a95d715c76454c99511146f64736f6c63430008110033

View File

@ -0,0 +1,29 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
contract Test_contract {
uint256 public number;
constructor(uint256 _number) {
number = _number;
}
function get_number() public view returns (uint256) {
return number;
}
}
contract App {
event NewTest(address sender, uint256 number);
function new_Test(uint256 number)
public
returns (address)
{
address mynew = address(new Test_contract({_number: number}));
emit NewTest(tx.origin, number);
return mynew;
}
}

View File

@ -1 +1 @@
608060405234801561001057600080fd5b50610477806100206000396000f3fe6080604052600436106100345760003560e01c806361bc221a146100395780638ada066e14610064578063d1e0f3081461008f575b600080fd5b34801561004557600080fd5b5061004e6100bf565b60405161005b919061022c565b60405180910390f35b34801561007057600080fd5b506100796100c5565b604051610086919061022c565b60405180910390f35b6100a960048036038101906100a491906102d6565b6100ce565b6040516100b6919061022c565b60405180910390f35b60005481565b60008054905090565b6000808373ffffffffffffffffffffffffffffffffffffffff16836040516024016100f9919061022c565b6040516020818303038152906040527f6466414b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516101839190610387565b600060405180830381855af49150503d80600081146101be576040519150601f19603f3d011682016040523d82523d6000602084013e6101c3565b606091505b5050905080610207576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101fe90610421565b60405180910390fd5b60005491505092915050565b6000819050919050565b61022681610213565b82525050565b6000602082019050610241600083018461021d565b92915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102778261024c565b9050919050565b6102878161026c565b811461029257600080fd5b50565b6000813590506102a48161027e565b92915050565b6102b381610213565b81146102be57600080fd5b50565b6000813590506102d0816102aa565b92915050565b600080604083850312156102ed576102ec610247565b5b60006102fb85828601610295565b925050602061030c858286016102c1565b9150509250929050565b600081519050919050565b600081905092915050565b60005b8381101561034a57808201518184015260208101905061032f565b60008484015250505050565b600061036182610316565b61036b8185610321565b935061037b81856020860161032c565b80840191505092915050565b60006103938284610356565b915081905092915050565b600082825260208201905092915050565b7f4572726f72206d6573736167653a2044656c656761746563616c6c206661696c60008201527f6564000000000000000000000000000000000000000000000000000000000000602082015250565b600061040b60228361039e565b9150610416826103af565b604082019050919050565b6000602082019050818103600083015261043a816103fe565b905091905056fea26469706673582212203663909b8221e9b87047be99420c00339af1430c085260df209b909ed8e0f05164736f6c63430008110033 608060405234801561001057600080fd5b5061087e806100206000396000f3fe6080604052600436106100555760003560e01c80630712ede21461005a57806361bc221a1461008a5780637da3c3ab146100b55780638ada066e146100cc578063bed56f47146100f7578063d1e0f30814610127575b600080fd5b610074600480360381019061006f919061060f565b610157565b604051610081919061065e565b60405180910390f35b34801561009657600080fd5b5061009f610298565b6040516100ac919061065e565b60405180910390f35b3480156100c157600080fd5b506100ca61029e565b005b3480156100d857600080fd5b506100e16102e1565b6040516100ee919061065e565b60405180910390f35b610111600480360381019061010c919061060f565b6102ea565b60405161011e919061065e565b60405180910390f35b610141600480360381019061013c919061060f565b610431565b60405161014e919061065e565b60405180910390f35b6000808373ffffffffffffffffffffffffffffffffffffffff1683604051602401610182919061065e565b6040516020818303038152906040527f6466414b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161020c91906106ea565b600060405180830381855af49150503d8060008114610247576040519150601f19603f3d011682016040523d82523d6000602084013e61024c565b606091505b505090506000610291576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102889061075e565b60405180910390fd5b5092915050565b60005481565b60006102df576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102d69061075e565b60405180910390fd5b565b60008054905090565b6000808373ffffffffffffffffffffffffffffffffffffffff16848460405160240161031792919061078d565b6040516020818303038152906040527fbed56f47000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516103a191906106ea565b600060405180830381855af49150503d80600081146103dc576040519150601f19603f3d011682016040523d82523d6000602084013e6103e1565b606091505b5050905080610425576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161041c90610828565b60405180910390fd5b60005491505092915050565b6000808373ffffffffffffffffffffffffffffffffffffffff168360405160240161045c919061065e565b6040516020818303038152906040527f6466414b000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516104e691906106ea565b600060405180830381855af49150503d8060008114610521576040519150601f19603f3d011682016040523d82523d6000602084013e610526565b606091505b505090508061056a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161056190610828565b60405180910390fd5b60005491505092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006105a68261057b565b9050919050565b6105b68161059b565b81146105c157600080fd5b50565b6000813590506105d3816105ad565b92915050565b6000819050919050565b6105ec816105d9565b81146105f757600080fd5b50565b600081359050610609816105e3565b92915050565b6000806040838503121561062657610625610576565b5b6000610634858286016105c4565b9250506020610645858286016105fa565b9150509250929050565b610658816105d9565b82525050565b6000602082019050610673600083018461064f565b92915050565b600081519050919050565b600081905092915050565b60005b838110156106ad578082015181840152602081019050610692565b60008484015250505050565b60006106c482610679565b6106ce8185610684565b93506106de81856020860161068f565b80840191505092915050565b60006106f682846106b9565b915081905092915050565b600082825260208201905092915050565b7f696e74656e74696f6e616c6c79207468726f77696e67206572726f7200000000600082015250565b6000610748601c83610701565b915061075382610712565b602082019050919050565b600060208201905081810360008301526107778161073b565b9050919050565b6107878161059b565b82525050565b60006040820190506107a2600083018561077e565b6107af602083018461064f565b9392505050565b7f4572726f72206d6573736167653a2044656c656761746563616c6c206661696c60008201527f6564000000000000000000000000000000000000000000000000000000000000602082015250565b6000610812602283610701565b915061081d826107b6565b604082019050919050565b6000602082019050818103600083015261084181610805565b905091905056fea2646970667358221220b2a3ae7e2a9ffc78e3e2a7aadb2885435c5e51aa9d3f07372a0dffb6238aa1db64736f6c63430008110033

View File

@ -14,4 +14,20 @@ contract DelegatecallStorage {
require(success, 'Error message: Delegatecall failed'); require(success, 'Error message: Delegatecall failed');
return counter; return counter;
} }
function setVarsSelf(address _contract, uint _counter) public payable returns (uint){
(bool success, ) = _contract.delegatecall(
abi.encodeWithSignature("setVarsSelf(address,uint256)", _contract, _counter)
);
require(success, 'Error message: Delegatecall failed');
return counter;
}
function setVarsRevert(address _contract, uint _counter) public payable returns (uint){
(bool success, ) = _contract.delegatecall(
abi.encodeWithSignature("setVars(uint256)", _counter)
);
require(false,"intentionally throwing error");
}
function revert() public{
require(false,"intentionally throwing error");
}
} }

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b5061027c806100206000396000f3fe6080604052600436106100385760003560e01c80630bc07a88146100435780633da767881461005a578063f0ba84401461008557610039565b5b6100416100c2565b005b34801561004f57600080fd5b506100586100c2565b005b34801561006657600080fd5b5061006f61010d565b60405161007c9190610156565b60405180910390f35b34801561009157600080fd5b506100ac60048036038101906100a791906101a2565b610119565b6040516100b99190610156565b60405180910390f35b60005b606481101561010a5760008190806001815401808255809150506001900390600052602060002001600090919091909150558080610102906101fe565b9150506100c5565b50565b60008080549050905090565b6000818154811061012957600080fd5b906000526020600020016000915090505481565b6000819050919050565b6101508161013d565b82525050565b600060208201905061016b6000830184610147565b92915050565b600080fd5b61017f8161013d565b811461018a57600080fd5b50565b60008135905061019c81610176565b92915050565b6000602082840312156101b8576101b7610171565b5b60006101c68482850161018d565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006102098261013d565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361023b5761023a6101cf565b5b60018201905091905056fea2646970667358221220c56d78e0c60a01681eee1b76c95e7b214d16a512c944e31cfee71eb727c1e44064736f6c63430008110033

View File

@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract GasLimitTest {
address payable receiver;
constructor(){
address mynew = address(new GasLimitTestReceiver());
receiver = payable(mynew);
}
function send() public payable{
receiver.transfer(msg.value);
}
function expensiveTest() public{
GasLimitTestReceiver(receiver).expensive();
}
function getDataLength() public returns (uint256) {
return GasLimitTestReceiver(receiver).getDataLength();
}
}
contract GasLimitTestReceiver {
uint256[] public data;
fallback() external payable {
expensive();
}
function expensive() public{
for (uint256 i = 0; i < 100; i++) {
data.push(i);
}
}
function getDataLength() public view returns (uint256) {
return data.length;
}
}

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b5060b68061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80630c55699c14602d575b600080fd5b60336047565b604051603e91906067565b60405180910390f35b60006007905090565b6000819050919050565b6061816050565b82525050565b6000602082019050607a6000830184605a565b9291505056fea2646970667358221220c0f2da1b01178b54afba1ddf14f30307a03cdb66f61b4e1dc342079561db009064736f6c63430008110033

View File

@ -0,0 +1,9 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract GasLimitTestReceiver {
function x() public returns (uint256){
return 7;
}
}

View File

@ -0,0 +1 @@
6080604052348015600f57600080fd5b50604780601d6000396000f3fe6080604052348015600f57600080fd5b00fea26469706673582212200cd38951eddebe3692dc8921afb65a04fbe64e10d5e261806330156459bf227264736f6c63430008110033

View File

@ -0,0 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17;
//sending eth should fall because fallback is not payable
contract NotPayable {
fallback() external {}
}

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b5061025b806100206000396000f3fe60806040526004361061001e5760003560e01c8063cb7786d714610023575b600080fd5b61003d60048036038101906100389190610129565b61003f565b005b600083036100d15760008111156100cc573073ffffffffffffffffffffffffffffffffffffffff1663cb7786d7838460018561007b91906101ab565b6040518463ffffffff1660e01b8152600401610099939291906101ee565b600060405180830381600087803b1580156100b357600080fd5b505af11580156100c7573d6000803e3d6000fd5b505050505b6100e9565b6100e86001846100e191906101ab565b838361003f565b5b505050565b600080fd5b6000819050919050565b610106816100f3565b811461011157600080fd5b50565b600081359050610123816100fd565b92915050565b600080600060608486031215610142576101416100ee565b5b600061015086828701610114565b935050602061016186828701610114565b925050604061017286828701610114565b9150509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006101b6826100f3565b91506101c1836100f3565b92508282039050818111156101d9576101d861017c565b5b92915050565b6101e8816100f3565b82525050565b600060608201905061020360008301866101df565b61021060208301856101df565b61021d60408301846101df565b94935050505056fea26469706673582212209a21ff59c642e2970917c07bf498271c2a6df8e3929677952c0c2d8031db15cc64736f6c63430008110033

View File

@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract StackRecCall {
function exec1(uint256 n, uint256 m, uint256 r) public payable {
if(n == 0) {
if(r > 0) {
StackRecCall(address(this)).exec1(m, m, r-1);
}
return;
}
exec1(n-1, m, r);
}
}

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b506102d9806100206000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c8063032cec451461005c57806372536f3c1461007a57806399fdb86e14610098578063d2aac3ea146100b6578063ec49254c146100d4575b600080fd5b610064610104565b60405161007191906101c7565b60405180910390f35b610082610115565b60405161008f91906101c7565b60405180910390f35b6100a0610126565b6040516100ad91906101c7565b60405180910390f35b6100be610137565b6040516100cb91906101c7565b60405180910390f35b6100ee60048036038101906100e99190610213565b610148565b6040516100fb91906101c7565b60405180910390f35b60006101106001610148565b905090565b6000610121600a610148565b905090565b60006101326002610148565b905090565b60006101436000610148565b905090565b6000808211156101a5577f3110e0ccd510fcbb471c933ad12161c459e8735b5bde2eea61a659c2e2f0a3cc8260405161018191906101c7565b60405180910390a161019e600183610199919061026f565b610148565b90506101a9565b8190505b919050565b6000819050919050565b6101c1816101ae565b82525050565b60006020820190506101dc60008301846101b8565b92915050565b600080fd5b6101f0816101ae565b81146101fb57600080fd5b50565b60008135905061020d816101e7565b92915050565b600060208284031215610229576102286101e2565b5b6000610237848285016101fe565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061027a826101ae565b9150610285836101ae565b925082820390508181111561029d5761029c610240565b5b9291505056fea26469706673582212206178e15eb87e2f766b94ec09a6a860878c93d72a31de225e1684da1755f917c764736f6c63430008110033

View File

@ -0,0 +1,26 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract Recursive {
event RecursiveCallEvent(uint256 count);
function recursive10() public returns (uint256){
return recursiveCall(10);
}
function recursive2() public returns (uint256){
return recursiveCall(2);
}
function recursive1() public returns (uint256){
return recursiveCall(1);
}
function recursive0() public returns (uint256){
return recursiveCall(0);
}
function recursiveCall(uint256 count) public returns (uint256) {
if (count > 0) {
emit RecursiveCallEvent(count);
return recursiveCall(count-1);
}
return count;
}
}

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b50610459806100206000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c80633af3f24f1461003b578063ec49254c14610059575b600080fd5b610043610089565b6040516100509190610221565b60405180910390f35b610073600480360381019061006e919061026d565b61008f565b6040516100809190610221565b60405180910390f35b60005481565b60007faab69767807d0ab32f0099452739da31b76ecd3e8694bb49898829c8bf9d063582306040516100c29291906102db565b60405180910390a160016000808282546100dc9190610333565b9250508190555060018211156101ff576001826100f99190610367565b91506000803073ffffffffffffffffffffffffffffffffffffffff16846040516024016101269190610221565b6040516020818303038152906040527fec49254c000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506040516101b0919061040c565b600060405180830381855af49150503d80600081146101eb576040519150601f19603f3d011682016040523d82523d6000602084013e6101f0565b606091505b50915091508392505050610203565b8190505b919050565b6000819050919050565b61021b81610208565b82525050565b60006020820190506102366000830184610212565b92915050565b600080fd5b61024a81610208565b811461025557600080fd5b50565b60008135905061026781610241565b92915050565b6000602082840312156102835761028261023c565b5b600061029184828501610258565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102c58261029a565b9050919050565b6102d5816102ba565b82525050565b60006040820190506102f06000830185610212565b6102fd60208301846102cc565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061033e82610208565b915061034983610208565b925082820190508082111561036157610360610304565b5b92915050565b600061037282610208565b915061037d83610208565b925082820390508181111561039557610394610304565b5b92915050565b600081519050919050565b600081905092915050565b60005b838110156103cf5780820151818401526020810190506103b4565b60008484015250505050565b60006103e68261039b565b6103f081856103a6565b93506104008185602086016103b1565b80840191505092915050565b600061041882846103db565b91508190509291505056fea2646970667358221220e70fbbfaccd3fbb084623d6d06895fba1abc5fefc181215b56ab1e43db79c7fb64736f6c63430008110033

View File

@ -0,0 +1,21 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17;
contract RecursiveDelegatecall {
event RecursiveCallEvent(uint256 count, address self);
uint256 public totalCalls;
function recursiveCall(uint256 count) public returns (uint256) {
emit RecursiveCallEvent(count, address(this));
totalCalls += 1;
if (count > 1) {
count -= 1;
(bool success, bytes memory returnedData) = address(this)
.delegatecall(
abi.encodeWithSignature("recursiveCall(uint256)", count)
);
return count;
}
return count;
}
}

View File

@ -0,0 +1 @@
6080604052348015600f57600080fd5b5060848061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806383197ef014602d575b600080fd5b60336035565b005b3373ffffffffffffffffffffffffffffffffffffffff16fffea2646970667358221220d4aa109d42268586e7ce4f0fafb0ebbd04c412c6c7e8c387b009a08ecdff864264736f6c63430008110033

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17;
contract SelfDestruct {
function destroy() public {
selfdestruct(payable(msg.sender));
}
}

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b50610162806100206000396000f3fe60806040526004361061001e5760003560e01c8063c38e07dd14610023575b600080fd5b61003d6004803603810190610038919061009c565b61003f565b005b600081031561005e5761005d60018261005891906100f8565b61003f565b5b50565b600080fd5b6000819050919050565b61007981610066565b811461008457600080fd5b50565b60008135905061009681610070565b92915050565b6000602082840312156100b2576100b1610061565b5b60006100c084828501610087565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061010382610066565b915061010e83610066565b9250828203905081811115610126576101256100c9565b5b9291505056fea2646970667358221220ee8f18bfd33b1e0156cfe68e9071dd32960b370c7e63ec53c62dd48e28cb5d3b64736f6c63430008110033

View File

@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
contract StackSelf {
function exec1(uint256 n) public payable {
if(n == 0) {
return;
}
exec1(n-1);
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,211 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;
pragma experimental ABIEncoderV2;
contract Test_contract {
uint256 timestamp;
address sender;
string text;
uint256 number;
constructor(string memory _text, uint256 _number) {
sender = tx.origin;
timestamp = block.timestamp;
text = _text;
number = _number;
}
function getall()
public
view
returns (
address,
uint256,
address,
string memory,
uint256
)
{
return (address(this), timestamp, sender, text, number);
}
function get_timestamp() public view returns (uint256) {
return timestamp;
}
function get_sender() public view returns (address) {
return sender;
}
function get_text() public view returns (string memory) {
return text;
}
function get_number() public view returns (uint256) {
return number;
}
}
contract App {
address[] Test_list;
uint256 Test_list_length;
function get_Test_list_length() public view returns (uint256) {
return Test_list_length;
}
struct Test_getter {
address _address;
uint256 timestamp;
address sender;
string text;
uint256 number;
}
function get_Test_N(uint256 index)
public
view
returns (
address,
uint256,
address,
string memory,
uint256
)
{
return Test_contract(Test_list[index]).getall();
}
function get_first_Test_N(uint256 count, uint256 offset)
public
view
returns (Test_getter[] memory)
{
Test_getter[] memory getters = new Test_getter[](count);
for (uint256 i = offset; i < count; i++) {
Test_contract myTest = Test_contract(Test_list[i + offset]);
getters[i - offset]._address = address(myTest);
getters[i - offset].timestamp = myTest.get_timestamp();
getters[i - offset].sender = myTest.get_sender();
getters[i - offset].text = myTest.get_text();
getters[i - offset].number = myTest.get_number();
}
return getters;
}
function get_last_Test_N(uint256 count, uint256 offset)
public
view
returns (Test_getter[] memory)
{
Test_getter[] memory getters = new Test_getter[](count);
for (uint256 i = 0; i < count; i++) {
Test_contract myTest =
Test_contract(Test_list[Test_list_length - i - offset - 1]);
getters[i]._address = address(myTest);
getters[i].timestamp = myTest.get_timestamp();
getters[i].sender = myTest.get_sender();
getters[i].text = myTest.get_text();
getters[i].number = myTest.get_number();
}
return getters;
}
function get_Test_user_length(address user) public view returns (uint256) {
return user_map[user].Test_list_length;
}
function get_Test_user_N(address user, uint256 index)
public
view
returns (
address,
uint256,
address,
string memory,
uint256
)
{
return Test_contract(user_map[user].Test_list[index]).getall();
}
function get_last_Test_user_N(
address user,
uint256 count,
uint256 offset
) public view returns (Test_getter[] memory) {
Test_getter[] memory getters = new Test_getter[](count);
for (uint256 i = offset; i < count; i++) {
getters[i - offset]._address = user_map[user].Test_list[i + offset];
getters[i - offset].timestamp = Test_contract(
user_map[user].Test_list[i + offset]
)
.get_timestamp();
getters[i - offset].sender = Test_contract(
user_map[user].Test_list[i + offset]
)
.get_sender();
getters[i - offset].text = Test_contract(
user_map[user].Test_list[i + offset]
)
.get_text();
getters[i - offset].number = Test_contract(
user_map[user].Test_list[i + offset]
)
.get_number();
}
return getters;
}
struct UserInfo {
address owner;
bool exists;
address[] Test_list;
uint256 Test_list_length;
}
mapping(address => UserInfo) public user_map;
address[] UserInfoList;
uint256 UserInfoListLength;
event NewTest(address sender);
function new_Test(string memory text, uint256 number)
public
returns (address)
{
address mynew =
address(new Test_contract({_text: text, _number: number}));
if (!user_map[tx.origin].exists) {
user_map[tx.origin] = create_user_on_new_Test(mynew);
}
user_map[tx.origin].Test_list.push(mynew);
user_map[tx.origin].Test_list_length += 1;
Test_list.push(mynew);
Test_list_length += 1;
emit NewTest(tx.origin);
return mynew;
}
function create_user_on_new_Test(address addr)
private
returns (UserInfo memory)
{
address[] memory Test_list_;
UserInfoList.push(addr);
return
UserInfo({
exists: true,
owner: addr,
Test_list: Test_list_,
Test_list_length: 0
});
}
}

View File

@ -0,0 +1 @@
608060405234801561001057600080fd5b506106dc806100206000396000f3fe60806040526004361061002d5760003560e01c8063dbdc275d14610072578063fd75295a1461009d5761006d565b3661006d577f34fb9cb8f63eaccc6c0beefc202db703e529c3bf9ce83f485b398af7fd679308333460405161006392919061020f565b60405180910390a1005b600080fd5b34801561007e57600080fd5b506100876100b9565b6040516100949190610238565b60405180910390f35b6100b760048036038101906100b29190610296565b610125565b005b6000806040516100c8906101a8565b604051809103906000f0801580156100e4573d6000803e3d6000fd5b5090507f8db3b20eed31d927a4f613b5c11765212e129cf726d025649650665569ea683b816040516101169190610238565b60405180910390a18091505090565b7f34fb9cb8f63eaccc6c0beefc202db703e529c3bf9ce83f485b398af7fd6793088134604051610156929190610322565b60405180910390a18073ffffffffffffffffffffffffffffffffffffffff166108fc349081150290604051600060405180830381858888f193505050501580156101a4573d6000803e3d6000fd5b5050565b61035b8061034c83390190565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101e0826101b5565b9050919050565b6101f0816101d5565b82525050565b6000819050919050565b610209816101f6565b82525050565b600060408201905061022460008301856101e7565b6102316020830184610200565b9392505050565b600060208201905061024d60008301846101e7565b92915050565b600080fd5b6000610263826101b5565b9050919050565b61027381610258565b811461027e57600080fd5b50565b6000813590506102908161026a565b92915050565b6000602082840312156102ac576102ab610253565b5b60006102ba84828501610281565b91505092915050565b6000819050919050565b60006102e86102e36102de846101b5565b6102c3565b6101b5565b9050919050565b60006102fa826102cd565b9050919050565b600061030c826102ef565b9050919050565b61031c81610301565b82525050565b60006040820190506103376000830185610313565b6103446020830184610200565b939250505056fe608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102fb806100606000396000f3fe60806040526004361061002d5760003560e01c806337cb6570146100725780639cb8a26a1461007c5761006d565b3661006d577fe1494f56a1ccfd8c7361f2ca5b8fd2b1a2fe11773821ac29534f09f4a0637d603334604051610063929190610214565b60405180910390a1005b600080fd5b61007a610093565b005b34801561008857600080fd5b50610091610155565b005b7f76cff203b0794a9e9013657ecb172f2d6e8de5941fd11a6bfbc0fb6014a5f07960008054906101000a900473ffffffffffffffffffffffffffffffffffffffff16476040516100e492919061029c565b60405180910390a160008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166108fc479081150290604051600060405180830381858888f19350505050158015610152573d6000803e3d6000fd5b50565b7f1cbd47e7b0f55dc1a45ba8ebada53eaa1712b3e3e86f49a12c008ff22334569060405160405180910390a160008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16ff5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101e5826101ba565b9050919050565b6101f5816101da565b82525050565b6000819050919050565b61020e816101fb565b82525050565b600060408201905061022960008301856101ec565b6102366020830184610205565b9392505050565b6000819050919050565b600061026261025d610258846101ba565b61023d565b6101ba565b9050919050565b600061027482610247565b9050919050565b600061028682610269565b9050919050565b6102968161027b565b82525050565b60006040820190506102b1600083018561028d565b6102be6020830184610205565b939250505056fea2646970667358221220bdf21908b1c91973a8440fe81130e0077cf83c8f8e9a053d8a0c3063391aa40764736f6c63430008110033a26469706673582212202e514fe078dfcf4f1142a088c57cfa71ada74d72ee0cc4a46b7e1141cdbaeeed64736f6c63430008110033

View File

@ -0,0 +1,54 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.17;
contract A {
event LogCreateB(address _bAddress);
event LogSendEthA(address _bAddress, uint _value);
event LogReceiveEthA(address _bAddress, uint _value);
// Function to create a new instance of contract B and return its address
function createB() public returns (address) {
address bAddress = address(new B());
emit LogCreateB(bAddress);
return bAddress;
}
// Payable method to accept eth and an address for B and send the eth to B
function sendEthToB(address payable _bAddress) public payable {
emit LogSendEthA(_bAddress, msg.value);
_bAddress.transfer(msg.value);
}
// Payable function to accept the eth
receive() external payable {
emit LogSendEthA(msg.sender, msg.value);
}
}
contract B {
event LogSelfDestruct();
event LogSendEthToA(address _to, uint _value);
event LogReceiveEth(address from, uint value);
address payable creator;
constructor(){
creator = payable(msg.sender);
}
// Payable function to accept the eth
receive() external payable {
emit LogReceiveEth(msg.sender, msg.value);
}
// Method to send ether to contract A
function sendEthToA() public payable {
emit LogSendEthToA(creator,address(this).balance);
creator.transfer(address(this).balance);
}
// Self destruct method to send eth to its creator
function selfDestruct() public {
emit LogSelfDestruct();
selfdestruct(creator);
}
}

View File

@ -1,6 +1,19 @@
set -eu
set -o pipefail
#use the solc compiler https://docs.soliditylang.org/en/v0.8.17/installing-solidity.html #use the solc compiler https://docs.soliditylang.org/en/v0.8.17/installing-solidity.html
# to compile all of the .sol files to their corresponding evm binary files stored as .hex # to compile all of the .sol files to their corresponding evm binary files stored as .hex
# solc outputs to stdout a format that we just want to grab the last line of and then remove the trailing newline on that line # solc outputs to stdout a format that we just want to grab the last line of and then remove the trailing newline on that line
find -type f -name \*.sol -print0 | find . -name \*.sol -print0 |
xargs -0 -I{} bash -c 'solc --bin {} |tail -n1 | tr -d "\n" > $(echo {} | sed -e s/.sol$/.hex/)' xargs -0 -I{} bash -euc -o pipefail 'solc --bin {} |tail -n1 | tr -d "\n" > $(echo {} | sed -e s/.sol$/.hex/)'
#for these contracts we have 2 contracts in the same solidity file
#this command grabs the correct bytecode for us
for filename in Constructor TestApp ValueSender ; do
echo $filename
solc --bin $filename.sol | tail -n5|head -n1 | tr -d "\n" > $filename.hex
done

View File

@ -11,6 +11,8 @@ import (
"github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/big"
builtin2 "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/exitcode" "github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
@ -70,7 +72,7 @@ func TestEthAccountAbstraction(t *testing.T) {
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK) msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
require.NoError(t, err) require.NoError(t, err)
txArgs, err := ethtypes.EthTxArgsFromMessage(msgFromPlaceholder) txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder)
require.NoError(t, err) require.NoError(t, err)
digest, err := txArgs.ToRlpUnsignedMsg() digest, err := txArgs.ToRlpUnsignedMsg()
@ -106,7 +108,7 @@ func TestEthAccountAbstraction(t *testing.T) {
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK) msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
require.NoError(t, err) require.NoError(t, err)
txArgs, err = ethtypes.EthTxArgsFromMessage(msgFromPlaceholder) txArgs, err = ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder)
require.NoError(t, err) require.NoError(t, err)
digest, err = txArgs.ToRlpUnsignedMsg() digest, err = txArgs.ToRlpUnsignedMsg()
@ -178,7 +180,7 @@ func TestEthAccountAbstractionFailure(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
msgFromPlaceholder.Value = abi.TokenAmount(types.MustParseFIL("1000")) msgFromPlaceholder.Value = abi.TokenAmount(types.MustParseFIL("1000"))
txArgs, err := ethtypes.EthTxArgsFromMessage(msgFromPlaceholder) txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder)
require.NoError(t, err) require.NoError(t, err)
digest, err := txArgs.ToRlpUnsignedMsg() digest, err := txArgs.ToRlpUnsignedMsg()
@ -216,7 +218,7 @@ func TestEthAccountAbstractionFailure(t *testing.T) {
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK) msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
require.NoError(t, err) require.NoError(t, err)
txArgs, err = ethtypes.EthTxArgsFromMessage(msgFromPlaceholder) txArgs, err = ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder)
require.NoError(t, err) require.NoError(t, err)
digest, err = txArgs.ToRlpUnsignedMsg() digest, err = txArgs.ToRlpUnsignedMsg()
@ -313,3 +315,51 @@ func TestEthAccountAbstractionFailsFromEvmActor(t *testing.T) {
require.Error(t, err, "expected gas estimation to fail") require.Error(t, err, "expected gas estimation to fail")
require.Contains(t, err.Error(), "SysErrSenderInvalid") require.Contains(t, err.Error(), "SysErrSenderInvalid")
} }
func TestEthAccountManagerPermissions(t *testing.T) {
kit.QuietMiningLogs()
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(10 * time.Millisecond)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel()
// setup f1/f3/f4 accounts
wsp, err := client.WalletNew(ctx, types.KTSecp256k1)
require.NoError(t, err)
wbl, err := client.WalletNew(ctx, types.KTBLS)
require.NoError(t, err)
wdl, err := client.WalletNew(ctx, types.KTDelegated)
require.NoError(t, err)
def := client.DefaultKey.Address
// send some funds
client.ExpectSend(ctx, def, wsp, types.FromFil(10), "")
client.ExpectSend(ctx, def, wbl, types.FromFil(10), "")
client.ExpectSend(ctx, def, wdl, types.FromFil(10), "")
require.NoError(t, err)
// make sure that EAM only allows CreateExternal to be called by accounts
client.ExpectSend(ctx, wsp, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create, &eam.CreateParams{Nonce: 0}))
client.ExpectSend(ctx, wbl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create, &eam.CreateParams{Nonce: 0}))
client.ExpectSend(ctx, wdl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create, &eam.CreateParams{Nonce: 0}))
client.ExpectSend(ctx, wsp, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create2, &eam.Create2Params{}))
client.ExpectSend(ctx, wbl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create2, &eam.Create2Params{}))
client.ExpectSend(ctx, wdl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "not one of supported (18)", client.MakeSendCall(builtin2.MethodsEAM.Create2, &eam.Create2Params{}))
contractHex, err := os.ReadFile("contracts/SimpleCoin.hex")
require.NoError(t, err)
contract, err := hex.DecodeString(string(contractHex))
require.NoError(t, err)
contractParams := abi.CborBytes(contract)
client.ExpectSend(ctx, wsp, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "", client.MakeSendCall(builtin2.MethodsEAM.CreateExternal, &contractParams))
client.ExpectSend(ctx, wbl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "", client.MakeSendCall(builtin2.MethodsEAM.CreateExternal, &contractParams))
client.ExpectSend(ctx, wdl, builtin2.EthereumAddressManagerActorAddr, big.Zero(), "", client.MakeSendCall(builtin2.MethodsEAM.CreateExternal, &contractParams))
}

62
itests/eth_config_test.go Normal file
View File

@ -0,0 +1,62 @@
// stm: #integration
package itests
import (
"context"
"testing"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
"github.com/filecoin-project/lotus/itests/kit"
"github.com/filecoin-project/lotus/node/impl/full"
)
func TestEthFilterAPIDisabledViaConfig(t *testing.T) {
ctx := context.Background()
kit.QuietMiningLogs()
// pass kit.DisableEthRPC() so RealTimeFilterAPI will not be enabled
client, _, _ := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.DisableEthRPC())
_, err := client.EthNewPendingTransactionFilter(ctx)
require.NotNil(t, err)
require.Equal(t, err.Error(), full.ErrModuleDisabled.Error())
_, err = client.EthGetLogs(ctx, &ethtypes.EthFilterSpec{})
require.NotNil(t, err)
require.Equal(t, err.Error(), full.ErrModuleDisabled.Error())
_, err = client.EthGetFilterChanges(ctx, ethtypes.EthFilterID{})
require.NotNil(t, err)
require.Equal(t, err.Error(), full.ErrModuleDisabled.Error())
_, err = client.EthGetFilterLogs(ctx, ethtypes.EthFilterID{})
require.NotNil(t, err)
require.Equal(t, err.Error(), full.ErrModuleDisabled.Error())
_, err = client.EthNewFilter(ctx, &ethtypes.EthFilterSpec{})
require.NotNil(t, err)
require.Equal(t, err.Error(), full.ErrModuleDisabled.Error())
_, err = client.EthNewBlockFilter(ctx)
require.NotNil(t, err)
require.Equal(t, err.Error(), full.ErrModuleDisabled.Error())
_, err = client.EthNewPendingTransactionFilter(ctx)
require.NotNil(t, err)
require.Equal(t, err.Error(), full.ErrModuleDisabled.Error())
_, err = client.EthUninstallFilter(ctx, ethtypes.EthFilterID{})
require.NotNil(t, err)
require.Equal(t, err.Error(), full.ErrModuleDisabled.Error())
_, err = client.EthSubscribe(ctx, []byte("{}"))
require.NotNil(t, err)
require.Equal(t, err.Error(), full.ErrModuleDisabled.Error())
_, err = client.EthUnsubscribe(ctx, ethtypes.EthSubscriptionID{})
require.NotNil(t, err)
require.Equal(t, err.Error(), full.ErrModuleDisabled.Error())
}

File diff suppressed because it is too large Load Diff

View File

@ -83,6 +83,23 @@ func TestValueTransferValidSignature(t *testing.T) {
// Success. // Success.
require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status) require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status)
ethTx, err := client.EthGetTransactionByHash(ctx, &hash)
require.Nil(t, err)
require.EqualValues(t, ethAddr, ethTx.From)
require.EqualValues(t, ethAddr2, *ethTx.To)
require.EqualValues(t, tx.ChainID, ethTx.ChainID)
require.EqualValues(t, tx.Nonce, ethTx.Nonce)
require.EqualValues(t, hash, ethTx.Hash)
require.EqualValues(t, tx.Value, ethTx.Value)
require.EqualValues(t, 2, ethTx.Type)
require.EqualValues(t, ethtypes.EthBytes{}, ethTx.Input)
require.EqualValues(t, tx.GasLimit, ethTx.Gas)
require.EqualValues(t, tx.MaxFeePerGas, ethTx.MaxFeePerGas)
require.EqualValues(t, tx.MaxPriorityFeePerGas, ethTx.MaxPriorityFeePerGas)
require.EqualValues(t, tx.V, ethTx.V)
require.EqualValues(t, tx.R, ethTx.R)
require.EqualValues(t, tx.S, ethTx.S)
} }
func TestLegacyTransaction(t *testing.T) { func TestLegacyTransaction(t *testing.T) {

View File

@ -6,7 +6,6 @@ import (
"encoding/hex" "encoding/hex"
"os" "os"
"testing" "testing"
"time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
@ -44,14 +43,43 @@ func effectiveEthAddressForCreate(t *testing.T, sender address.Address) ethtypes
panic("unreachable") panic("unreachable")
} }
func createAndDeploy(ctx context.Context, t *testing.T, client *kit.TestFullNode, fromAddr address.Address, contract []byte) *api.MsgLookup {
// Create and deploy evm actor
method := builtintypes.MethodsEAM.CreateExternal
contractParams := abi.CborBytes(contract)
params, actorsErr := actors.SerializeParams(&contractParams)
require.NoError(t, actorsErr)
createMsg := &types.Message{
To: builtintypes.EthereumAddressManagerActorAddr,
From: fromAddr,
Value: big.Zero(),
Method: method,
Params: params,
}
smsg, err := client.MpoolPushMessage(ctx, createMsg, nil)
require.NoError(t, err)
wait, err := client.StateWaitMsg(ctx, smsg.Cid(), 0, 0, false)
require.NoError(t, err)
require.Equal(t, exitcode.Ok, wait.Receipt.ExitCode)
return wait
}
func getEthAddressTX(ctx context.Context, t *testing.T, client *kit.TestFullNode, wait *api.MsgLookup, ethAddr ethtypes.EthAddress) ethtypes.EthAddress {
// Check if eth address returned from CreateExternal is the same as eth address predicted at the start
var createExternalReturn eam.CreateExternalReturn
err := createExternalReturn.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return))
require.NoError(t, err)
createdEthAddr, err := ethtypes.CastEthAddress(createExternalReturn.EthAddress[:])
require.NoError(t, err)
return createdEthAddr
}
func TestAddressCreationBeforeDeploy(t *testing.T) { func TestAddressCreationBeforeDeploy(t *testing.T) {
kit.QuietMiningLogs() ctx, cancel, client := kit.SetupFEVMTest(t)
blockTime := 100 * time.Millisecond
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
defer cancel() defer cancel()
// install contract // install contract
@ -72,22 +100,11 @@ func TestAddressCreationBeforeDeploy(t *testing.T) {
contractFilAddr, err := ethAddr.ToFilecoinAddress() contractFilAddr, err := ethAddr.ToFilecoinAddress()
require.NoError(t, err) require.NoError(t, err)
// Send contract address some funds //transfer half the wallet balance
bal, err := client.WalletBalance(ctx, client.DefaultKey.Address) bal, err := client.WalletBalance(ctx, client.DefaultKey.Address)
require.NoError(t, err) require.NoError(t, err)
sendAmount := big.Div(bal, big.NewInt(2)) sendAmount := big.Div(bal, big.NewInt(2))
client.EVM().TransferValueOrFail(ctx, fromAddr, contractFilAddr, sendAmount)
sendMsg := &types.Message{
From: fromAddr,
To: contractFilAddr,
Value: sendAmount,
}
signedMsg, err := client.MpoolPushMessage(ctx, sendMsg, nil)
require.NoError(t, err)
mLookup, err := client.StateWaitMsg(ctx, signedMsg.Cid(), 3, api.LookbackNoLimit, true)
require.NoError(t, err)
require.Equal(t, exitcode.Ok, mLookup.Receipt.ExitCode)
// Check if actor at new address is a placeholder actor // Check if actor at new address is a placeholder actor
actor, err := client.StateGetActor(ctx, contractFilAddr, types.EmptyTSK) actor, err := client.StateGetActor(ctx, contractFilAddr, types.EmptyTSK)
@ -95,40 +112,69 @@ func TestAddressCreationBeforeDeploy(t *testing.T) {
require.True(t, builtin.IsPlaceholderActor(actor.Code)) require.True(t, builtin.IsPlaceholderActor(actor.Code))
// Create and deploy evm actor // Create and deploy evm actor
wait := createAndDeploy(ctx, t, client, fromAddr, contract)
method := builtintypes.MethodsEAM.CreateExternal
contractParams := abi.CborBytes(contract)
params, err := actors.SerializeParams(&contractParams)
require.NoError(t, err)
createMsg := &types.Message{
To: builtintypes.EthereumAddressManagerActorAddr,
From: fromAddr,
Value: big.Zero(),
Method: method,
Params: params,
}
smsg, err := client.MpoolPushMessage(ctx, createMsg, nil)
require.NoError(t, err)
wait, err := client.StateWaitMsg(ctx, smsg.Cid(), 0, 0, false)
require.NoError(t, err)
require.Equal(t, exitcode.Ok, wait.Receipt.ExitCode)
// Check if eth address returned from CreateExternal is the same as eth address predicted at the start // Check if eth address returned from CreateExternal is the same as eth address predicted at the start
var createExternalReturn eam.CreateExternalReturn createdEthAddr := getEthAddressTX(ctx, t, client, wait, ethAddr)
err = createExternalReturn.UnmarshalCBOR(bytes.NewReader(wait.Receipt.Return))
require.NoError(t, err)
createdEthAddr, err := ethtypes.CastEthAddress(createExternalReturn.EthAddress[:])
require.NoError(t, err)
require.Equal(t, ethAddr, createdEthAddr) require.Equal(t, ethAddr, createdEthAddr)
// Check if newly deployed actor still has funds // Check if newly deployed actor still has funds
actorPostCreate, err := client.StateGetActor(ctx, contractFilAddr, types.EmptyTSK) actorPostCreate, err := client.StateGetActor(ctx, contractFilAddr, types.EmptyTSK)
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, actorPostCreate.Balance, sendAmount) require.Equal(t, actorPostCreate.Balance, sendAmount)
require.True(t, builtin.IsEvmActor(actorPostCreate.Code)) require.True(t, builtin.IsEvmActor(actorPostCreate.Code))
} }
func TestDeployAddressMultipleTimes(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
// install contract
contractHex, err := os.ReadFile("contracts/SimpleCoin.hex")
require.NoError(t, err)
contract, err := hex.DecodeString(string(contractHex))
require.NoError(t, err)
fromAddr, err := client.WalletDefaultAddress(ctx)
require.NoError(t, err)
// We hash the f1/f3 address into the EVM's address space when deploying contracts from
// accounts.
effectiveEvmAddress := effectiveEthAddressForCreate(t, fromAddr)
ethAddr := client.EVM().ComputeContractAddress(effectiveEvmAddress, 1)
contractFilAddr, err := ethAddr.ToFilecoinAddress()
require.NoError(t, err)
// Send contract address small funds to init
sendAmount := big.NewInt(2)
client.EVM().TransferValueOrFail(ctx, fromAddr, contractFilAddr, sendAmount)
// Check if actor at new address is a placeholder actor
actor, err := client.StateGetActor(ctx, contractFilAddr, types.EmptyTSK)
require.NoError(t, err)
require.True(t, builtin.IsPlaceholderActor(actor.Code))
// Create and deploy evm actor
wait := createAndDeploy(ctx, t, client, fromAddr, contract)
// Check if eth address returned from CreateExternal is the same as eth address predicted at the start
createdEthAddr := getEthAddressTX(ctx, t, client, wait, ethAddr)
require.Equal(t, ethAddr, createdEthAddr)
// Check if newly deployed actor still has funds
actorPostCreate, err := client.StateGetActor(ctx, contractFilAddr, types.EmptyTSK)
require.NoError(t, err)
require.Equal(t, actorPostCreate.Balance, sendAmount)
require.True(t, builtin.IsEvmActor(actorPostCreate.Code))
// Create and deploy evm actor
wait = createAndDeploy(ctx, t, client, fromAddr, contract)
// Check that this time eth address returned from CreateExternal is not the same as eth address predicted at the start
createdEthAddr = getEthAddressTX(ctx, t, client, wait, ethAddr)
require.NotEqual(t, ethAddr, createdEthAddr)
}

View File

@ -64,20 +64,23 @@ func TestFEVMEvents(t *testing.T) {
require.Empty(res.Results) require.Empty(res.Results)
// log a zero topic event with data // log a zero topic event with data
ret := client.EVM().InvokeSolidity(ctx, fromAddr, idAddr, []byte{0x00, 0x00, 0x00, 0x00}, nil) ret, err := client.EVM().InvokeSolidity(ctx, fromAddr, idAddr, []byte{0x00, 0x00, 0x00, 0x00}, nil)
require.NoError(err)
require.True(ret.Receipt.ExitCode.IsSuccess(), "contract execution failed") require.True(ret.Receipt.ExitCode.IsSuccess(), "contract execution failed")
require.NotNil(ret.Receipt.EventsRoot) require.NotNil(ret.Receipt.EventsRoot)
fmt.Println(ret) fmt.Println(ret)
fmt.Printf("Events:\n %+v\n", client.EVM().LoadEvents(ctx, *ret.Receipt.EventsRoot)) fmt.Printf("Events:\n %+v\n", client.EVM().LoadEvents(ctx, *ret.Receipt.EventsRoot))
// log a zero topic event with no data // log a zero topic event with no data
ret = client.EVM().InvokeSolidity(ctx, fromAddr, idAddr, []byte{0x00, 0x00, 0x00, 0x01}, nil) ret, err = client.EVM().InvokeSolidity(ctx, fromAddr, idAddr, []byte{0x00, 0x00, 0x00, 0x01}, nil)
require.NoError(err)
require.True(ret.Receipt.ExitCode.IsSuccess(), "contract execution failed") require.True(ret.Receipt.ExitCode.IsSuccess(), "contract execution failed")
fmt.Println(ret) fmt.Println(ret)
fmt.Printf("Events:\n %+v\n", client.EVM().LoadEvents(ctx, *ret.Receipt.EventsRoot)) fmt.Printf("Events:\n %+v\n", client.EVM().LoadEvents(ctx, *ret.Receipt.EventsRoot))
// log a four topic event with data // log a four topic event with data
ret = client.EVM().InvokeSolidity(ctx, fromAddr, idAddr, []byte{0x00, 0x00, 0x00, 0x02}, nil) ret, err = client.EVM().InvokeSolidity(ctx, fromAddr, idAddr, []byte{0x00, 0x00, 0x00, 0x02}, nil)
require.NoError(err)
require.True(ret.Receipt.ExitCode.IsSuccess(), "contract execution failed") require.True(ret.Receipt.ExitCode.IsSuccess(), "contract execution failed")
fmt.Println(ret) fmt.Println(ret)
fmt.Printf("Events:\n %+v\n", client.EVM().LoadEvents(ctx, *ret.Receipt.EventsRoot)) fmt.Printf("Events:\n %+v\n", client.EVM().LoadEvents(ctx, *ret.Receipt.EventsRoot))

View File

@ -1,15 +1,19 @@
package itests package itests
import ( import (
"bytes"
"context" "context"
"encoding/binary"
"encoding/hex" "encoding/hex"
"fmt"
"testing" "testing"
"time"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/filecoin-project/go-address" "github.com/filecoin-project/go-address"
"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/exitcode"
"github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/go-state-types/manifest"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
@ -36,19 +40,139 @@ func inputDataFromFrom(ctx context.Context, t *testing.T, client *kit.TestFullNo
return inputData return inputData
} }
func setupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *kit.TestFullNode) { func decodeOutputToUint64(output []byte) (uint64, error) {
kit.QuietMiningLogs() var result uint64
blockTime := 100 * time.Millisecond buf := bytes.NewReader(output[len(output)-8:])
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) err := binary.Read(buf, binary.BigEndian, &result)
ens.InterconnectAll().BeginMining(blockTime) return result, err
ctx, cancel := context.WithTimeout(context.Background(), time.Minute) }
return ctx, cancel, client func buildInputFromuint64(number uint64) []byte {
// Convert the number to a binary uint64 array
binaryNumber := make([]byte, 8)
binary.BigEndian.PutUint64(binaryNumber, number)
return inputDataFromArray(binaryNumber)
}
// recursive delegate calls that fail due to gas limits are currently getting to 229 iterations
// before running out of gas
func recursiveDelegatecallFail(ctx context.Context, t *testing.T, client *kit.TestFullNode, filename string, count uint64) {
expectedIterationsBeforeFailing := int(229)
fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename)
t.Log("recursion count - ", count)
inputData := buildInputFromuint64(count)
_, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "recursiveCall(uint256)", inputData)
require.NoError(t, err)
result, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "totalCalls()", []byte{})
require.NoError(t, err)
resultUint, err := decodeOutputToUint64(result)
require.NoError(t, err)
require.NotEqual(t, int(resultUint), int(count))
require.Equal(t, expectedIterationsBeforeFailing, int(resultUint))
}
func recursiveDelegatecallSuccess(ctx context.Context, t *testing.T, client *kit.TestFullNode, filename string, count uint64) {
t.Log("Count - ", count)
fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename)
inputData := buildInputFromuint64(count)
_, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "recursiveCall(uint256)", inputData)
require.NoError(t, err)
result, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "totalCalls()", []byte{})
require.NoError(t, err)
resultUint, err := decodeOutputToUint64(result)
require.NoError(t, err)
require.Equal(t, int(count), int(resultUint))
}
// TestFEVMRecursive does a basic fevm contract installation and invocation
func TestFEVMRecursive(t *testing.T) {
callCounts := []uint64{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 100, 230, 330}
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
filename := "contracts/Recursive.hex"
fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename)
// Successful calls
for _, callCount := range callCounts {
callCount := callCount // linter unhappy unless callCount is local to loop
t.Run(fmt.Sprintf("TestFEVMRecursive%d", callCount), func(t *testing.T) {
_, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "recursiveCall(uint256)", buildInputFromuint64(callCount))
require.NoError(t, err)
})
}
}
func TestFEVMRecursiveFail(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
filename := "contracts/Recursive.hex"
fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename)
// Unsuccessful calls
failCallCounts := []uint64{340, 400, 600, 850, 1000}
for _, failCallCount := range failCallCounts {
failCallCount := failCallCount // linter unhappy unless callCount is local to loop
t.Run(fmt.Sprintf("TestFEVMRecursiveFail%d", failCallCount), func(t *testing.T) {
_, wait, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "recursiveCall(uint256)", buildInputFromuint64(failCallCount))
require.Error(t, err)
require.Equal(t, exitcode.ExitCode(23), wait.Receipt.ExitCode)
})
}
}
func TestFEVMRecursive1(t *testing.T) {
callCount := 1
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
filename := "contracts/Recursive.hex"
fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename)
_, ret, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "recursive1()", []byte{})
require.NoError(t, err)
events := client.EVM().LoadEvents(ctx, *ret.Receipt.EventsRoot)
require.Equal(t, callCount, len(events))
}
func TestFEVMRecursive2(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
filename := "contracts/Recursive.hex"
fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename)
_, ret, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "recursive2()", []byte{})
require.NoError(t, err)
events := client.EVM().LoadEvents(ctx, *ret.Receipt.EventsRoot)
require.Equal(t, 2, len(events))
}
// TestFEVMBasic does a basic fevm contract installation and invocation
// recursive delegate call succeeds up to 238 times
func TestFEVMRecursiveDelegatecall(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
filename := "contracts/RecursiveDelegeatecall.hex"
//success with 238 or fewer calls
for i := uint64(1); i <= 238; i += 30 {
recursiveDelegatecallSuccess(ctx, t, client, filename, i)
}
recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(238))
for i := uint64(239); i <= 800; i += 40 {
recursiveDelegatecallFail(ctx, t, client, filename, i)
}
} }
// TestFEVMBasic does a basic fevm contract installation and invocation // TestFEVMBasic does a basic fevm contract installation and invocation
func TestFEVMBasic(t *testing.T) { func TestFEVMBasic(t *testing.T) {
ctx, cancel, client := setupFEVMTest(t) ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel() defer cancel()
filename := "contracts/SimpleCoin.hex" filename := "contracts/SimpleCoin.hex"
@ -58,7 +182,8 @@ func TestFEVMBasic(t *testing.T) {
// invoke the contract with owner // invoke the contract with owner
{ {
inputData := inputDataFromFrom(ctx, t, client, fromAddr) inputData := inputDataFromFrom(ctx, t, client, fromAddr)
result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "getBalance(address)", inputData) result, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "getBalance(address)", inputData)
require.NoError(t, err)
expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000002710") expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000002710")
require.NoError(t, err) require.NoError(t, err)
@ -68,8 +193,9 @@ func TestFEVMBasic(t *testing.T) {
// invoke the contract with non owner // invoke the contract with non owner
{ {
inputData := inputDataFromFrom(ctx, t, client, fromAddr) inputData := inputDataFromFrom(ctx, t, client, fromAddr)
inputData[31]++ // change the pub address to one that has 0 balance by incrementing the last byte of the address inputData[31]++ // change the pub address to one that has 0 balance by modifying the last byte of the address
result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "getBalance(address)", inputData) result, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "getBalance(address)", inputData)
require.NoError(t, err)
expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000") expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err) require.NoError(t, err)
@ -79,7 +205,7 @@ func TestFEVMBasic(t *testing.T) {
// TestFEVMETH0 tests that the ETH0 actor is in genesis // TestFEVMETH0 tests that the ETH0 actor is in genesis
func TestFEVMETH0(t *testing.T) { func TestFEVMETH0(t *testing.T) {
ctx, cancel, client := setupFEVMTest(t) ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel() defer cancel()
eth0id, err := address.NewIDAddress(1001) eth0id, err := address.NewIDAddress(1001)
@ -98,7 +224,47 @@ func TestFEVMETH0(t *testing.T) {
// TestFEVMDelegateCall deploys two contracts and makes a delegate call transaction // TestFEVMDelegateCall deploys two contracts and makes a delegate call transaction
func TestFEVMDelegateCall(t *testing.T) { func TestFEVMDelegateCall(t *testing.T) {
ctx, cancel, client := setupFEVMTest(t) ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract Actor
filenameActor := "contracts/DelegatecallActor.hex"
fromAddr, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor)
//install contract Storage
filenameStorage := "contracts/DelegatecallStorage.hex"
fromAddrStorage, storageAddr := client.EVM().DeployContractFromFilename(ctx, filenameStorage)
require.Equal(t, fromAddr, fromAddrStorage)
//call Contract Storage which makes a delegatecall to contract Actor
//this contract call sets the "counter" variable to 7, from default value 0
inputDataContract := inputDataFromFrom(ctx, t, client, actorAddr)
inputDataValue := inputDataFromArray([]byte{7})
inputData := append(inputDataContract, inputDataValue...)
//verify that the returned value of the call to setvars is 7
result, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "setVars(address,uint256)", inputData)
require.NoError(t, err)
expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000007")
require.NoError(t, err)
require.Equal(t, result, expectedResult)
//test the value is 7 a second way by calling the getter
result, _, err = client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "getCounter()", []byte{})
require.NoError(t, err)
require.Equal(t, result, expectedResult)
//test the value is 0 via calling the getter on the Actor contract
result, _, err = client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "getCounter()", []byte{})
require.NoError(t, err)
expectedResultActor, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err)
require.Equal(t, result, expectedResultActor)
}
// TestFEVMDelegateCallRevert makes a delegatecall action and then calls revert.
// the state should not have changed because of the revert
func TestFEVMDelegateCallRevert(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel() defer cancel()
//install contract Actor //install contract Actor
@ -117,20 +283,247 @@ func TestFEVMDelegateCall(t *testing.T) {
inputData := append(inputDataContract, inputDataValue...) inputData := append(inputDataContract, inputDataValue...)
//verify that the returned value of the call to setvars is 7 //verify that the returned value of the call to setvars is 7
result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "setVars(address,uint256)", inputData) _, wait, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "setVarsRevert(address,uint256)", inputData)
expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000007") require.Error(t, err)
require.NoError(t, err) require.Equal(t, exitcode.ExitCode(33), wait.Receipt.ExitCode)
require.Equal(t, result, expectedResult)
//test the value is 7 via calling the getter //test the value is 0 via calling the getter and was not set to 7
result = client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "getCounter()", []byte{}) expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err)
result, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "getCounter()", []byte{})
require.NoError(t, err)
require.Equal(t, result, expectedResult) require.Equal(t, result, expectedResult)
//test the value is 0 via calling the getter on the Actor contract //test the value is 0 via calling the getter on the Actor contract
result = client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "getCounter()", []byte{}) result, _, err = client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "getCounter()", []byte{})
expectedResultActor, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, result, expectedResultActor) require.Equal(t, result, expectedResult)
}
// TestFEVMSimpleRevert makes a call that is a simple revert
func TestFEVMSimpleRevert(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract Actor
filenameStorage := "contracts/DelegatecallStorage.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameStorage)
//call revert
_, wait, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "revert()", []byte{})
require.Equal(t, wait.Receipt.ExitCode, exitcode.ExitCode(33))
require.Error(t, err)
}
// TestFEVMSelfDestruct creates a contract that just has a self destruct feature and calls it
func TestFEVMSelfDestruct(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract Actor
filenameStorage := "contracts/SelfDestruct.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameStorage)
//call destroy
_, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "destroy()", []byte{})
require.NoError(t, err)
//call destroy a second time and also no error
_, _, err = client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "destroy()", []byte{})
require.NoError(t, err)
}
// TestFEVMTestApp deploys a fairly complex app contract and confirms it works as expected
func TestFEVMTestApp(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract Actor
filenameStorage := "contracts/TestApp.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameStorage)
inputData, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000066162636465660000000000000000000000000000000000000000000000000000") // sending string "abcdef" and int 7 - constructed using remix
require.NoError(t, err)
_, _, err = client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "new_Test(string,uint256)", inputData)
require.NoError(t, err)
inputData, err = hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err)
_, _, err = client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "get_Test_N(uint256)", inputData)
require.NoError(t, err)
}
// TestFEVMTestApp creates a contract that just has a self destruct feature and calls it
func TestFEVMTestConstructor(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract Actor
filenameStorage := "contracts/Constructor.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameStorage)
//input = uint256{7}. set value and confirm tx success
inputData, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000007")
require.NoError(t, err)
_, _, err = client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "new_Test(uint256)", inputData)
require.NoError(t, err)
}
// TestFEVMAutoSelfDestruct creates a contract that just has a self destruct feature and calls it
func TestFEVMAutoSelfDestruct(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract Actor
filenameStorage := "contracts/AutoSelfDestruct.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameStorage)
//call destroy
_, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "destroy()", []byte{})
require.NoError(t, err)
}
// TestFEVMTestApp creates a contract that just has a self destruct feature and calls it
func TestFEVMTestSendToContract(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
bal, err := client.WalletBalance(ctx, client.DefaultKey.Address)
require.NoError(t, err)
//install contract TestApp
filenameStorage := "contracts/SelfDestruct.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameStorage)
//transfer half balance to contract
sendAmount := big.Div(bal, big.NewInt(2))
client.EVM().TransferValueOrFail(ctx, fromAddr, contractAddr, sendAmount)
//call self destruct which should return balance
_, _, err = client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "destroy()", []byte{})
require.NoError(t, err)
finalBalanceMinimum := types.FromFil(uint64(99_999_999)) // 100 million FIL - 1 FIL for gas upper bounds
finalBal, err := client.WalletBalance(ctx, client.DefaultKey.Address)
require.NoError(t, err)
require.Equal(t, true, finalBal.GreaterThan(finalBalanceMinimum))
}
// creates a contract that would fail when tx are sent to it
// on eth but on fevm it succeeds
// example failing on testnet https://goerli.etherscan.io/address/0x2ff1525e060169dbf97b9461758c8f701f107cd2
func TestFEVMTestNotPayable(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
fromAddr := client.DefaultKey.Address
t.Log("from - ", fromAddr)
//create contract A
filenameStorage := "contracts/NotPayable.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameStorage)
sendAmount := big.NewInt(10_000_000)
client.EVM().TransferValueOrFail(ctx, fromAddr, contractAddr, sendAmount)
}
// tx to non function succeeds
func TestFEVMSendCall(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract
filenameActor := "contracts/GasSendTest.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor)
_, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "x()", []byte{})
require.NoError(t, err)
}
// creates a contract that would fail when tx are sent to it
// on eth but on fevm it succeeds
// example on goerli of tx failing https://goerli.etherscan.io/address/0xec037bdc9a79420985a53a49fdae3ccf8989909b
func TestFEVMSendGasLimit(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract
filenameActor := "contracts/GasLimitSend.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor)
//send $ to contract
//transfer 1 attoFIL to contract
sendAmount := big.MustFromString("1")
client.EVM().TransferValueOrFail(ctx, fromAddr, contractAddr, sendAmount)
_, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "getDataLength()", []byte{})
require.NoError(t, err)
}
// TestFEVMDelegateCall deploys the two contracts in TestFEVMDelegateCall but instead of A calling B, A calls A which should cause A to cause A in an infinite loop and should give a reasonable error
// XXX should not be fatal errors
func TestFEVMDelegateCallRecursiveFail(t *testing.T) {
//TODO change the gas limit of this invocation and confirm that the number of errors is different
//also TODO should we not have fatal error show up here?
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract Actor
filenameActor := "contracts/DelegatecallStorage.hex"
fromAddr, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor)
//any data will do for this test that fails
inputDataContract := inputDataFromFrom(ctx, t, client, actorAddr)
inputDataValue := inputDataFromArray([]byte{7})
inputData := append(inputDataContract, inputDataValue...)
//verify that the returned value of the call to setvars is 7
_, wait, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "setVarsSelf(address,uint256)", inputData)
require.Error(t, err)
require.Equal(t, exitcode.SysErrorIllegalArgument, wait.Receipt.ExitCode)
//assert no fatal errors but still there are errors::
errorAny := "fatal error"
require.NotContains(t, err.Error(), errorAny)
}
// XXX Currently fails as self destruct has a bug
// TestFEVMTestSendValueThroughContracts creates A and B contract and exchanges value
// and self destructs and accounts for value sent
func TestFEVMTestSendValueThroughContractsAndDestroy(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
fromAddr := client.DefaultKey.Address
t.Log("from - ", fromAddr)
//create contract A
filenameStorage := "contracts/ValueSender.hex"
fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameStorage)
//create contract B
ret, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "createB()", []byte{})
require.NoError(t, err)
ethAddr, err := ethtypes.CastEthAddress(ret[12:])
require.NoError(t, err)
contractBAddress, err := ethAddr.ToFilecoinAddress()
require.NoError(t, err)
t.Log("contractBAddress - ", contractBAddress)
//self destruct contract B
_, _, err = client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractBAddress, "selfDestruct()", []byte{})
require.NoError(t, err)
} }
func TestEVMRpcDisable(t *testing.T) { func TestEVMRpcDisable(t *testing.T) {
@ -139,3 +532,77 @@ func TestEVMRpcDisable(t *testing.T) {
_, err := client.EthBlockNumber(context.Background()) _, err := client.EthBlockNumber(context.Background())
require.ErrorContains(t, err, "module disabled, enable with Fevm.EnableEthRPC") require.ErrorContains(t, err, "module disabled, enable with Fevm.EnableEthRPC")
} }
// TestFEVMRecursiveFuncCall deploys a contract and makes a recursive function calls
func TestFEVMRecursiveFuncCall(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract Actor
filenameActor := "contracts/StackFunc.hex"
fromAddr, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor)
testN := func(n int, ex exitcode.ExitCode) func(t *testing.T) {
return func(t *testing.T) {
inputData := make([]byte, 32)
binary.BigEndian.PutUint64(inputData[24:], uint64(n))
client.EVM().InvokeContractByFuncNameExpectExit(ctx, fromAddr, actorAddr, "exec1(uint256)", inputData, ex)
}
}
t.Run("n=0", testN(0, exitcode.Ok))
t.Run("n=1", testN(1, exitcode.Ok))
t.Run("n=20", testN(20, exitcode.Ok))
t.Run("n=200", testN(200, exitcode.Ok))
t.Run("n=507", testN(507, exitcode.Ok))
t.Run("n=508", testN(508, exitcode.ExitCode(37))) // 37 means stack overflow
}
// TestFEVMRecursiveActorCall deploys a contract and makes a recursive actor calls
func TestFEVMRecursiveActorCall(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract Actor
filenameActor := "contracts/RecCall.hex"
fromAddr, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor)
testN := func(n, r int, ex exitcode.ExitCode) func(t *testing.T) {
return func(t *testing.T) {
inputData := make([]byte, 32*3)
binary.BigEndian.PutUint64(inputData[24:], uint64(n))
binary.BigEndian.PutUint64(inputData[32+24:], uint64(n))
binary.BigEndian.PutUint64(inputData[32+32+24:], uint64(r))
client.EVM().InvokeContractByFuncNameExpectExit(ctx, fromAddr, actorAddr, "exec1(uint256,uint256,uint256)", inputData, ex)
}
}
t.Run("n=0,r=1", testN(0, 1, exitcode.Ok))
t.Run("n=1,r=1", testN(1, 1, exitcode.Ok))
t.Run("n=20,r=1", testN(20, 1, exitcode.Ok))
t.Run("n=200,r=1", testN(200, 1, exitcode.Ok))
t.Run("n=251,r=1", testN(251, 1, exitcode.Ok))
t.Run("n=252,r=1-fails", testN(252, 1, exitcode.ExitCode(37))) // 37 means stack overflow
t.Run("n=0,r=10", testN(0, 10, exitcode.Ok))
t.Run("n=1,r=10", testN(1, 10, exitcode.Ok))
t.Run("n=20,r=10", testN(20, 10, exitcode.Ok))
t.Run("n=200,r=10", testN(200, 10, exitcode.Ok))
t.Run("n=251,r=10", testN(251, 10, exitcode.Ok))
t.Run("n=252,r=10-fails", testN(252, 10, exitcode.ExitCode(37)))
t.Run("n=0,r=32", testN(0, 32, exitcode.Ok))
t.Run("n=1,r=32", testN(1, 32, exitcode.Ok))
t.Run("n=20,r=32", testN(20, 32, exitcode.Ok))
t.Run("n=200,r=32", testN(200, 32, exitcode.Ok))
t.Run("n=251,r=32", testN(251, 32, exitcode.Ok))
t.Run("n=0,r=254", testN(0, 254, exitcode.Ok))
t.Run("n=251,r=166", testN(251, 166, exitcode.Ok))
t.Run("n=0,r=256-fails", testN(0, 256, exitcode.ExitCode(33))) // 33 means transaction reverted
t.Run("n=251,r=167-fails", testN(251, 167, exitcode.ExitCode(33)))
}

View File

@ -290,7 +290,7 @@ func startNodes(
ens.InterconnectAll().BeginMining(blocktime) ens.InterconnectAll().BeginMining(blocktime)
// Create a gateway server in front of the full node // Create a gateway server in front of the full node
gwapi := gateway.NewNode(full, lookbackCap, stateWaitLookbackLimit, 0, time.Minute) gwapi := gateway.NewNode(full, nil, lookbackCap, stateWaitLookbackLimit, 0, time.Minute)
handler, err := gateway.Handler(gwapi, full, 0, 0) handler, err := gateway.Handler(gwapi, full, 0, 0)
require.NoError(t, err) require.NoError(t, err)

View File

@ -47,6 +47,7 @@ import (
"github.com/filecoin-project/lotus/chain/wallet/key" "github.com/filecoin-project/lotus/chain/wallet/key"
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed" "github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
"github.com/filecoin-project/lotus/cmd/lotus-worker/sealworker" "github.com/filecoin-project/lotus/cmd/lotus-worker/sealworker"
"github.com/filecoin-project/lotus/gateway"
"github.com/filecoin-project/lotus/genesis" "github.com/filecoin-project/lotus/genesis"
"github.com/filecoin-project/lotus/markets/idxprov" "github.com/filecoin-project/lotus/markets/idxprov"
"github.com/filecoin-project/lotus/markets/idxprov/idxprov_test" "github.com/filecoin-project/lotus/markets/idxprov/idxprov_test"
@ -210,7 +211,7 @@ func (n *Ensemble) FullNode(full *TestFullNode, opts ...NodeOpt) *Ensemble {
n.genesis.accounts = append(n.genesis.accounts, genacc) n.genesis.accounts = append(n.genesis.accounts, genacc)
} }
*full = TestFullNode{t: n.t, options: options, DefaultKey: key} *full = TestFullNode{t: n.t, options: options, DefaultKey: key, EthSubRouter: gateway.NewEthSubHandler()}
n.inactive.fullnodes = append(n.inactive.fullnodes, full) n.inactive.fullnodes = append(n.inactive.fullnodes, full)
return n return n

View File

@ -7,8 +7,11 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"os" "os"
"testing"
"time"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2"
"github.com/multiformats/go-varint" "github.com/multiformats/go-varint"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
cbg "github.com/whyrusleeping/cbor-gen" cbg "github.com/whyrusleeping/cbor-gen"
@ -21,8 +24,10 @@ import (
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/go-state-types/crypto" "github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/chain/types/ethtypes"
@ -38,16 +43,9 @@ func (f *TestFullNode) EVM() *EVM {
} }
func (e *EVM) DeployContract(ctx context.Context, sender address.Address, bytecode []byte) eam.CreateReturn { func (e *EVM) DeployContract(ctx context.Context, sender address.Address, bytecode []byte) eam.CreateReturn {
var err error
require := require.New(e.t) require := require.New(e.t)
nonce, err := e.MpoolGetNonce(ctx, sender)
if err != nil {
nonce = 0 // assume a zero nonce on error (e.g. sender doesn't exist).
}
var salt [32]byte
binary.BigEndian.PutUint64(salt[:], nonce)
method := builtintypes.MethodsEAM.CreateExternal method := builtintypes.MethodsEAM.CreateExternal
initcode := abi.CborBytes(bytecode) initcode := abi.CborBytes(bytecode)
params, err := actors.SerializeParams(&initcode) params, err := actors.SerializeParams(&initcode)
@ -99,13 +97,13 @@ func (e *EVM) DeployContractFromFilename(ctx context.Context, binFilename string
return fromAddr, idAddr return fromAddr, idAddr
} }
func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target address.Address, selector []byte, inputData []byte) *api.MsgLookup { func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target address.Address, selector []byte, inputData []byte) (*api.MsgLookup, error) {
require := require.New(e.t)
params := append(selector, inputData...) params := append(selector, inputData...)
var buffer bytes.Buffer var buffer bytes.Buffer
err := cbg.WriteByteArray(&buffer, params) err := cbg.WriteByteArray(&buffer, params)
require.NoError(err) if err != nil {
return nil, err
}
params = buffer.Bytes() params = buffer.Bytes()
msg := &types.Message{ msg := &types.Message{
@ -113,18 +111,23 @@ func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target
From: sender, From: sender,
Value: big.Zero(), Value: big.Zero(),
Method: builtintypes.MethodsEVM.InvokeContract, Method: builtintypes.MethodsEVM.InvokeContract,
GasLimit: build.BlockGasLimit, // note: we hardcode block gas limit due to slightly broken gas estimation - https://github.com/filecoin-project/lotus/issues/10041
Params: params, Params: params,
} }
e.t.Log("sending invoke message") e.t.Log("sending invoke message")
smsg, err := e.MpoolPushMessage(ctx, msg, nil) smsg, err := e.MpoolPushMessage(ctx, msg, nil)
require.NoError(err) if err != nil {
return nil, err
}
e.t.Log("waiting for message to execute") e.t.Log("waiting for message to execute")
wait, err := e.StateWaitMsg(ctx, smsg.Cid(), 0, 0, false) wait, err := e.StateWaitMsg(ctx, smsg.Cid(), 0, 0, false)
require.NoError(err) if err != nil {
return nil, err
}
return wait return wait, nil
} }
// LoadEvents loads all events in an event AMT. // LoadEvents loads all events in an event AMT.
@ -234,13 +237,26 @@ func (e *EVM) ComputeContractAddress(deployer ethtypes.EthAddress, nonce uint64)
return *(*ethtypes.EthAddress)(hasher.Sum(nil)[12:]) return *(*ethtypes.EthAddress)(hasher.Sum(nil)[12:])
} }
func (e *EVM) InvokeContractByFuncName(ctx context.Context, fromAddr address.Address, idAddr address.Address, funcSignature string, inputData []byte) []byte { func (e *EVM) InvokeContractByFuncName(ctx context.Context, fromAddr address.Address, idAddr address.Address, funcSignature string, inputData []byte) ([]byte, *api.MsgLookup, error) {
entryPoint := CalcFuncSignature(funcSignature) entryPoint := CalcFuncSignature(funcSignature)
wait := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData) wait, err := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData)
require.True(e.t, wait.Receipt.ExitCode.IsSuccess(), "contract execution failed") if err != nil {
return nil, wait, err
}
if !wait.Receipt.ExitCode.IsSuccess() {
return nil, wait, fmt.Errorf("contract execution failed - %v", wait.Receipt.ExitCode)
}
result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return))) result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return)))
require.NoError(e.t, err) if err != nil {
return result return nil, wait, err
}
return result, wait, nil
}
func (e *EVM) InvokeContractByFuncNameExpectExit(ctx context.Context, fromAddr address.Address, idAddr address.Address, funcSignature string, inputData []byte, exit exitcode.ExitCode) {
entryPoint := CalcFuncSignature(funcSignature)
wait, _ := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData)
require.Equal(e.t, exit, wait.Receipt.ExitCode)
} }
// function signatures are the first 4 bytes of the hash of the function name and types // function signatures are the first 4 bytes of the hash of the function name and types
@ -301,3 +317,40 @@ func removeLeadingZeros(data []byte) []byte {
} }
return data[firstNonZeroIndex:] return data[firstNonZeroIndex:]
} }
func SetupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *TestFullNode) {
//make all logs extra quiet for fevm tests
lvl, err := logging.LevelFromString("error")
if err != nil {
panic(err)
}
logging.SetAllLoggers(lvl)
blockTime := 100 * time.Millisecond
client, _, ens := EnsembleMinimal(t, MockProofs(), ThroughRPC())
ens.InterconnectAll().BeginMining(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
// require that the initial balance is 100 million FIL in setup
// this way other tests can count on this initial wallet balance
fromAddr := client.DefaultKey.Address
bal, err := client.WalletBalance(ctx, fromAddr)
require.NoError(t, err)
originalBalance := types.FromFil(uint64(100_000_000)) // 100 million FIL
require.Equal(t, originalBalance, bal)
return ctx, cancel, client
}
func (e *EVM) TransferValueOrFail(ctx context.Context, fromAddr address.Address, toAddr address.Address, sendAmount big.Int) {
sendMsg := &types.Message{
From: fromAddr,
To: toAddr,
Value: sendAmount,
}
signedMsg, err := e.MpoolPushMessage(ctx, sendMsg, nil)
require.NoError(e.t, err)
mLookup, err := e.StateWaitMsg(ctx, signedMsg.Cid(), 3, api.LookbackNoLimit, true)
require.NoError(e.t, err)
require.Equal(e.t, exitcode.Ok, mLookup.Receipt.ExitCode)
}

View File

@ -1,6 +1,7 @@
package kit package kit
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"testing" "testing"
@ -10,15 +11,18 @@ import (
"github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peer"
"github.com/multiformats/go-multiaddr" "github.com/multiformats/go-multiaddr"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
cbg "github.com/whyrusleeping/cbor-gen"
"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/abi"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/api/v1api" "github.com/filecoin-project/lotus/api/v1api"
"github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet/key" "github.com/filecoin-project/lotus/chain/wallet/key"
cliutil "github.com/filecoin-project/lotus/cli/util" cliutil "github.com/filecoin-project/lotus/cli/util"
"github.com/filecoin-project/lotus/gateway"
"github.com/filecoin-project/lotus/node" "github.com/filecoin-project/lotus/node"
) )
@ -43,6 +47,10 @@ type TestFullNode struct {
Stop node.StopFunc Stop node.StopFunc
// gateway handler makes it convenient to register callbalks per topic, so we
// also use it for tests
EthSubRouter *gateway.EthSubHandler
options nodeOpts options nodeOpts
} }
@ -124,6 +132,50 @@ func (f *TestFullNode) AssignPrivKey(pkey *Libp2p) {
f.Pkey = pkey f.Pkey = pkey
} }
type SendCall struct {
Method abi.MethodNum
Params []byte
}
func (f *TestFullNode) MakeSendCall(m abi.MethodNum, params cbg.CBORMarshaler) SendCall {
var b bytes.Buffer
err := params.MarshalCBOR(&b)
require.NoError(f.t, err)
return SendCall{
Method: m,
Params: b.Bytes(),
}
}
func (f *TestFullNode) ExpectSend(ctx context.Context, from, to address.Address, value types.BigInt, errContains string, sc ...SendCall) *types.SignedMessage {
msg := &types.Message{From: from, To: to, Value: value}
if len(sc) == 1 {
msg.Method = sc[0].Method
msg.Params = sc[0].Params
}
_, err := f.GasEstimateMessageGas(ctx, msg, nil, types.EmptyTSK)
if errContains != "" {
require.ErrorContains(f.t, err, errContains)
return nil
}
require.NoError(f.t, err)
if errContains == "" {
m, err := f.MpoolPushMessage(ctx, msg, nil)
require.NoError(f.t, err)
r, err := f.StateWaitMsg(ctx, m.Cid(), 1, api.LookbackNoLimit, true)
require.NoError(f.t, err)
require.Equal(f.t, exitcode.Ok, r.Receipt.ExitCode)
return m
}
return nil
}
// ChainPredicate encapsulates a chain condition. // ChainPredicate encapsulates a chain condition.
type ChainPredicate func(set *types.TipSet) bool type ChainPredicate func(set *types.TipSet) bool

View File

@ -13,6 +13,8 @@ import (
manet "github.com/multiformats/go-multiaddr/net" manet "github.com/multiformats/go-multiaddr/net"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api/client" "github.com/filecoin-project/lotus/api/client"
"github.com/filecoin-project/lotus/cmd/lotus-worker/sealworker" "github.com/filecoin-project/lotus/cmd/lotus-worker/sealworker"
"github.com/filecoin-project/lotus/node" "github.com/filecoin-project/lotus/node"
@ -52,7 +54,12 @@ func fullRpc(t *testing.T, f *TestFullNode) (*TestFullNode, Closer) {
fmt.Printf("FULLNODE RPC ENV FOR CLI DEBUGGING `export FULLNODE_API_INFO=%s`\n", "ws://"+srv.Listener.Addr().String()) fmt.Printf("FULLNODE RPC ENV FOR CLI DEBUGGING `export FULLNODE_API_INFO=%s`\n", "ws://"+srv.Listener.Addr().String())
sendItestdNotif("FULLNODE_API_INFO", t.Name(), "ws://"+srv.Listener.Addr().String()) sendItestdNotif("FULLNODE_API_INFO", t.Name(), "ws://"+srv.Listener.Addr().String())
cl, stop, err := client.NewFullNodeRPCV1(context.Background(), "ws://"+srv.Listener.Addr().String()+"/rpc/v1", nil) rpcOpts := []jsonrpc.Option{
jsonrpc.WithClientHandler("Filecoin", f.EthSubRouter),
jsonrpc.WithClientHandlerAlias("eth_subscription", "Filecoin.EthSubscription"),
}
cl, stop, err := client.NewFullNodeRPCV1(context.Background(), "ws://"+srv.Listener.Addr().String()+"/rpc/v1", nil, rpcOpts...)
require.NoError(t, err) require.NoError(t, err)
f.ListenAddr, f.ListenURL, f.FullNode = maddr, srv.URL, cl f.ListenAddr, f.ListenURL, f.FullNode = maddr, srv.URL, cl

View File

@ -30,6 +30,12 @@ func Wrap[T any](value T, err error) Result[T] {
} }
} }
func (r *Result[T]) Unwrap() (T, error) { func (r Result[T]) Unwrap() (T, error) {
return r.Value, r.Error return r.Value, r.Error
} }
func (r Result[T]) Assert(noErrFn func(err error, msgAndArgs ...interface{})) T {
noErrFn(r.Error)
return r.Value
}

View File

@ -127,6 +127,8 @@ const (
SetApiEndpointKey SetApiEndpointKey
StoreEventsKey
_nInvokes // keep this last _nInvokes // keep this last
) )

View File

@ -219,6 +219,11 @@ func ConfigFullNode(c interface{}) Option {
Override(SetupFallbackBlockstoresKey, modules.InitFallbackBlockstores), Override(SetupFallbackBlockstoresKey, modules.InitFallbackBlockstores),
), ),
// If the Eth JSON-RPC is enabled, enable storing events at the ChainStore.
// This is the case even if real-time and historic filtering are disabled,
// as it enables us to serve logs in eth_getTransactionReceipt.
If(cfg.Fevm.EnableEthRPC, Override(StoreEventsKey, modules.EnableStoringEvents)),
Override(new(dtypes.ClientImportMgr), modules.ClientImportMgr), Override(new(dtypes.ClientImportMgr), modules.ClientImportMgr),
Override(new(dtypes.ClientBlockstore), modules.ClientBlockstore), Override(new(dtypes.ClientBlockstore), modules.ClientBlockstore),
@ -258,11 +263,18 @@ func ConfigFullNode(c interface{}) Option {
// Actor event filtering support // Actor event filtering support
Override(new(events.EventAPI), From(new(modules.EventAPI))), Override(new(events.EventAPI), From(new(modules.EventAPI))),
// in lite-mode Eth event api is provided by gateway
ApplyIf(isFullNode, Override(new(full.EthEventAPI), modules.EthEventAPI(cfg.Fevm))),
If(cfg.Fevm.EnableEthRPC, Override(new(full.EthModuleAPI), modules.EthModuleAPI(cfg.Fevm))), // in lite-mode Eth api is provided by gateway
If(!cfg.Fevm.EnableEthRPC, Override(new(full.EthModuleAPI), &full.EthModuleDummy{})), ApplyIf(isFullNode,
If(cfg.Fevm.EnableEthRPC,
Override(new(full.EthModuleAPI), modules.EthModuleAPI(cfg.Fevm)),
Override(new(full.EthEventAPI), modules.EthEventAPI(cfg.Fevm)),
),
If(!cfg.Fevm.EnableEthRPC,
Override(new(full.EthModuleAPI), &full.EthModuleDummy{}),
Override(new(full.EthEventAPI), &full.EthModuleDummy{}),
),
),
) )
} }

View File

@ -6,6 +6,8 @@ import (
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/chain/types/ethtypes"
) )
@ -118,4 +120,45 @@ func (e *EthModuleDummy) EthSendRawTransaction(ctx context.Context, rawTx ethtyp
return ethtypes.EthHash{}, ErrModuleDisabled return ethtypes.EthHash{}, ErrModuleDisabled
} }
func (e *EthModuleDummy) Web3ClientVersion(ctx context.Context) (string, error) {
return "", ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetLogs(ctx context.Context, filter *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) {
return &ethtypes.EthFilterResult{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) {
return &ethtypes.EthFilterResult{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthGetFilterLogs(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) {
return &ethtypes.EthFilterResult{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthNewFilter(ctx context.Context, filter *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) {
return ethtypes.EthFilterID{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) {
return ethtypes.EthFilterID{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) {
return ethtypes.EthFilterID{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) {
return false, ErrModuleDisabled
}
func (e *EthModuleDummy) EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
return ethtypes.EthSubscriptionID{}, ErrModuleDisabled
}
func (e *EthModuleDummy) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) {
return false, ErrModuleDisabled
}
var _ EthModuleAPI = &EthModuleDummy{} var _ EthModuleAPI = &EthModuleDummy{}
var _ EthEventAPI = &EthModuleDummy{}

View File

@ -3,6 +3,7 @@ package full
import ( import (
"bytes" "bytes"
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"strconv" "strconv"
@ -16,6 +17,7 @@ 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-jsonrpc"
"github.com/filecoin-project/go-state-types/abi" "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"
@ -64,6 +66,7 @@ type EthModuleAPI interface {
EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error)
EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error)
EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.EthBytes) (ethtypes.EthHash, error)
Web3ClientVersion(ctx context.Context) (string, error)
} }
type EthEventAPI interface { type EthEventAPI interface {
@ -74,13 +77,15 @@ type EthEventAPI interface {
EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error)
EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error)
EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error)
EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error)
EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error)
} }
var ( var (
_ EthModuleAPI = *new(api.FullNode) _ EthModuleAPI = *new(api.FullNode)
_ EthEventAPI = *new(api.FullNode) _ EthEventAPI = *new(api.FullNode)
_ EthModuleAPI = *new(api.Gateway)
) )
// EthModule provides the default implementation of the standard Ethereum JSON-RPC API. // EthModule provides the default implementation of the standard Ethereum JSON-RPC API.
@ -131,6 +136,7 @@ type EthEvent struct {
FilterStore filter.FilterStore FilterStore filter.FilterStore
SubManager *EthSubscriptionManager SubManager *EthSubscriptionManager
MaxFilterHeightRange abi.ChainEpoch MaxFilterHeightRange abi.ChainEpoch
SubscribtionCtx context.Context
} }
var _ EthEventAPI = (*EthEvent)(nil) var _ EthEventAPI = (*EthEvent)(nil)
@ -211,7 +217,7 @@ func (a *EthModule) EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthH
if err != nil { if err != nil {
return ethtypes.EthBlock{}, xerrors.Errorf("error loading tipset %s: %w", ts, err) return ethtypes.EthBlock{}, xerrors.Errorf("error loading tipset %s: %w", ts, err)
} }
return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.Chain, a.ChainAPI, a.StateAPI) return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.Chain, a.StateAPI)
} }
func (a *EthModule) parseBlkParam(ctx context.Context, blkParam string) (tipset *types.TipSet, err error) { func (a *EthModule) parseBlkParam(ctx context.Context, blkParam string) (tipset *types.TipSet, err error) {
@ -248,7 +254,7 @@ func (a *EthModule) EthGetBlockByNumber(ctx context.Context, blkParam string, fu
if err != nil { if err != nil {
return ethtypes.EthBlock{}, err return ethtypes.EthBlock{}, err
} }
return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.Chain, a.ChainAPI, a.StateAPI) return newEthBlockFromFilecoinTipSet(ctx, ts, fullTxInfo, a.Chain, a.StateAPI)
} }
func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) { func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) {
@ -269,8 +275,8 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype
// first, try to get the cid from mined transactions // first, try to get the cid from mined transactions
msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, c, api.LookbackNoLimit, true) msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, c, api.LookbackNoLimit, true)
if err == nil { if err == nil && msgLookup != nil {
tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI) tx, err := newEthTxFromMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI)
if err == nil { if err == nil {
return &tx, nil return &tx, nil
} }
@ -286,7 +292,7 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype
for _, p := range pending { for _, p := range pending {
if p.Cid() == c { if p.Cid() == c {
tx, err := NewEthTxFromFilecoinMessage(ctx, p, a.StateAPI) tx, err := newEthTxFromSignedMessage(ctx, p, a.StateAPI)
if err != nil { if err != nil {
return nil, fmt.Errorf("could not convert Filecoin message into tx: %s", err) return nil, fmt.Errorf("could not convert Filecoin message into tx: %s", err)
} }
@ -335,7 +341,7 @@ func (a *EthModule) EthGetMessageCidByTransactionHash(ctx context.Context, txHas
} }
func (a *EthModule) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) { func (a *EthModule) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) {
hash, err := EthTxHashFromFilecoinMessageCid(ctx, cid, a.StateAPI) hash, err := EthTxHashFromMessageCid(ctx, cid, a.StateAPI)
if hash == ethtypes.EmptyEthHash { if hash == ethtypes.EmptyEthHash {
// not found // not found
return nil, nil return nil, nil
@ -378,7 +384,7 @@ func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash ethtype
return nil, nil return nil, nil
} }
tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI) tx, err := newEthTxFromMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI)
if err != nil { if err != nil {
return nil, nil return nil, nil
} }
@ -500,18 +506,8 @@ func (a *EthModule) EthGetStorageAt(ctx context.Context, ethAddr ethtypes.EthAdd
return nil, fmt.Errorf("failed to construct system sender address: %w", err) return nil, fmt.Errorf("failed to construct system sender address: %w", err)
} }
// TODO super duper hack (raulk). The EVM runtime actor uses the U256 parameter type in
// GetStorageAtParams, which serializes as a hex-encoded string. It should serialize
// as bytes. We didn't get to fix in time for Iron, so for now we just pass
// through the hex-encoded value passed through the Eth JSON-RPC API, by remarshalling it.
// We don't fix this at origin (builtin-actors) because we are not updating the bundle
// for Iron.
tmp, err := position.MarshalJSON()
if err != nil {
panic(err)
}
params, err := actors.SerializeParams(&evm.GetStorageAtParams{ params, err := actors.SerializeParams(&evm.GetStorageAtParams{
StorageKey: tmp[1 : len(tmp)-1], // TODO strip the JSON-encoding quotes -- yuck StorageKey: position,
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to serialize parameters: %w", err) return nil, fmt.Errorf("failed to serialize parameters: %w", err)
@ -614,7 +610,7 @@ func (a *EthModule) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint
for ts.Height() >= abi.ChainEpoch(oldestBlkHeight) { for ts.Height() >= abi.ChainEpoch(oldestBlkHeight) {
// Unfortunately we need to rebuild the full message view so we can // Unfortunately we need to rebuild the full message view so we can
// totalize gas used in the tipset. // totalize gas used in the tipset.
block, err := newEthBlockFromFilecoinTipSet(ctx, ts, false, a.Chain, a.ChainAPI, a.StateAPI) block, err := newEthBlockFromFilecoinTipSet(ctx, ts, false, a.Chain, a.StateAPI)
if err != nil { if err != nil {
return ethtypes.EthFeeHistory{}, fmt.Errorf("cannot create eth block: %v", err) return ethtypes.EthFeeHistory{}, fmt.Errorf("cannot create eth block: %v", err)
} }
@ -699,13 +695,6 @@ func (a *EthModule) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.Et
return ethtypes.EmptyEthHash, err return ethtypes.EmptyEthHash, err
} }
_, err = a.StateAPI.StateGetActor(ctx, smsg.Message.To, types.EmptyTSK)
if err != nil {
// if actor does not exist on chain yet, set the method to 0 because
// placeholders only implement method 0
smsg.Message.Method = builtinactors.MethodSend
}
_, err = a.MpoolAPI.MpoolPush(ctx, smsg) _, err = a.MpoolAPI.MpoolPush(ctx, smsg)
if err != nil { if err != nil {
return ethtypes.EmptyEthHash, err return ethtypes.EmptyEthHash, err
@ -714,6 +703,10 @@ func (a *EthModule) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.Et
return ethtypes.EthHashFromTxBytes(rawTx), nil return ethtypes.EthHashFromTxBytes(rawTx), nil
} }
func (a *EthModule) Web3ClientVersion(ctx context.Context) (string, error) {
return build.UserVersion(), nil
}
func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.EthCall) (*types.Message, error) { func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.EthCall) (*types.Message, error) {
var from address.Address var from address.Address
if tx.From == nil || *tx.From == (ethtypes.EthAddress{}) { if tx.From == nil || *tx.From == (ethtypes.EthAddress{}) {
@ -827,8 +820,9 @@ func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (et
func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) { func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) {
msg, err := a.ethCallToFilecoinMessage(ctx, tx) msg, err := a.ethCallToFilecoinMessage(ctx, tx)
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to convert ethcall to filecoin message: %w", err)
} }
ts, err := a.parseBlkParam(ctx, blkParam) ts, err := a.parseBlkParam(ctx, blkParam)
if err != nil { if err != nil {
return nil, xerrors.Errorf("cannot parse block param: %s", blkParam) return nil, xerrors.Errorf("cannot parse block param: %s", blkParam)
@ -836,11 +830,17 @@ func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam s
invokeResult, err := a.applyMessage(ctx, msg, ts.Key()) invokeResult, err := a.applyMessage(ctx, msg, ts.Key())
if err != nil { if err != nil {
return nil, err return nil, xerrors.Errorf("failed to apply message: %w", err)
} }
if len(invokeResult.MsgRct.Return) > 0 {
if msg.To == builtintypes.EthereumAddressManagerActorAddr {
// As far as I can tell, the Eth API always returns empty on contract deployment
return ethtypes.EthBytes{}, nil
} else if len(invokeResult.MsgRct.Return) > 0 {
return cbg.ReadByteArray(bytes.NewReader(invokeResult.MsgRct.Return), uint64(len(invokeResult.MsgRct.Return))) return cbg.ReadByteArray(bytes.NewReader(invokeResult.MsgRct.Return), uint64(len(invokeResult.MsgRct.Return)))
} }
return ethtypes.EthBytes{}, nil return ethtypes.EthBytes{}, nil
} }
@ -980,17 +980,9 @@ func (e *EthEvent) installEthFilterSpec(ctx context.Context, filterSpec *ethtype
addresses = append(addresses, a) addresses = append(addresses, a)
} }
for idx, vals := range filterSpec.Topics { keys, err := parseEthTopics(filterSpec.Topics)
if len(vals) == 0 { if err != nil {
continue return nil, err
}
// Ethereum topics are emitted using `LOG{0..4}` opcodes resulting in topics1..4
key := fmt.Sprintf("topic%d", idx+1)
for _, v := range vals {
buf := make([]byte, len(v[:]))
copy(buf, v[:])
keys[key] = append(keys[key], buf)
}
} }
return e.EventFilterManager.Install(ctx, minHeight, maxHeight, tipsetCid, addresses, keys) return e.EventFilterManager.Install(ctx, minHeight, maxHeight, tipsetCid, addresses, keys)
@ -1015,7 +1007,6 @@ func (e *EthEvent) EthNewFilter(ctx context.Context, filterSpec *ethtypes.EthFil
return ethtypes.EthFilterID{}, err return ethtypes.EthFilterID{}, err
} }
return ethtypes.EthFilterID(f.ID()), nil return ethtypes.EthFilterID(f.ID()), nil
} }
@ -1114,54 +1105,71 @@ const (
EthSubscribeEventTypeLogs = "logs" EthSubscribeEventTypeLogs = "logs"
) )
func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) { func (e *EthEvent) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
if e.SubManager == nil { params, err := jsonrpc.DecodeParams[ethtypes.EthSubscribeParams](p)
return nil, api.ErrNotSupported
}
// Note that go-jsonrpc will set the method field of the response to "xrpc.ch.val" but the ethereum api expects the name of the
// method to be "eth_subscription". This probably doesn't matter in practice.
sub, err := e.SubManager.StartSubscription(ctx)
if err != nil { if err != nil {
return nil, err return ethtypes.EthSubscriptionID{}, xerrors.Errorf("decoding params: %w", err)
} }
switch eventType { if e.SubManager == nil {
return ethtypes.EthSubscriptionID{}, api.ErrNotSupported
}
ethCb, ok := jsonrpc.ExtractReverseClient[api.EthSubscriberMethods](ctx)
if !ok {
return ethtypes.EthSubscriptionID{}, xerrors.Errorf("connection doesn't support callbacks")
}
sub, err := e.SubManager.StartSubscription(e.SubscribtionCtx, ethCb.EthSubscription)
if err != nil {
return ethtypes.EthSubscriptionID{}, err
}
switch params.EventType {
case EthSubscribeEventTypeHeads: case EthSubscribeEventTypeHeads:
f, err := e.TipSetFilterManager.Install(ctx) f, err := e.TipSetFilterManager.Install(ctx)
if err != nil { if err != nil {
// clean up any previous filters added and stop the sub // clean up any previous filters added and stop the sub
_, _ = e.EthUnsubscribe(ctx, sub.id) _, _ = e.EthUnsubscribe(ctx, sub.id)
return nil, err return ethtypes.EthSubscriptionID{}, err
} }
sub.addFilter(ctx, f) sub.addFilter(ctx, f)
case EthSubscribeEventTypeLogs: case EthSubscribeEventTypeLogs:
keys := map[string][][]byte{} keys := map[string][][]byte{}
if params != nil { if params.Params != nil {
for idx, vals := range params.Topics { var err error
// Ethereum topics are emitted using `LOG{0..4}` opcodes resulting in topics1..4 keys, err = parseEthTopics(params.Params.Topics)
key := fmt.Sprintf("topic%d", idx+1)
keyvals := make([][]byte, len(vals))
for i, v := range vals {
keyvals[i] = v[:]
}
keys[key] = keyvals
}
}
f, err := e.EventFilterManager.Install(ctx, -1, -1, cid.Undef, []address.Address{}, keys)
if err != nil { if err != nil {
// clean up any previous filters added and stop the sub // clean up any previous filters added and stop the sub
_, _ = e.EthUnsubscribe(ctx, sub.id) _, _ = e.EthUnsubscribe(ctx, sub.id)
return nil, err return ethtypes.EthSubscriptionID{}, err
}
}
var addresses []address.Address
if params.Params != nil {
for _, ea := range params.Params.Address {
a, err := ea.ToFilecoinAddress()
if err != nil {
return ethtypes.EthSubscriptionID{}, xerrors.Errorf("invalid address %x", ea)
}
addresses = append(addresses, a)
}
}
f, err := e.EventFilterManager.Install(ctx, -1, -1, cid.Undef, addresses, keys)
if err != nil {
// clean up any previous filters added and stop the sub
_, _ = e.EthUnsubscribe(ctx, sub.id)
return ethtypes.EthSubscriptionID{}, err
} }
sub.addFilter(ctx, f) sub.addFilter(ctx, f)
default: default:
return nil, xerrors.Errorf("unsupported event type: %s", eventType) return ethtypes.EthSubscriptionID{}, xerrors.Errorf("unsupported event type: %s", params.EventType)
} }
return sub.out, nil return sub.id, nil
} }
func (e *EthEvent) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) { func (e *EthEvent) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) {
@ -1233,7 +1241,10 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*etht
var err error var err error
for _, entry := range ev.Entries { for _, entry := range ev.Entries {
value := ethtypes.EthBytes(leftpad32(entry.Value)) // value has already been cbor-decoded but see https://github.com/filecoin-project/ref-fvm/issues/1345 value, err := cborDecodeTopicValue(entry.Value)
if err != nil {
return nil, err
}
if entry.Key == ethtypes.EthTopic1 || entry.Key == ethtypes.EthTopic2 || entry.Key == ethtypes.EthTopic3 || entry.Key == ethtypes.EthTopic4 { if entry.Key == ethtypes.EthTopic1 || entry.Key == ethtypes.EthTopic2 || entry.Key == ethtypes.EthTopic3 || entry.Key == ethtypes.EthTopic4 {
log.Topics = append(log.Topics, value) log.Topics = append(log.Topics, value)
} else { } else {
@ -1246,7 +1257,7 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*etht
return nil, err return nil, err
} }
log.TransactionHash, err = EthTxHashFromFilecoinMessageCid(context.TODO(), ev.MsgCid, sa) log.TransactionHash, err = EthTxHashFromMessageCid(context.TODO(), ev.MsgCid, sa)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1288,7 +1299,7 @@ func ethFilterResultFromMessages(cs []*types.SignedMessage, sa StateAPI) (*ethty
res := &ethtypes.EthFilterResult{} res := &ethtypes.EthFilterResult{}
for _, c := range cs { for _, c := range cs {
hash, err := EthTxHashFromSignedFilecoinMessage(context.TODO(), c, sa) hash, err := EthTxHashFromSignedMessage(context.TODO(), c, sa)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1307,7 +1318,7 @@ type EthSubscriptionManager struct {
subs map[ethtypes.EthSubscriptionID]*ethSubscription subs map[ethtypes.EthSubscriptionID]*ethSubscription
} }
func (e *EthSubscriptionManager) StartSubscription(ctx context.Context) (*ethSubscription, error) { // nolint func (e *EthSubscriptionManager) StartSubscription(ctx context.Context, out ethSubscriptionCallback) (*ethSubscription, error) { // nolint
rawid, err := uuid.NewRandom() rawid, err := uuid.NewRandom()
if err != nil { if err != nil {
return nil, xerrors.Errorf("new uuid: %w", err) return nil, xerrors.Errorf("new uuid: %w", err)
@ -1323,7 +1334,7 @@ func (e *EthSubscriptionManager) StartSubscription(ctx context.Context) (*ethSub
ChainAPI: e.ChainAPI, ChainAPI: e.ChainAPI,
id: id, id: id,
in: make(chan interface{}, 200), in: make(chan interface{}, 200),
out: make(chan ethtypes.EthSubscriptionResponse, 20), out: out,
quit: quit, quit: quit,
} }
@ -1353,13 +1364,15 @@ func (e *EthSubscriptionManager) StopSubscription(ctx context.Context, id ethtyp
return sub.filters, nil return sub.filters, nil
} }
type ethSubscriptionCallback func(context.Context, jsonrpc.RawParams) error
type ethSubscription struct { type ethSubscription struct {
Chain *store.ChainStore Chain *store.ChainStore
StateAPI StateAPI StateAPI StateAPI
ChainAPI ChainAPI ChainAPI ChainAPI
id ethtypes.EthSubscriptionID id ethtypes.EthSubscriptionID
in chan interface{} in chan interface{}
out chan ethtypes.EthSubscriptionResponse out ethSubscriptionCallback
mu sync.Mutex mu sync.Mutex
filters []filter.Filter filters []filter.Filter
@ -1389,7 +1402,7 @@ func (e *ethSubscription) start(ctx context.Context) {
case *filter.CollectedEvent: case *filter.CollectedEvent:
resp.Result, err = ethFilterResultFromEvents([]*filter.CollectedEvent{vt}, e.StateAPI) resp.Result, err = ethFilterResultFromEvents([]*filter.CollectedEvent{vt}, e.StateAPI)
case *types.TipSet: case *types.TipSet:
eb, err := newEthBlockFromFilecoinTipSet(ctx, vt, true, e.Chain, e.ChainAPI, e.StateAPI) eb, err := newEthBlockFromFilecoinTipSet(ctx, vt, true, e.Chain, e.StateAPI)
if err != nil { if err != nil {
break break
} }
@ -1403,10 +1416,15 @@ func (e *ethSubscription) start(ctx context.Context) {
continue continue
} }
select { outParam, err := json.Marshal(resp)
case e.out <- resp: if err != nil {
default: log.Warnw("marshaling subscription response", "sub", e.id, "error", err)
// Skip if client is not reading responses continue
}
if err := e.out(ctx, outParam); err != nil {
log.Warnw("sending subscription response", "sub", e.id, "error", err)
continue
} }
} }
} }
@ -1418,12 +1436,11 @@ func (e *ethSubscription) stop() {
if e.quit != nil { if e.quit != nil {
e.quit() e.quit()
close(e.out)
e.quit = nil e.quit = nil
} }
} }
func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTxInfo bool, cs *store.ChainStore, ca ChainAPI, sa StateAPI) (ethtypes.EthBlock, error) { func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTxInfo bool, cs *store.ChainStore, sa StateAPI) (ethtypes.EthBlock, error) {
parent, err := cs.LoadTipSet(ctx, ts.Parents()) parent, err := cs.LoadTipSet(ctx, ts.Parents())
if err != nil { if err != nil {
return ethtypes.EthBlock{}, err return ethtypes.EthBlock{}, err
@ -1462,7 +1479,7 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx
} }
gasUsed += msgLookup.Receipt.GasUsed gasUsed += msgLookup.Receipt.GasUsed
tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, txIdx, cs, sa) tx, err := newEthTxFromMessageLookup(ctx, msgLookup, txIdx, cs, sa)
if err != nil { if err != nil {
return ethtypes.EthBlock{}, nil return ethtypes.EthBlock{}, nil
} }
@ -1522,11 +1539,11 @@ func lookupEthAddress(ctx context.Context, addr address.Address, sa StateAPI) (e
return ethtypes.EthAddressFromFilecoinAddress(idAddr) return ethtypes.EthAddressFromFilecoinAddress(idAddr)
} }
func EthTxHashFromFilecoinMessageCid(ctx context.Context, c cid.Cid, sa StateAPI) (ethtypes.EthHash, error) { func EthTxHashFromMessageCid(ctx context.Context, c cid.Cid, sa StateAPI) (ethtypes.EthHash, error) {
smsg, err := sa.Chain.GetSignedMessage(ctx, c) smsg, err := sa.Chain.GetSignedMessage(ctx, c)
if err == nil { if err == nil {
// This is an Eth Tx, Secp message, Or BLS message in the mpool // This is an Eth Tx, Secp message, Or BLS message in the mpool
return EthTxHashFromSignedFilecoinMessage(ctx, smsg, sa) return EthTxHashFromSignedMessage(ctx, smsg, sa)
} }
_, err = sa.Chain.GetMessage(ctx, c) _, err = sa.Chain.GetMessage(ctx, c)
@ -1538,93 +1555,51 @@ func EthTxHashFromFilecoinMessageCid(ctx context.Context, c cid.Cid, sa StateAPI
return ethtypes.EmptyEthHash, nil return ethtypes.EmptyEthHash, nil
} }
func EthTxHashFromSignedFilecoinMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthHash, error) { func EthTxHashFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthHash, error) {
if smsg.Signature.Type == crypto.SigTypeDelegated { if smsg.Signature.Type == crypto.SigTypeDelegated {
ethTx, err := NewEthTxFromFilecoinMessage(ctx, smsg, sa) ethTx, err := newEthTxFromSignedMessage(ctx, smsg, sa)
if err != nil { if err != nil {
return ethtypes.EmptyEthHash, err return ethtypes.EmptyEthHash, err
} }
return ethTx.Hash, nil return ethTx.Hash, nil
} } else if smsg.Signature.Type == crypto.SigTypeSecp256k1 {
return ethtypes.EthHashFromCid(smsg.Cid()) return ethtypes.EthHashFromCid(smsg.Cid())
} else { // BLS message
return ethtypes.EthHashFromCid(smsg.Message.Cid())
}
} }
func NewEthTxFromFilecoinMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthTx, error) { func newEthTxFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthTx, error) {
// Ignore errors here so we can still parse non-eth messages var tx ethtypes.EthTx
fromEthAddr, _ := lookupEthAddress(ctx, smsg.Message.From, sa)
toEthAddr, _ := lookupEthAddress(ctx, smsg.Message.To, sa)
toAddr := &toEthAddr
input := smsg.Message.Params
var err error var err error
// Check to see if we need to decode as contract deployment.
// We don't need to resolve the to address, because there's only one form (an ID).
if smsg.Message.To == builtintypes.EthereumAddressManagerActorAddr {
switch smsg.Message.Method {
case builtintypes.MethodsEAM.Create:
toAddr = nil
var params eam.CreateParams
err = params.UnmarshalCBOR(bytes.NewReader(smsg.Message.Params))
input = params.Initcode
case builtintypes.MethodsEAM.Create2:
toAddr = nil
var params eam.Create2Params
err = params.UnmarshalCBOR(bytes.NewReader(smsg.Message.Params))
input = params.Initcode
case builtintypes.MethodsEAM.CreateExternal:
toAddr = nil
var params abi.CborBytes
err = params.UnmarshalCBOR(bytes.NewReader(smsg.Message.Params))
input = []byte(params)
}
if err != nil {
return ethtypes.EthTx{}, err
}
}
// Otherwise, try to decode as a cbor byte array.
// TODO: Actually check if this is an ethereum call. This code will work for demo purposes, but is not correct.
if toAddr != nil {
if decodedParams, err := cbg.ReadByteArray(bytes.NewReader(smsg.Message.Params), uint64(len(smsg.Message.Params))); err == nil {
input = decodedParams
}
}
r, s, v, err := ethtypes.RecoverSignature(smsg.Signature)
if err != nil {
// we don't want to return error if the message is not an Eth tx
r, s, v = ethtypes.EthBigIntZero, ethtypes.EthBigIntZero, ethtypes.EthBigIntZero
}
tx := ethtypes.EthTx{
Nonce: ethtypes.EthUint64(smsg.Message.Nonce),
ChainID: ethtypes.EthUint64(build.Eip155ChainId),
From: fromEthAddr,
To: toAddr,
Value: ethtypes.EthBigInt(smsg.Message.Value),
Type: ethtypes.EthUint64(2),
Input: input,
Gas: ethtypes.EthUint64(smsg.Message.GasLimit),
MaxFeePerGas: ethtypes.EthBigInt(smsg.Message.GasFeeCap),
MaxPriorityFeePerGas: ethtypes.EthBigInt(smsg.Message.GasPremium),
V: v,
R: r,
S: s,
}
// This is an eth tx // This is an eth tx
if smsg.Signature.Type == crypto.SigTypeDelegated { if smsg.Signature.Type == crypto.SigTypeDelegated {
tx, err = ethtypes.EthTxFromSignedEthMessage(smsg)
if err != nil {
return ethtypes.EthTx{}, xerrors.Errorf("failed to convert from signed message: %w", err)
}
tx.Hash, err = tx.TxHash() tx.Hash, err = tx.TxHash()
if err != nil { if err != nil {
return tx, err return ethtypes.EthTx{}, xerrors.Errorf("failed to calculate hash for ethTx: %w", err)
} }
} else if smsg.Signature.Type == crypto.SigTypeUnknown { // BLS Filecoin message
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Message.Cid()) fromAddr, err := lookupEthAddress(ctx, smsg.Message.From, sa)
if err != nil {
return ethtypes.EthTx{}, xerrors.Errorf("failed to resolve Ethereum address: %w", err)
}
tx.From = fromAddr
} else if smsg.Signature.Type == crypto.SigTypeSecp256k1 { // Secp Filecoin Message
tx = ethTxFromNativeMessage(ctx, smsg.VMMessage(), sa)
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid())
if err != nil { if err != nil {
return tx, err return tx, err
} }
} else { // Secp Filecoin Message } else { // BLS Filecoin message
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid()) tx = ethTxFromNativeMessage(ctx, smsg.VMMessage(), sa)
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Message.Cid())
if err != nil { if err != nil {
return tx, err return tx, err
} }
@ -1633,14 +1608,32 @@ func NewEthTxFromFilecoinMessage(ctx context.Context, smsg *types.SignedMessage,
return tx, nil return tx, nil
} }
// newEthTxFromFilecoinMessageLookup creates an ethereum transaction from filecoin message lookup. If a negative txIdx is passed // ethTxFromNativeMessage does NOT populate:
// into the function, it looksup the transaction index of the message in the tipset, otherwise it uses the txIdx passed into the // - BlockHash
// function // - BlockNumber
func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, txIdx int, cs *store.ChainStore, sa StateAPI) (ethtypes.EthTx, error) { // - TransactionIndex
if msgLookup == nil { // - Hash
return ethtypes.EthTx{}, fmt.Errorf("msg does not exist") func ethTxFromNativeMessage(ctx context.Context, msg *types.Message, sa StateAPI) ethtypes.EthTx {
// We don't care if we error here, conversion is best effort for non-eth transactions
from, _ := lookupEthAddress(ctx, msg.From, sa)
to, _ := lookupEthAddress(ctx, msg.To, sa)
return ethtypes.EthTx{
To: &to,
From: from,
Nonce: ethtypes.EthUint64(msg.Nonce),
ChainID: ethtypes.EthUint64(build.Eip155ChainId),
Value: ethtypes.EthBigInt(msg.Value),
Type: ethtypes.Eip1559TxType,
Gas: ethtypes.EthUint64(msg.GasLimit),
MaxFeePerGas: ethtypes.EthBigInt(msg.GasFeeCap),
MaxPriorityFeePerGas: ethtypes.EthBigInt(msg.GasPremium),
} }
}
// newEthTxFromMessageLookup creates an ethereum transaction from filecoin message lookup. If a negative txIdx is passed
// into the function, it looks up the transaction index of the message in the tipset, otherwise it uses the txIdx passed into the
// function
func newEthTxFromMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, txIdx int, cs *store.ChainStore, sa StateAPI) (ethtypes.EthTx, error) {
ts, err := cs.LoadTipSet(ctx, msgLookup.TipSet) ts, err := cs.LoadTipSet(ctx, msgLookup.TipSet)
if err != nil { if err != nil {
return ethtypes.EthTx{}, err return ethtypes.EthTx{}, err
@ -1689,13 +1682,13 @@ func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLo
smsg = &types.SignedMessage{ smsg = &types.SignedMessage{
Message: *msg, Message: *msg,
Signature: crypto.Signature{ Signature: crypto.Signature{
Type: crypto.SigTypeUnknown, Type: crypto.SigTypeBLS,
Data: nil, Data: nil,
}, },
} }
} }
tx, err := NewEthTxFromFilecoinMessage(ctx, smsg, sa) tx, err := newEthTxFromSignedMessage(ctx, smsg, sa)
if err != nil { if err != nil {
return ethtypes.EthTx{}, err return ethtypes.EthTx{}, err
} }
@ -1741,16 +1734,6 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook
LogsBloom: ethtypes.EmptyEthBloom[:], LogsBloom: ethtypes.EmptyEthBloom[:],
} }
if receipt.To == nil && lookup.Receipt.ExitCode.IsSuccess() {
// Create and Create2 return the same things.
var ret eam.CreateReturn
if err := ret.UnmarshalCBOR(bytes.NewReader(lookup.Receipt.Return)); err != nil {
return api.EthTxReceipt{}, xerrors.Errorf("failed to parse contract creation result: %w", err)
}
addr := ethtypes.EthAddress(ret.EthAddress)
receipt.ContractAddress = &addr
}
if lookup.Receipt.ExitCode.IsSuccess() { if lookup.Receipt.ExitCode.IsSuccess() {
receipt.Status = 1 receipt.Status = 1
} }
@ -1758,6 +1741,24 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook
receipt.Status = 0 receipt.Status = 0
} }
receipt.GasUsed = ethtypes.EthUint64(lookup.Receipt.GasUsed)
// TODO: handle CumulativeGasUsed
receipt.CumulativeGasUsed = ethtypes.EmptyEthInt
effectiveGasPrice := big.Div(replay.GasCost.TotalCost, big.NewInt(lookup.Receipt.GasUsed))
receipt.EffectiveGasPrice = ethtypes.EthBigInt(effectiveGasPrice)
if receipt.To == nil && lookup.Receipt.ExitCode.IsSuccess() {
// Create and Create2 return the same things.
var ret eam.CreateExternalReturn
if err := ret.UnmarshalCBOR(bytes.NewReader(lookup.Receipt.Return)); err != nil {
return api.EthTxReceipt{}, xerrors.Errorf("failed to parse contract creation result: %w", err)
}
addr := ethtypes.EthAddress(ret.EthAddress)
receipt.ContractAddress = &addr
}
if len(events) > 0 { if len(events) > 0 {
// TODO return a dummy non-zero bloom to signal that there are logs // TODO return a dummy non-zero bloom to signal that there are logs
// need to figure out how worth it is to populate with a real bloom // need to figure out how worth it is to populate with a real bloom
@ -1776,7 +1777,10 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook
} }
for _, entry := range evt.Entries { for _, entry := range evt.Entries {
value := ethtypes.EthBytes(leftpad32(entry.Value)) // value has already been cbor-decoded but see https://github.com/filecoin-project/ref-fvm/issues/1345 value, err := cborDecodeTopicValue(entry.Value)
if err != nil {
return api.EthTxReceipt{}, xerrors.Errorf("failed to decode event log value: %w", err)
}
if entry.Key == ethtypes.EthTopic1 || entry.Key == ethtypes.EthTopic2 || entry.Key == ethtypes.EthTopic3 || entry.Key == ethtypes.EthTopic4 { if entry.Key == ethtypes.EthTopic1 || entry.Key == ethtypes.EthTopic2 || entry.Key == ethtypes.EthTopic3 || entry.Key == ethtypes.EthTopic4 {
l.Topics = append(l.Topics, value) l.Topics = append(l.Topics, value)
} else { } else {
@ -1798,14 +1802,6 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook
} }
} }
receipt.GasUsed = ethtypes.EthUint64(lookup.Receipt.GasUsed)
// TODO: handle CumulativeGasUsed
receipt.CumulativeGasUsed = ethtypes.EmptyEthInt
effectiveGasPrice := big.Div(replay.GasCost.TotalCost, big.NewInt(lookup.Receipt.GasUsed))
receipt.EffectiveGasPrice = ethtypes.EthBigInt(effectiveGasPrice)
return receipt, nil return receipt, nil
} }
@ -1821,7 +1817,7 @@ func (m *EthTxHashManager) Apply(ctx context.Context, from, to *types.TipSet) er
continue continue
} }
hash, err := EthTxHashFromSignedFilecoinMessage(ctx, smsg, m.StateAPI) hash, err := EthTxHashFromSignedMessage(ctx, smsg, m.StateAPI)
if err != nil { if err != nil {
return err return err
} }
@ -1858,7 +1854,7 @@ func WaitForMpoolUpdates(ctx context.Context, ch <-chan api.MpoolUpdate, manager
continue continue
} }
ethTx, err := NewEthTxFromFilecoinMessage(ctx, u.Message, manager.StateAPI) ethTx, err := newEthTxFromSignedMessage(ctx, u.Message, manager.StateAPI)
if err != nil { if err != nil {
log.Errorf("error converting filecoin message to eth tx: %s", err) log.Errorf("error converting filecoin message to eth tx: %s", err)
} }
@ -1887,10 +1883,6 @@ func EthTxHashGC(ctx context.Context, retentionDays int, manager *EthTxHashManag
} }
} }
// TODO we could also emit full EVM words from the EVM runtime, but not doing so
// makes the contract slightly cheaper (and saves storage bytes), at the expense
// of having to left pad in the API, which is a pretty acceptable tradeoff at
// face value. There may be other protocol implications to consider.
func leftpad32(orig []byte) []byte { func leftpad32(orig []byte) []byte {
needed := 32 - len(orig) needed := 32 - len(orig)
if needed <= 0 { if needed <= 0 {
@ -1900,3 +1892,51 @@ func leftpad32(orig []byte) []byte {
copy(ret[needed:], orig) copy(ret[needed:], orig)
return ret return ret
} }
func trimLeadingZeros(b []byte) []byte {
for i := range b {
if b[i] != 0 {
return b[i:]
}
}
return []byte{}
}
func cborEncodeTopicValue(orig []byte) ([]byte, error) {
var buf bytes.Buffer
err := cbg.WriteByteArray(&buf, trimLeadingZeros(orig))
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
func cborDecodeTopicValue(orig []byte) ([]byte, error) {
if len(orig) == 0 {
return orig, nil
}
decoded, err := cbg.ReadByteArray(bytes.NewReader(orig), uint64(len(orig)))
if err != nil {
return nil, err
}
return leftpad32(decoded), nil
}
func parseEthTopics(topics ethtypes.EthTopicSpec) (map[string][][]byte, error) {
keys := map[string][][]byte{}
for idx, vals := range topics {
if len(vals) == 0 {
continue
}
// Ethereum topics are emitted using `LOG{0..4}` opcodes resulting in topics1..4
key := fmt.Sprintf("t%d", idx+1)
for _, v := range vals {
encodedVal, err := cborEncodeTopicValue(v[:])
if err != nil {
return nil, xerrors.Errorf("failed to encode topic value")
}
keys[key] = append(keys[key], encodedVal)
}
}
return keys, nil
}

View File

@ -40,6 +40,7 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo
ee := &full.EthEvent{ ee := &full.EthEvent{
Chain: cs, Chain: cs,
MaxFilterHeightRange: abi.ChainEpoch(cfg.Events.MaxFilterHeightRange), MaxFilterHeightRange: abi.ChainEpoch(cfg.Events.MaxFilterHeightRange),
SubscribtionCtx: ctx,
} }
if !cfg.EnableEthRPC || cfg.Events.DisableRealTimeFilterAPI { if !cfg.EnableEthRPC || cfg.Events.DisableRealTimeFilterAPI {

View File

@ -181,3 +181,7 @@ func NewSlashFilter(ds dtypes.MetadataDS) *slashfilter.SlashFilter {
func UpgradeSchedule() stmgr.UpgradeSchedule { func UpgradeSchedule() stmgr.UpgradeSchedule {
return filcns.DefaultUpgradeSchedule() return filcns.DefaultUpgradeSchedule()
} }
func EnableStoringEvents(cs *store.ChainStore) {
cs.StoreEvents(true)
}

View File

@ -75,7 +75,7 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
m := mux.NewRouter() m := mux.NewRouter()
serveRpc := func(path string, hnd interface{}) { serveRpc := func(path string, hnd interface{}) {
rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithServerErrors(api.RPCErrors))...) rpcServer := jsonrpc.NewServer(append(opts, jsonrpc.WithReverseClient[api.EthSubscriberMethods]("Filecoin"), jsonrpc.WithServerErrors(api.RPCErrors))...)
rpcServer.Register("Filecoin", hnd) rpcServer.Register("Filecoin", hnd)
rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover") rpcServer.AliasMethod("rpc.discover", "Filecoin.Discover")