Merge remote-tracking branch 'origin/release/v1.20.0' into iand/eth-openrpc-validate

This commit is contained in:
Ian Davis 2023-02-01 16:19:17 +00:00
commit d29a244064
79 changed files with 2492 additions and 1369 deletions

View File

@ -426,6 +426,10 @@ jobs:
steps:
- setup_remote_docker
- checkout
- git_fetch_all_tags
- run: git submodule sync
- run: git submodule update --init
- docker/check:
docker-username: DOCKERHUB_USERNAME
docker-password: DOCKERHUB_PASSWORD

View File

@ -426,6 +426,10 @@ jobs:
steps:
- setup_remote_docker
- checkout
- git_fetch_all_tags
- run: git submodule sync
- run: git submodule update --init
- docker/check:
docker-username: DOCKERHUB_USERNAME
docker-password: DOCKERHUB_PASSWORD

View File

@ -13,10 +13,14 @@ name: "CodeQL"
on:
push:
branches: [ master ]
branches:
- master
- 'release/*'
pull_request:
# The branches below must be a subset of the branches above
branches: [ master ]
branches:
- master
- 'release/*'
jobs:
analyze:
@ -33,17 +37,17 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v2
uses: actions/checkout@v3
- uses: actions/setup-go@v1
- uses: actions/setup-go@v3
with:
go-version: '1.18.8'
# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v1
uses: github/codeql-action/init@v2
with:
languages: ${{ matrix.language }}
languages: go
# If you wish to specify custom queries, you can do so here or in a config file.
# By default, queries listed here will override any specified in a config file.
# Prefix the list here with "+" to use these queries and those in the config file.
@ -52,7 +56,7 @@ jobs:
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
# If this step fails, then you should remove it and run the build manually (see below)
- name: Autobuild
uses: github/codeql-action/autobuild@v1
uses: github/codeql-action/autobuild@v2
# Command-line programs to run using the OS shell.
# 📚 https://git.io/JvXDl
@ -66,4 +70,4 @@ jobs:
# make release
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v1
uses: github/codeql-action/analyze@v2

8
.gitignore vendored
View File

@ -1,6 +1,3 @@
/AppDir
/appimage-builder-cache
*.AppImage
/lotus
/lotus-miner
/lotus-worker
@ -50,3 +47,8 @@ build/builtin-actors/v*
build/builtin-actors/*.car
dist/
# The following files are checked into git and result
# in dirty git state if removed from the docker context
!extern/filecoin-ffi/rust/filecoin.pc

View File

@ -1 +0,0 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" viewBox="0 0 127 127" xml:space="preserve" enable-background="new 0 0 127 127"><style type="text/css">.st0{fill:#00d2d6}.st1{fill:#fff}</style><g><path class="st0" d="M63.5,127C28.5,127.1-0.2,98.4,0,63.2C0.2,28.3,28.6-0.2,63.9,0c34.8,0.2,63.3,28.7,63.1,64 C126.7,98.7,98.5,127.1,63.5,127z M71.4,57.6c5.5,0.8,11,1.5,16.5,2.3c0.5-1.7,0.9-3.1,1.3-4.7c-5.7-0.8-11.2-1.7-17.1-2.5 c2-7,3.7-13.7,5.8-20.2c0.7-2.2,2.3-4.2,3.9-5.9c2.1-2.2,5-1.7,6.8,0.7c0.7,1,1.4,2.1,2.3,2.9c1.1,1.1,2.8,1.6,4,0.6 c0.8-0.7,0.7-2.4,0.8-3.6c0-0.5-0.6-1.1-1-1.6c-2-2.3-4.7-3.1-7.5-3.2c-6.3-0.3-10.9,3-14.5,7.8c-3.5,4.8-5.1,10.5-6.8,16.2 c-0.5,1.6-0.9,3.3-1.4,5.1c-6.2-0.9-12.1-1.7-18.2-2.6c-0.2,1.6-0.4,3.2-0.6,4.8c6,0.9,11.8,1.8,17.8,2.7c-0.8,3.4-1.5,6.5-2.3,9.7 c-5.8-0.8-11.4-1.6-17-2.4c-0.2,1.8-0.4,3.2-0.6,4.8c5.6,0.9,11,1.7,16.5,2.5c0,0.6,0.1,1,0,1.3c-1.7,7.4-3.4,14.8-5.3,22.2 c-0.9,3.5-2.4,6.9-5.3,9.3c-2.4,2-5,1.7-6.8-0.8c-0.8-1.1-1.5-2.5-2.6-3.3c-0.8-0.6-2.5-0.9-3.1-0.5c-0.9,0.7-1.5,2.2-1.4,3.3 c0.1,1,1,2.2,1.9,2.8c3,2.3,6.5,2.6,10,1.9c5.9-1.2,10.1-4.9,12.7-10.1c2-4.1,3.6-8.5,5-12.9c1.3-4,2.2-8,3.3-12.2 c5.8,0.8,11.5,1.7,17.3,2.5c0.5-1.7,0.9-3.2,1.4-4.8c-6.1-0.9-11.9-1.7-17.7-2.6C70.1,64,70.7,60.9,71.4,57.6z"/><path class="st1" d="M71.4,57.6c-0.7,3.3-1.3,6.4-2,9.8c5.9,0.9,11.7,1.7,17.7,2.6c-0.5,1.6-0.9,3.1-1.4,4.8 c-5.8-0.8-11.5-1.7-17.3-2.5c-1.1,4.2-2,8.3-3.3,12.2c-1.4,4.4-3,8.7-5,12.9c-2.6,5.2-6.8,8.9-12.7,10.1c-3.5,0.7-7,0.4-10-1.9 c-0.9-0.7-1.8-1.8-1.9-2.8c-0.1-1.1,0.5-2.7,1.4-3.3c0.6-0.5,2.3-0.1,3.1,0.5c1.1,0.8,1.8,2.1,2.6,3.3c1.8,2.5,4.4,2.9,6.8,0.8 c2.9-2.5,4.4-5.8,5.3-9.3c1.9-7.3,3.6-14.8,5.3-22.2c0.1-0.3,0-0.7,0-1.3c-5.4-0.8-10.8-1.7-16.5-2.5c0.2-1.6,0.4-3,0.6-4.8 c5.6,0.8,11.1,1.6,17,2.4c0.8-3.2,1.5-6.4,2.3-9.7c-6-0.9-11.7-1.8-17.8-2.7c0.2-1.6,0.4-3.2,0.6-4.8c6.1,0.9,12,1.7,18.2,2.6 c0.5-1.8,0.9-3.5,1.4-5.1c1.7-5.6,3.2-11.3,6.8-16.2c3.6-4.9,8.1-8.1,14.5-7.8c2.8,0.1,5.5,0.9,7.5,3.2c0.4,0.5,1,1.1,1,1.6 c-0.1,1.2,0,2.9-0.8,3.6c-1.1,1.1-2.8,0.5-4-0.6c-0.9-0.9-1.6-1.9-2.3-2.9c-1.8-2.4-4.7-2.9-6.8-0.7c-1.6,1.7-3.2,3.7-3.9,5.9 C75.7,39.4,74,46,72,53c5.9,0.9,11.4,1.7,17.1,2.5c-0.5,1.6-0.9,3.1-1.3,4.7C82.3,59.1,76.9,58.3,71.4,57.6z"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,71 +0,0 @@
version: 1
AppDir:
path: ./AppDir
app_info:
id: io.filecoin.lotus
name: Lotus
icon: icon
version: latest
exec: usr/bin/lotus
exec_args: $@
apt:
arch: amd64
allow_unauthenticated: true
sources:
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates main restricted
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates universe
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-updates multiverse
- sourceline: deb http://archive.ubuntu.com/ubuntu/ focal-backports main restricted
universe multiverse
- sourceline: deb http://security.ubuntu.com/ubuntu focal-security main restricted
- sourceline: deb http://security.ubuntu.com/ubuntu focal-security universe
- sourceline: deb http://security.ubuntu.com/ubuntu focal-security multiverse
- sourceline: deb https://cli-assets.heroku.com/apt ./
- sourceline: deb http://ppa.launchpad.net/openjdk-r/ppa/ubuntu focal main
- sourceline: deb http://ppa.launchpad.net/git-core/ppa/ubuntu focal main
- sourceline: deb http://archive.canonical.com/ubuntu focal partner
include:
- ocl-icd-libopencl1
- libhwloc15
exclude: []
files:
include:
- /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
- /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
- /usr/lib/x86_64-linux-gnu/libm-2.31.so
- /usr/lib/x86_64-linux-gnu/libdl-2.31.so
- /usr/lib/x86_64-linux-gnu/libc-2.31.so
- /usr/lib/x86_64-linux-gnu/libudev.so.1.6.17
exclude:
- usr/share/man
- usr/share/doc/*/README.*
- usr/share/doc/*/changelog.*
- usr/share/doc/*/NEWS.*
- usr/share/doc/*/TODO.*
test:
fedora:
image: appimagecrafters/tests-env:fedora-30
command: ./AppRun
use_host_x: false
debian:
image: appimagecrafters/tests-env:debian-stable
command: ./AppRun
use_host_x: false
arch:
image: appimagecrafters/tests-env:archlinux-latest
command: ./AppRun
use_host_x: false
centos:
image: appimagecrafters/tests-env:centos-7
command: ./AppRun
use_host_x: false
ubuntu:
image: appimagecrafters/tests-env:ubuntu-xenial
command: ./AppRun
use_host_x: false
AppImage:
arch: x86_64

View File

@ -33,6 +33,8 @@ RUN set -eux; \
COPY ./ /opt/filecoin
WORKDIR /opt/filecoin
RUN scripts/docker-git-state-check.sh
### make configurable filecoin-ffi build
ARG FFI_BUILD_FROM_SOURCE=0
ENV FFI_BUILD_FROM_SOURCE=${FFI_BUILD_FROM_SOURCE}

View File

@ -16,6 +16,7 @@ import (
datatransfer "github.com/filecoin-project/go-data-transfer"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
"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/big"
"github.com/filecoin-project/go-state-types/builtin/v8/paych"
@ -831,7 +832,7 @@ type FullNode interface {
// - logs: notify new event logs that match a criteria
// params contains additional parameters used with the log event type
// 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
EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) //perm:write
@ -849,6 +850,12 @@ type FullNode interface {
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 {
Response *storagemarket.StorageAsk

View File

@ -7,6 +7,7 @@ import (
"github.com/ipfs/go-cid"
"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/builtin/v9/miner"
"github.com/filecoin-project/go-state-types/dline"
@ -102,6 +103,6 @@ type Gateway interface {
EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error)
EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, 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)
}

View File

@ -35,10 +35,10 @@ func NewFullNodeRPCV0(ctx context.Context, addr string, requestHeader http.Heade
}
// 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
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
}

View File

@ -23,6 +23,7 @@ import (
bitfield "github.com/filecoin-project/go-bitfield"
datatransfer "github.com/filecoin-project/go-data-transfer"
retrievalmarket "github.com/filecoin-project/go-fil-markets/retrievalmarket"
jsonrpc "github.com/filecoin-project/go-jsonrpc"
auth "github.com/filecoin-project/go-jsonrpc/auth"
abi "github.com/filecoin-project/go-state-types/abi"
big "github.com/filecoin-project/go-state-types/big"
@ -1388,18 +1389,18 @@ func (mr *MockFullNodeMockRecorder) EthSendRawTransaction(arg0, arg1 interface{}
}
// 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()
ret := m.ctrl.Call(m, "EthSubscribe", arg0, arg1, arg2)
ret0, _ := ret[0].(<-chan ethtypes.EthSubscriptionResponse)
ret := m.ctrl.Call(m, "EthSubscribe", arg0, arg1)
ret0, _ := ret[0].(ethtypes.EthSubscriptionID)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// 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()
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.

View File

@ -22,6 +22,7 @@ import (
"github.com/filecoin-project/go-fil-markets/piecestore"
"github.com/filecoin-project/go-fil-markets/retrievalmarket"
"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-state-types/abi"
"github.com/filecoin-project/go-state-types/builtin/v8/paych"
@ -49,20 +50,25 @@ import (
var ErrNotSupported = xerrors.New("method not supported")
type ChainIOStruct struct {
Internal struct {
Internal ChainIOMethods
}
type ChainIOMethods struct {
ChainHasObj func(p0 context.Context, p1 cid.Cid) (bool, error) ``
ChainPutObj func(p0 context.Context, p1 blocks.Block) error ``
ChainReadObj func(p0 context.Context, p1 cid.Cid) ([]byte, error) ``
}
}
type ChainIOStub struct {
}
type CommonStruct struct {
Internal struct {
Internal CommonMethods
}
type CommonMethods struct {
AuthNew func(p0 context.Context, p1 []auth.Permission) ([]byte, error) `perm:"admin"`
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"`
Version func(p0 context.Context) (APIVersion, error) `perm:"read"`
}
}
type CommonStub struct {
@ -95,8 +100,10 @@ type CommonNetStruct struct {
NetStruct
Internal struct {
}
Internal CommonNetMethods
}
type CommonNetMethods struct {
}
type CommonNetStub struct {
@ -105,12 +112,26 @@ type CommonNetStub struct {
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 {
CommonStruct
NetStruct
Internal struct {
Internal FullNodeMethods
}
type FullNodeMethods struct {
ChainBlockstoreInfo func(p0 context.Context) (map[string]interface{}, error) `perm:"read"`
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"`
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"`
@ -584,7 +605,6 @@ type FullNodeStruct struct {
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 {
@ -594,7 +614,10 @@ type FullNodeStub struct {
}
type GatewayStruct struct {
Internal struct {
Internal GatewayMethods
}
type GatewayMethods struct {
ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*BlockMessages, error) ``
ChainGetGenesis func(p0 context.Context) (*types.TipSet, error) ``
@ -681,7 +704,7 @@ type GatewayStruct struct {
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) ``
@ -738,14 +761,16 @@ type GatewayStruct struct {
Version func(p0 context.Context) (APIVersion, error) ``
WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) ``
}
}
type GatewayStub struct {
}
type NetStruct struct {
Internal struct {
Internal NetMethods
}
type NetMethods struct {
ID func(p0 context.Context) (peer.ID, error) `perm:"read"`
NetAddrsListen func(p0 context.Context) (peer.AddrInfo, error) `perm:"read"`
@ -793,16 +818,17 @@ type NetStruct struct {
NetSetLimit func(p0 context.Context, p1 string, p2 NetLimit) error `perm:"admin"`
NetStat func(p0 context.Context, p1 string) (NetStat, error) `perm:"read"`
}
}
type NetStub struct {
}
type SignableStruct struct {
Internal struct {
Internal SignableMethods
}
type SignableMethods struct {
Sign func(p0 context.Context, p1 SignFunc) error ``
}
}
type SignableStub struct {
@ -813,7 +839,10 @@ type StorageMinerStruct struct {
NetStruct
Internal struct {
Internal StorageMinerMethods
}
type StorageMinerMethods struct {
ActorAddress func(p0 context.Context) (address.Address, error) `perm:"read"`
ActorAddressConfig func(p0 context.Context) (AddressConfig, error) `perm:"read"`
@ -1077,7 +1106,6 @@ type StorageMinerStruct struct {
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"`
}
}
type StorageMinerStub struct {
@ -1087,7 +1115,10 @@ type StorageMinerStub struct {
}
type WalletStruct struct {
Internal struct {
Internal WalletMethods
}
type WalletMethods struct {
WalletDelete func(p0 context.Context, p1 address.Address) error `perm:"admin"`
WalletExport func(p0 context.Context, p1 address.Address) (*types.KeyInfo, error) `perm:"admin"`
@ -1101,14 +1132,16 @@ type WalletStruct struct {
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"`
}
}
type WalletStub 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"`
DataCid func(p0 context.Context, p1 abi.UnpaddedPieceSize, p2 storiface.Data) (storiface.CallID, error) `perm:"admin"`
@ -1182,7 +1215,6 @@ type WorkerStruct struct {
Version func(p0 context.Context) (Version, error) `perm:"admin"`
WaitQuiet func(p0 context.Context) error `perm:"admin"`
}
}
type WorkerStub struct {
@ -1342,6 +1374,17 @@ func (s *CommonStub) Version(p0 context.Context) (APIVersion, error) {
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) {
if s.Internal.ChainBlockstoreInfo == nil {
return *new(map[string]interface{}), ErrNotSupported
@ -2277,15 +2320,15 @@ func (s *FullNodeStub) EthSendRawTransaction(p0 context.Context, p1 ethtypes.Eth
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 {
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) {
return nil, ErrNotSupported
func (s *FullNodeStub) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
return *new(ethtypes.EthSubscriptionID), ErrNotSupported
}
func (s *FullNodeStruct) EthUninstallFilter(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) {
@ -4422,15 +4465,15 @@ func (s *GatewayStub) EthSendRawTransaction(p0 context.Context, p1 ethtypes.EthB
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 {
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) {
return nil, ErrNotSupported
func (s *GatewayStub) EthSubscribe(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
return *new(ethtypes.EthSubscriptionID), ErrNotSupported
}
func (s *GatewayStruct) EthUninstallFilter(p0 context.Context, p1 ethtypes.EthFilterID) (bool, error) {
@ -6955,6 +6998,7 @@ func (s *WorkerStub) WaitQuiet(p0 context.Context) error {
var _ ChainIO = new(ChainIOStruct)
var _ Common = new(CommonStruct)
var _ CommonNet = new(CommonNetStruct)
var _ EthSubscriber = new(EthSubscriberStruct)
var _ FullNode = new(FullNodeStruct)
var _ Gateway = new(GatewayStruct)
var _ Net = new(NetStruct)

View File

@ -39,7 +39,10 @@ type FullNodeStruct struct {
NetStruct
Internal struct {
Internal FullNodeMethods
}
type FullNodeMethods struct {
BeaconGetEntry func(p0 context.Context, p1 abi.ChainEpoch) (*types.BeaconEntry, error) `perm:"read"`
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"`
WalletVerify func(p0 context.Context, p1 address.Address, p2 []byte, p3 *crypto.Signature) (bool, error) `perm:"read"`
}
}
type FullNodeStub struct {
@ -425,7 +427,10 @@ type FullNodeStub struct {
}
type GatewayStruct struct {
Internal struct {
Internal GatewayMethods
}
type GatewayMethods struct {
ChainGetBlockMessages func(p0 context.Context, p1 cid.Cid) (*api.BlockMessages, 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) ``
WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) ``
}
}
type GatewayStub struct {

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 ActorDebugging = true
const GenesisNetworkVersion = network.Version18
const GenesisNetworkVersion = network.Version17
var UpgradeBreezeHeight = abi.ChainEpoch(-1)
@ -59,7 +59,7 @@ var UpgradeSkyrHeight = abi.ChainEpoch(-19)
var UpgradeSharkHeight = abi.ChainEpoch(-20)
var UpgradeHyggeHeight = abi.ChainEpoch(-21)
var UpgradeHyggeHeight = abi.ChainEpoch(30)
var DrandSchedule = map[abi.ChainEpoch]DrandEnum{
0: DrandMainnet,

View File

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

View File

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

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) {
q, err := ei.db.Query("SELECT cid FROM eth_tx_hashes WHERE hash = :hash;", sql.Named("hash", txHash.String()))
if err != nil {
return cid.Undef, err
}
row := ei.db.QueryRow("SELECT cid FROM eth_tx_hashes WHERE hash = :hash;", sql.Named("hash", txHash.String()))
var c string
if !q.Next() {
err := row.Scan(&c)
if err != nil {
if err == sql.ErrNoRows {
return cid.Undef, ErrNotFound
}
err = q.Scan(&c)
if err != nil {
return cid.Undef, err
}
return cid.Decode(c)
}
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()))
if err != nil {
return ethtypes.EmptyEthHash, err
}
row := ei.db.QueryRow("SELECT hash FROM eth_tx_hashes WHERE cid = :cid;", sql.Named("cid", c.String()))
var hashString string
if !q.Next() {
err := row.Scan(&c)
if err != nil {
if err == sql.ErrNoRows {
return ethtypes.EmptyEthHash, ErrNotFound
}
err = q.Scan(&hashString)
if err != nil {
return ethtypes.EmptyEthHash, err
}
return ethtypes.ParseEthHash(hashString)

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) {
if sigType == crypto.SigTypeDelegated {
txArgs, err := ethtypes.EthTxArgsFromMessage(msg)
txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(msg)
if err != nil {
return nil, xerrors.Errorf("failed to reconstruct eth transaction: %w", err)
}

View File

@ -22,7 +22,7 @@ func AuthenticateMessage(msg *types.SignedMessage, signer address.Address) error
typ := msg.Signature.Type
switch typ {
case crypto.SigTypeDelegated:
txArgs, err := ethtypes.EthTxArgsFromMessage(&msg.Message)
txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(&msg.Message)
if err != nil {
return 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.BurntFundsActorAddr ||
a == builtin.SaftAddress ||
a == builtin.ReserveAddress:
a == builtin.ReserveAddress ||
a == builtin.EthereumAddressManagerActorAddr:
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))
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)
case builtin.IsStorageMinerActor(actor.Code):

View File

@ -56,7 +56,44 @@ type EthTxArgs struct {
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 (
to *EthAddress
params []byte

View File

@ -363,6 +363,18 @@ func (h *EthHash) UnmarshalJSON(b []byte) error {
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) {
s = handleHexStringPrefix(s)
if len(s) != expectedLen*2 {
@ -420,18 +432,6 @@ func EthHashFromTxBytes(b []byte) 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 {
OldestBlock EthUint64 `json:"oldestBlock"`
BaseFeePerGas []EthBigInt `json:"baseFeePerGas"`
@ -441,49 +441,31 @@ type EthFeeHistory struct {
type EthFilterID EthHash
func (id EthFilterID) String() string {
return "0x" + hex.EncodeToString(id[:])
func (h EthFilterID) MarshalJSON() ([]byte, error) {
return (EthHash)(h).MarshalJSON()
}
func (id EthFilterID) MarshalJSON() ([]byte, error) {
return json.Marshal(id.String())
func (h *EthFilterID) UnmarshalJSON(b []byte) error {
return (*EthHash)(h).UnmarshalJSON(b)
}
func (id *EthFilterID) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
hash, err := ParseEthHash(s)
if err != nil {
return err
}
copy(id[:], hash[:])
return nil
func (h EthFilterID) String() string {
return (EthHash)(h).String()
}
// An opaque identifier generated by the Lotus node to refer to an active subscription.
type EthSubscriptionID EthHash
func (id EthSubscriptionID) String() string {
return "0x" + hex.EncodeToString(id[:])
func (h EthSubscriptionID) MarshalJSON() ([]byte, error) {
return (EthHash)(h).MarshalJSON()
}
func (id EthSubscriptionID) MarshalJSON() ([]byte, error) {
return json.Marshal(id.String())
func (h *EthSubscriptionID) UnmarshalJSON(b []byte) error {
return (*EthHash)(h).UnmarshalJSON(b)
}
func (id *EthSubscriptionID) UnmarshalJSON(b []byte) error {
var s string
if err := json.Unmarshal(b, &s); err != nil {
return err
}
hash, err := ParseEthHash(s)
if err != nil {
return err
}
copy(id[:], hash[:])
return nil
func (h EthSubscriptionID) String() string {
return (EthHash)(h).String()
}
type EthFilterSpec struct {
@ -637,6 +619,43 @@ type EthLog struct {
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 {
// List of topics to be matched.
// Optional, default: empty list

View File

@ -93,6 +93,48 @@ func TestEthHash(t *testing.T) {
h1, err := EthHashFromCid(c)
require.Nil(t, err)
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

@ -319,11 +319,33 @@ func GetFullNodeAPIV1Single(ctx *cli.Context) (v1api.FullNode, jsonrpc.ClientClo
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 {
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")
if err != nil {
return nil, nil, err
@ -337,7 +359,7 @@ func GetFullNodeAPIV1(ctx *cli.Context) (v1api.FullNode, jsonrpc.ClientCloser, e
var closers []jsonrpc.ClientCloser
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 {
log.Warnf("Not able to establish connection to node with addr: ", head.addr)
continue

View File

@ -162,7 +162,9 @@ var runCmd = &cli.Command{
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 {
return err
}
@ -195,7 +197,7 @@ var runCmd = &cli.Command{
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...)
if err != nil {
return xerrors.Errorf("failed to set up gateway HTTP handler")

View File

@ -2342,7 +2342,7 @@ Inputs:
Response:
```json
{
"oldestBlock": "0x5",
"oldestBlock": 42,
"baseFeePerGas": [
"0x0"
],
@ -2407,7 +2407,7 @@ Response:
"gasLimit": "0x5",
"gasUsed": "0x5",
"timestamp": "0x5",
"extraData": "0x07",
"extraData": "Ynl0ZSBhcnJheQ==",
"mixHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e",
"nonce": "0x0707070707070707",
"baseFeePerGas": "0x0",
@ -2451,7 +2451,7 @@ Response:
"gasLimit": "0x5",
"gasUsed": "0x5",
"timestamp": "0x5",
"extraData": "0x07",
"extraData": "Ynl0ZSBhcnJheQ==",
"mixHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e",
"nonce": "0x0707070707070707",
"baseFeePerGas": "0x0",
@ -2886,24 +2886,11 @@ Perms: write
Inputs:
```json
[
"string value",
{
"topics": [
[
"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"
]
]
}
"Bw=="
]
```
Response:
```json
{
"subscription": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e",
"result": {}
}
```
Response: `"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"`
### EthUninstallFilter
Uninstalls a filter with given id.

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
const filterTrackerKey filterTrackerKeyType = "filterTracker"
const statefulCallTrackerKey filterTrackerKeyType = "statefulCallTracker"
// 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) {
m := mux.NewRouter()
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.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))
// 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)
}

View File

@ -12,6 +12,7 @@ import (
"github.com/filecoin-project/go-address"
"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/dline"
"github.com/filecoin-project/go-state-types/network"
@ -117,7 +118,7 @@ type TargetAPI interface {
EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error)
EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, 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)
}
@ -125,6 +126,7 @@ var _ TargetAPI = *new(api.FullNode) // gateway depends on latest
type Node struct {
target TargetAPI
subHnd *EthSubHandler
lookbackCap time.Duration
stateWaitLookbackLimit abi.ChainEpoch
rateLimiter *rate.Limiter
@ -141,7 +143,7 @@ var (
)
// 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
if rateLimit == 0 {
limit = rate.Inf
@ -150,6 +152,7 @@ func NewNode(api TargetAPI, lookbackCap time.Duration, stateWaitLookbackLimit ab
}
return &Node{
target: api,
subHnd: sHnd,
lookbackCap: lookbackCap,
stateWaitLookbackLimit: stateWaitLookbackLimit,
rateLimiter: rate.NewLimiter(limit, stateRateLimitTokens),

View File

@ -89,7 +89,7 @@ func TestGatewayAPIChainGetTipSetByHeight(t *testing.T) {
tt := tt
t.Run(tt.name, func(t *testing.T) {
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
ts := mock.createTipSets(tt.args.tskh, tt.args.genesisTS)
@ -245,7 +245,7 @@ func TestGatewayVersion(t *testing.T) {
//stm: @GATEWAY_NODE_GET_VERSION_001
ctx := context.Background()
mock := &mockGatewayDepsAPI{}
a := NewNode(mock, DefaultLookbackCap, DefaultStateWaitLookbackLimit, 0, time.Minute)
a := NewNode(mock, nil, DefaultLookbackCap, DefaultStateWaitLookbackLimit, 0, time.Minute)
v, err := a.Version(ctx)
require.NoError(t, err)
@ -256,7 +256,7 @@ func TestGatewayLimitTokensAvailable(t *testing.T) {
ctx := context.Background()
mock := &mockGatewayDepsAPI{}
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")
}
@ -264,7 +264,7 @@ func TestGatewayLimitTokensNotAvailable(t *testing.T) {
ctx := context.Background()
mock := &mockGatewayDepsAPI{}
tokens := 3
a := NewNode(mock, DefaultLookbackCap, DefaultStateWaitLookbackLimit, int64(1), time.Millisecond)
a := NewNode(mock, nil, DefaultLookbackCap, DefaultStateWaitLookbackLimit, int64(1), time.Millisecond)
var err error
// try to be rate limited
for i := 0; i <= 1000; i++ {

View File

@ -3,12 +3,14 @@ package gateway
import (
"bytes"
"context"
"encoding/json"
"fmt"
"sync"
"time"
"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/big"
@ -352,7 +354,7 @@ func (gw *Node) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID
return nil, err
}
ft := filterTrackerFromContext(ctx)
ft := statefulCallFromContext(ctx)
ft.lk.Lock()
_, ok := ft.userFilters[id]
ft.lk.Unlock()
@ -369,7 +371,7 @@ func (gw *Node) EthGetFilterLogs(ctx context.Context, id ethtypes.EthFilterID) (
return nil, err
}
ft := filterTrackerFromContext(ctx)
ft := statefulCallFromContext(ctx)
ft.lk.Lock()
_, ok := ft.userFilters[id]
ft.lk.Unlock()
@ -417,7 +419,7 @@ func (gw *Node) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID)
}
// check if the filter belongs to this connection
ft := filterTrackerFromContext(ctx)
ft := statefulCallFromContext(ctx)
ft.lk.Lock()
defer ft.lk.Unlock()
@ -434,18 +436,88 @@ func (gw *Node) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID)
return ok, nil
}
func (gw *Node) EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) {
return nil, xerrors.Errorf("not implemented")
func (gw *Node) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
// 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) {
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
func addUserFilterLimited(ctx context.Context, cb func() (ethtypes.EthFilterID, error)) (ethtypes.EthFilterID, error) {
ft := filterTrackerFromContext(ctx)
ft := statefulCallFromContext(ctx)
ft.lk.Lock()
defer ft.lk.Unlock()
@ -463,19 +535,21 @@ func addUserFilterLimited(ctx context.Context, cb func() (ethtypes.EthFilterID,
return id, nil
}
func filterTrackerFromContext(ctx context.Context) *filterTracker {
return ctx.Value(filterTrackerKey).(*filterTracker)
func statefulCallFromContext(ctx context.Context) *statefulCallTracker {
return ctx.Value(statefulCallTrackerKey).(*statefulCallTracker)
}
type filterTracker struct {
type statefulCallTracker struct {
lk sync.Mutex
userFilters map[ethtypes.EthFilterID]time.Time
userSubscriptions map[ethtypes.EthSubscriptionID]time.Time
}
// called per request (ws connection)
func newFilterTracker() *filterTracker {
return &filterTracker{
func newStatefulCallTracker() *statefulCallTracker {
return &statefulCallTracker{
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 {
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
}
info.Methods[mname].Tags[tf[0]] = tf
@ -302,12 +302,14 @@ type {{.Num}}Struct struct {
{{range .Include}}
{{.}}Struct
{{end}}
Internal struct {
Internal {{.Num}}Methods
}
type {{.Num}}Methods struct {
{{range .Methods}}
{{.Num}} func({{.NamedParams}}) ({{.Results}}) `+"`"+`{{range .Tags}}{{index . 0}}:"{{index . 1}}"{{end}}`+"`"+`
{{end}}
}
}
type {{.Num}}Stub struct {
{{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-commp-hashhash v0.1.0
github.com/filecoin-project/go-fil-markets v1.25.2
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-padreader v0.0.1
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-statestore v0.2.0
github.com/filecoin-project/go-storedcounter v0.1.0

9
go.sum
View File

@ -338,8 +338,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.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-jsonrpc v0.1.9 h1:HRWLxo7HAWzI3xZGeFG4LZJoYpms+Q+8kwmMTLnyS3A=
github.com/filecoin-project/go-jsonrpc v0.1.9/go.mod h1:XBBpuKIMaXIIzeqzO1iucq4GvbF8CxmXRFoezRh+Cx4=
github.com/filecoin-project/go-jsonrpc v0.2.1 h1:xfxkfIAO300sPiV59DnxnCb4sdTtWYlRz/TsP+ByT2E=
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/go.mod h1:JQ3hA6xpJdbR8euZ2rO0jkxaMxeidXf0LDnVuqPAe9s=
github.com/filecoin-project/go-padreader v0.0.0-20200903213702-ed5fae088b20/go.mod h1:mPn+LRRd5gEKNAtc+r3ScpW2JRU/pj4NBKdADYWHiak=
@ -354,8 +354,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.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.10.0-alpha-10 h1:QUpSayVFUADlrtzCh7SDNlbuaNSlYPBR46Nt7WpFl9I=
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 h1:lfrbmLXaC3vQk1gQCUwtTuY1U2ANrgDsJ7+VapBjRCo=
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 v1.0.2 h1:421SSWBk8GIoCoWYYTE/d+qCWccgmRH0uXotXRDjUbc=
github.com/filecoin-project/go-statemachine v1.0.2/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54=
@ -835,7 +835,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.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.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.2-0.20200626104915-0016c0b4b3e4/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');
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 @@
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));
}
}

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
# 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
find -type f -name \*.sol -print0 |
xargs -0 -I{} bash -c 'solc --bin {} |tail -n1 | tr -d "\n" > $(echo {} | sed -e s/.sol$/.hex/)'
find . -name \*.sol -print0 |
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

@ -72,7 +72,7 @@ func TestEthAccountAbstraction(t *testing.T) {
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
require.NoError(t, err)
txArgs, err := ethtypes.EthTxArgsFromMessage(msgFromPlaceholder)
txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder)
require.NoError(t, err)
digest, err := txArgs.ToRlpUnsignedMsg()
@ -108,7 +108,7 @@ func TestEthAccountAbstraction(t *testing.T) {
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
require.NoError(t, err)
txArgs, err = ethtypes.EthTxArgsFromMessage(msgFromPlaceholder)
txArgs, err = ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder)
require.NoError(t, err)
digest, err = txArgs.ToRlpUnsignedMsg()
@ -180,7 +180,7 @@ func TestEthAccountAbstractionFailure(t *testing.T) {
require.NoError(t, err)
msgFromPlaceholder.Value = abi.TokenAmount(types.MustParseFIL("1000"))
txArgs, err := ethtypes.EthTxArgsFromMessage(msgFromPlaceholder)
txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder)
require.NoError(t, err)
digest, err := txArgs.ToRlpUnsignedMsg()
@ -218,7 +218,7 @@ func TestEthAccountAbstractionFailure(t *testing.T) {
msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK)
require.NoError(t, err)
txArgs, err = ethtypes.EthTxArgsFromMessage(msgFromPlaceholder)
txArgs, err = ethtypes.EthTxArgsFromUnsignedEthMessage(msgFromPlaceholder)
require.NoError(t, err)
digest, err = txArgs.ToRlpUnsignedMsg()

View File

@ -5,6 +5,7 @@ import (
"context"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"sort"
@ -19,6 +20,7 @@ import (
"golang.org/x/xerrors"
"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/big"
@ -27,6 +29,7 @@ import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/types/ethtypes"
"github.com/filecoin-project/lotus/itests/kit"
res "github.com/filecoin-project/lotus/lib/result"
)
func TestEthNewPendingTransactionFilter(t *testing.T) {
@ -389,15 +392,15 @@ func TestEthSubscribeLogsNoTopicSpec(t *testing.T) {
t.Logf("actor ID address is %s", idAddr)
// install filter
respCh, err := client.EthSubscribe(ctx, "logs", nil)
subId, err := client.EthSubscribe(ctx, res.Wrap[jsonrpc.RawParams](json.Marshal(ethtypes.EthSubscribeParams{EventType: "logs"})).Assert(require.NoError))
require.NoError(err)
subResponses := []ethtypes.EthSubscriptionResponse{}
go func() {
for resp := range respCh {
subResponses = append(subResponses, resp)
}
}()
var subResponses []ethtypes.EthSubscriptionResponse
err = client.EthSubRouter.AddSub(ctx, subId, func(ctx context.Context, resp *ethtypes.EthSubscriptionResponse) error {
subResponses = append(subResponses, *resp)
return nil
})
require.NoError(err)
const iterations = 10
ethContractAddr, messages := invokeLogFourData(t, client, iterations)
@ -518,44 +521,33 @@ func TestEthSubscribeLogs(t *testing.T) {
testResponses := map[string]chan ethtypes.EthSubscriptionResponse{}
// quit is used to signal that we're ready to start testing collected results
quit := make(chan struct{})
// Create all the filters
for _, tc := range testCases {
// subscribe to topics in filter
subCh, err := client.EthSubscribe(ctx, "logs", &ethtypes.EthSubscriptionParams{Topics: tc.spec.Topics})
subParam, err := json.Marshal(ethtypes.EthSubscribeParams{
EventType: "logs",
Params: &ethtypes.EthSubscriptionParams{Topics: tc.spec.Topics},
})
require.NoError(err)
subId, err := client.EthSubscribe(ctx, subParam)
require.NoError(err)
responseCh := make(chan ethtypes.EthSubscriptionResponse, len(invocations))
testResponses[tc.name] = responseCh
// start a goroutine to forward responses from subscription to a buffered channel with guaranteed capacity
go func(subCh <-chan ethtypes.EthSubscriptionResponse, responseCh chan<- ethtypes.EthSubscriptionResponse, quit chan struct{}) {
defer func() {
close(responseCh)
}()
for {
select {
case resp := <-subCh:
responseCh <- resp
case <-quit:
return
case <-ctx.Done():
return
}
}
}(subCh, responseCh, quit)
err = client.EthSubRouter.AddSub(ctx, subId, func(ctx context.Context, resp *ethtypes.EthSubscriptionResponse) error {
responseCh <- *resp
return nil
})
require.NoError(err)
}
// Perform all the invocations
messages := invokeAndWaitUntilAllOnChain(t, client, invocations)
// wait a little for subscriptions to gather results and then tell all the goroutines to stop
// wait a little for subscriptions to gather results
time.Sleep(blockTime * 6)
close(quit)
for _, tc := range testCases {
tc := tc // appease the lint despot
@ -563,6 +555,9 @@ func TestEthSubscribeLogs(t *testing.T) {
responseCh, ok := testResponses[tc.name]
require.True(ok)
// don't expect any more responses
close(responseCh)
var elogs []*ethtypes.EthLog
for resp := range responseCh {
rlist, ok := resp.Result.([]interface{})
@ -1055,7 +1050,8 @@ func invokeAndWaitUntilAllOnChain(t *testing.T, client *kit.TestFullNode, invoca
}
}
}
ret := client.EVM().InvokeSolidity(ctx, inv.Sender, inv.Target, inv.Selector, inv.Data)
ret, err := client.EVM().InvokeSolidity(ctx, inv.Sender, inv.Target, inv.Selector, inv.Data)
require.NoError(err)
require.True(ret.Receipt.ExitCode.IsSuccess(), "contract execution failed")
invocationMap[ret.Message] = inv

View File

@ -83,6 +83,11 @@ func TestValueTransferValidSignature(t *testing.T) {
// Success.
require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status)
ethTx, err := client.EthGetTransactionByHash(ctx, &hash)
require.Nil(t, err)
require.EqualValues(t, ethAddr, ethTx.From)
}
func TestLegacyTransaction(t *testing.T) {

View File

@ -6,7 +6,6 @@ import (
"encoding/hex"
"os"
"testing"
"time"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/sha3"
@ -44,14 +43,43 @@ func effectiveEthAddressForCreate(t *testing.T, sender address.Address) ethtypes
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) {
kit.QuietMiningLogs()
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)
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
// install contract
@ -72,22 +100,11 @@ func TestAddressCreationBeforeDeploy(t *testing.T) {
contractFilAddr, err := ethAddr.ToFilecoinAddress()
require.NoError(t, err)
// Send contract address some funds
//transfer half the wallet balance
bal, err := client.WalletBalance(ctx, client.DefaultKey.Address)
require.NoError(t, err)
sendAmount := big.Div(bal, big.NewInt(2))
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)
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)
@ -95,40 +112,69 @@ func TestAddressCreationBeforeDeploy(t *testing.T) {
require.True(t, builtin.IsPlaceholderActor(actor.Code))
// Create and deploy evm actor
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)
wait := createAndDeploy(ctx, t, client, fromAddr, contract)
// 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)
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))
}
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)
// 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.NotNil(ret.Receipt.EventsRoot)
fmt.Println(ret)
fmt.Printf("Events:\n %+v\n", client.EVM().LoadEvents(ctx, *ret.Receipt.EventsRoot))
// 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")
fmt.Println(ret)
fmt.Printf("Events:\n %+v\n", client.EVM().LoadEvents(ctx, *ret.Receipt.EventsRoot))
// 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")
fmt.Println(ret)
fmt.Printf("Events:\n %+v\n", client.EVM().LoadEvents(ctx, *ret.Receipt.EventsRoot))

View File

@ -1,15 +1,17 @@
package itests
import (
"bytes"
"context"
"encoding/binary"
"encoding/hex"
"fmt"
"testing"
"time"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/big"
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"
@ -38,19 +40,139 @@ func inputDataFromFrom(ctx context.Context, t *testing.T, client *kit.TestFullNo
return inputData
}
func setupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *kit.TestFullNode) {
kit.QuietMiningLogs()
blockTime := 5 * time.Millisecond
client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC())
ens.InterconnectAll().BeginMiningMustPost(blockTime)
ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
return ctx, cancel, client
func decodeOutputToUint64(output []byte) (uint64, error) {
var result uint64
buf := bytes.NewReader(output[len(output)-8:])
err := binary.Read(buf, binary.BigEndian, &result)
return result, err
}
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
func TestFEVMBasic(t *testing.T) {
ctx, cancel, client := setupFEVMTest(t)
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
filename := "contracts/SimpleCoin.hex"
@ -60,7 +182,8 @@ func TestFEVMBasic(t *testing.T) {
// invoke the contract with owner
{
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")
require.NoError(t, err)
@ -70,8 +193,9 @@ func TestFEVMBasic(t *testing.T) {
// invoke the contract with non owner
{
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
result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "getBalance(address)", inputData)
inputData[31]++ // change the pub address to one that has 0 balance by modifying the last byte of the address
result, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "getBalance(address)", inputData)
require.NoError(t, err)
expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
require.NoError(t, err)
@ -81,7 +205,7 @@ func TestFEVMBasic(t *testing.T) {
// TestFEVMETH0 tests that the ETH0 actor is in genesis
func TestFEVMETH0(t *testing.T) {
ctx, cancel, client := setupFEVMTest(t)
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
eth0id, err := address.NewIDAddress(1001)
@ -100,7 +224,47 @@ func TestFEVMETH0(t *testing.T) {
// TestFEVMDelegateCall deploys two contracts and makes a delegate call transaction
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()
//install contract Actor
@ -119,20 +283,247 @@ func TestFEVMDelegateCall(t *testing.T) {
inputData := append(inputDataContract, inputDataValue...)
//verify that the returned value of the call to setvars is 7
result := client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "setVars(address,uint256)", inputData)
expectedResult, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000007")
require.NoError(t, err)
require.Equal(t, result, expectedResult)
_, wait, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "setVarsRevert(address,uint256)", inputData)
require.Error(t, err)
require.Equal(t, exitcode.ExitCode(33), wait.Receipt.ExitCode)
//test the value is 7 via calling the getter
result = client.EVM().InvokeContractByFuncName(ctx, fromAddr, storageAddr, "getCounter()", []byte{})
//test the value is 0 via calling the getter and was not set to 7
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)
//test the value is 0 via calling the getter on the Actor contract
result = client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "getCounter()", []byte{})
expectedResultActor, err := hex.DecodeString("0000000000000000000000000000000000000000000000000000000000000000")
result, _, err = client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "getCounter()", []byte{})
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) {
@ -144,7 +535,7 @@ func TestEVMRpcDisable(t *testing.T) {
// TestFEVMRecursiveFuncCall deploys a contract and makes a recursive function calls
func TestFEVMRecursiveFuncCall(t *testing.T) {
ctx, cancel, client := setupFEVMTest(t)
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract Actor
@ -155,7 +546,6 @@ func TestFEVMRecursiveFuncCall(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)
}
}
@ -170,7 +560,7 @@ func TestFEVMRecursiveFuncCall(t *testing.T) {
// TestFEVMRecursiveActorCall deploys a contract and makes a recursive actor calls
func TestFEVMRecursiveActorCall(t *testing.T) {
ctx, cancel, client := setupFEVMTest(t)
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()
//install contract Actor

View File

@ -290,7 +290,7 @@ func startNodes(
ens.InterconnectAll().BeginMining(blocktime)
// 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)
require.NoError(t, err)

View File

@ -47,6 +47,7 @@ import (
"github.com/filecoin-project/lotus/chain/wallet/key"
"github.com/filecoin-project/lotus/cmd/lotus-seed/seed"
"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/markets/idxprov"
"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)
}
*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)
return n

View File

@ -7,8 +7,11 @@ import (
"encoding/hex"
"fmt"
"os"
"testing"
"time"
"github.com/ipfs/go-cid"
logging "github.com/ipfs/go-log/v2"
"github.com/multiformats/go-varint"
"github.com/stretchr/testify/require"
cbg "github.com/whyrusleeping/cbor-gen"
@ -101,13 +104,13 @@ func (e *EVM) DeployContractFromFilename(ctx context.Context, binFilename string
return fromAddr, idAddr
}
func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target address.Address, selector []byte, inputData []byte) *api.MsgLookup {
require := require.New(e.t)
func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target address.Address, selector []byte, inputData []byte) (*api.MsgLookup, error) {
params := append(selector, inputData...)
var buffer bytes.Buffer
err := cbg.WriteByteArray(&buffer, params)
require.NoError(err)
if err != nil {
return nil, err
}
params = buffer.Bytes()
msg := &types.Message{
@ -121,13 +124,17 @@ func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target
e.t.Log("sending invoke message")
smsg, err := e.MpoolPushMessage(ctx, msg, nil)
require.NoError(err)
if err != nil {
return nil, err
}
e.t.Log("waiting for message to execute")
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.
@ -237,18 +244,25 @@ func (e *EVM) ComputeContractAddress(deployer ethtypes.EthAddress, nonce uint64)
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)
wait := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData)
require.True(e.t, wait.Receipt.ExitCode.IsSuccess(), "contract execution failed: %d", wait.Receipt.ExitCode)
wait, err := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData)
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)))
require.NoError(e.t, err)
return result
if err != nil {
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)
wait, _ := e.InvokeSolidity(ctx, fromAddr, idAddr, entryPoint, inputData)
require.Equal(e.t, exit, wait.Receipt.ExitCode)
}
@ -311,6 +325,43 @@ func removeLeadingZeros(data []byte) []byte {
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)
}
func NewEthFilterBuilder() *EthFilterBuilder {
return &EthFilterBuilder{}
}

View File

@ -22,6 +22,7 @@ import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet/key"
cliutil "github.com/filecoin-project/lotus/cli/util"
"github.com/filecoin-project/lotus/gateway"
"github.com/filecoin-project/lotus/node"
)
@ -46,6 +47,10 @@ type TestFullNode struct {
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
}

View File

@ -13,6 +13,8 @@ import (
manet "github.com/multiformats/go-multiaddr/net"
"github.com/stretchr/testify/require"
"github.com/filecoin-project/go-jsonrpc"
"github.com/filecoin-project/lotus/api/client"
"github.com/filecoin-project/lotus/cmd/lotus-worker/sealworker"
"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())
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)
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
}
func (r Result[T]) Assert(noErrFn func(err error, msgAndArgs ...interface{})) T {
noErrFn(r.Error)
return r.Value
}

View File

@ -3,6 +3,7 @@ package full
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"strconv"
@ -16,6 +17,7 @@ import (
"golang.org/x/xerrors"
"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/big"
builtintypes "github.com/filecoin-project/go-state-types/builtin"
@ -75,7 +77,7 @@ type EthEventAPI interface {
EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error)
EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, 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)
}
@ -132,6 +134,7 @@ type EthEvent struct {
FilterStore filter.FilterStore
SubManager *EthSubscriptionManager
MaxFilterHeightRange abi.ChainEpoch
SubscribtionCtx context.Context
}
var _ EthEventAPI = (*EthEvent)(nil)
@ -212,7 +215,7 @@ func (a *EthModule) EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthH
if err != nil {
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) {
@ -249,7 +252,7 @@ func (a *EthModule) EthGetBlockByNumber(ctx context.Context, blkParam string, fu
if err != nil {
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) {
@ -270,8 +273,8 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype
// first, try to get the cid from mined transactions
msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, c, api.LookbackNoLimit, true)
if err == nil {
tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI)
if err == nil && msgLookup != nil {
tx, err := newEthTxFromMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI)
if err == nil {
return &tx, nil
}
@ -287,7 +290,7 @@ func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *ethtype
for _, p := range pending {
if p.Cid() == c {
tx, err := NewEthTxFromFilecoinMessage(ctx, p, a.StateAPI)
tx, err := newEthTxFromSignedMessage(ctx, p, a.StateAPI)
if err != nil {
return nil, fmt.Errorf("could not convert Filecoin message into tx: %s", err)
}
@ -336,7 +339,7 @@ func (a *EthModule) EthGetMessageCidByTransactionHash(ctx context.Context, txHas
}
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 {
// not found
return nil, nil
@ -379,7 +382,7 @@ func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash ethtype
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 {
return nil, nil
}
@ -610,7 +613,7 @@ func (a *EthModule) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint
for ts.Height() >= abi.ChainEpoch(oldestBlkHeight) {
// Unfortunately we need to rebuild the full message view so we can
// 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 {
return ethtypes.EthFeeHistory{}, fmt.Errorf("cannot create eth block: %v", err)
}
@ -695,13 +698,6 @@ func (a *EthModule) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.Et
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)
if err != nil {
return ethtypes.EmptyEthHash, err
@ -1111,37 +1107,45 @@ const (
EthSubscribeEventTypeLogs = "logs"
)
func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *ethtypes.EthSubscriptionParams) (<-chan ethtypes.EthSubscriptionResponse, error) {
if e.SubManager == nil {
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)
func (e *EthEvent) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) {
params, err := jsonrpc.DecodeParams[ethtypes.EthSubscribeParams](p)
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:
f, err := e.TipSetFilterManager.Install(ctx)
if err != nil {
// clean up any previous filters added and stop the sub
_, _ = e.EthUnsubscribe(ctx, sub.id)
return nil, err
return ethtypes.EthSubscriptionID{}, err
}
sub.addFilter(ctx, f)
case EthSubscribeEventTypeLogs:
keys := map[string][][]byte{}
if params != nil {
if params.Params != nil {
var err error
keys, err = parseEthTopics(params.Topics)
keys, err = parseEthTopics(params.Params.Topics)
if err != nil {
// clean up any previous filters added and stop the sub
_, _ = e.EthUnsubscribe(ctx, sub.id)
return nil, err
return ethtypes.EthSubscriptionID{}, err
}
}
@ -1149,14 +1153,14 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, eventType string, params *e
if err != nil {
// clean up any previous filters added and stop the sub
_, _ = e.EthUnsubscribe(ctx, sub.id)
return nil, err
return ethtypes.EthSubscriptionID{}, err
}
sub.addFilter(ctx, f)
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) {
@ -1244,7 +1248,7 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*etht
return nil, err
}
log.TransactionHash, err = EthTxHashFromFilecoinMessageCid(context.TODO(), ev.MsgCid, sa)
log.TransactionHash, err = EthTxHashFromMessageCid(context.TODO(), ev.MsgCid, sa)
if err != nil {
return nil, err
}
@ -1286,7 +1290,7 @@ func ethFilterResultFromMessages(cs []*types.SignedMessage, sa StateAPI) (*ethty
res := &ethtypes.EthFilterResult{}
for _, c := range cs {
hash, err := EthTxHashFromSignedFilecoinMessage(context.TODO(), c, sa)
hash, err := EthTxHashFromSignedMessage(context.TODO(), c, sa)
if err != nil {
return nil, err
}
@ -1305,7 +1309,7 @@ type EthSubscriptionManager struct {
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()
if err != nil {
return nil, xerrors.Errorf("new uuid: %w", err)
@ -1321,7 +1325,7 @@ func (e *EthSubscriptionManager) StartSubscription(ctx context.Context) (*ethSub
ChainAPI: e.ChainAPI,
id: id,
in: make(chan interface{}, 200),
out: make(chan ethtypes.EthSubscriptionResponse, 20),
out: out,
quit: quit,
}
@ -1351,13 +1355,15 @@ func (e *EthSubscriptionManager) StopSubscription(ctx context.Context, id ethtyp
return sub.filters, nil
}
type ethSubscriptionCallback func(context.Context, jsonrpc.RawParams) error
type ethSubscription struct {
Chain *store.ChainStore
StateAPI StateAPI
ChainAPI ChainAPI
id ethtypes.EthSubscriptionID
in chan interface{}
out chan ethtypes.EthSubscriptionResponse
out ethSubscriptionCallback
mu sync.Mutex
filters []filter.Filter
@ -1387,7 +1393,7 @@ func (e *ethSubscription) start(ctx context.Context) {
case *filter.CollectedEvent:
resp.Result, err = ethFilterResultFromEvents([]*filter.CollectedEvent{vt}, e.StateAPI)
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 {
break
}
@ -1401,10 +1407,15 @@ func (e *ethSubscription) start(ctx context.Context) {
continue
}
select {
case e.out <- resp:
default:
// Skip if client is not reading responses
outParam, err := json.Marshal(resp)
if err != nil {
log.Warnw("marshaling subscription response", "sub", e.id, "error", err)
continue
}
if err := e.out(ctx, outParam); err != nil {
log.Warnw("sending subscription response", "sub", e.id, "error", err)
continue
}
}
}
@ -1416,12 +1427,11 @@ func (e *ethSubscription) stop() {
if e.quit != nil {
e.quit()
close(e.out)
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())
if err != nil {
return ethtypes.EthBlock{}, err
@ -1460,7 +1470,7 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx
}
gasUsed += msgLookup.Receipt.GasUsed
tx, err := newEthTxFromFilecoinMessageLookup(ctx, msgLookup, txIdx, cs, sa)
tx, err := newEthTxFromMessageLookup(ctx, msgLookup, txIdx, cs, sa)
if err != nil {
return ethtypes.EthBlock{}, nil
}
@ -1520,11 +1530,11 @@ func lookupEthAddress(ctx context.Context, addr address.Address, sa StateAPI) (e
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)
if err == nil {
// 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)
@ -1536,93 +1546,51 @@ func EthTxHashFromFilecoinMessageCid(ctx context.Context, c cid.Cid, sa StateAPI
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 {
ethTx, err := NewEthTxFromFilecoinMessage(ctx, smsg, sa)
ethTx, err := newEthTxFromSignedMessage(ctx, smsg, sa)
if err != nil {
return ethtypes.EmptyEthHash, err
}
return ethTx.Hash, nil
}
} else if smsg.Signature.Type == crypto.SigTypeSecp256k1 {
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) {
// Ignore errors here so we can still parse non-eth messages
fromEthAddr, _ := lookupEthAddress(ctx, smsg.Message.From, sa)
toEthAddr, _ := lookupEthAddress(ctx, smsg.Message.To, sa)
toAddr := &toEthAddr
input := smsg.Message.Params
func newEthTxFromSignedMessage(ctx context.Context, smsg *types.SignedMessage, sa StateAPI) (ethtypes.EthTx, error) {
var tx ethtypes.EthTx
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
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()
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 {
return tx, err
}
} else { // Secp Filecoin Message
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Cid())
} else { // BLS Filecoin message
tx = ethTxFromNativeMessage(ctx, smsg.VMMessage(), sa)
tx.Hash, err = ethtypes.EthHashFromCid(smsg.Message.Cid())
if err != nil {
return tx, err
}
@ -1631,14 +1599,32 @@ func NewEthTxFromFilecoinMessage(ctx context.Context, smsg *types.SignedMessage,
return tx, nil
}
// newEthTxFromFilecoinMessageLookup creates an ethereum transaction from filecoin message lookup. If a negative txIdx is passed
// into the function, it looksup the transaction index of the message in the tipset, otherwise it uses the txIdx passed into the
// function
func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLookup, txIdx int, cs *store.ChainStore, sa StateAPI) (ethtypes.EthTx, error) {
if msgLookup == nil {
return ethtypes.EthTx{}, fmt.Errorf("msg does not exist")
// ethTxFromNativeMessage does NOT populate:
// - BlockHash
// - BlockNumber
// - TransactionIndex
// - Hash
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)
if err != nil {
return ethtypes.EthTx{}, err
@ -1687,13 +1673,13 @@ func newEthTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLo
smsg = &types.SignedMessage{
Message: *msg,
Signature: crypto.Signature{
Type: crypto.SigTypeUnknown,
Type: crypto.SigTypeBLS,
Data: nil,
},
}
}
tx, err := NewEthTxFromFilecoinMessage(ctx, smsg, sa)
tx, err := newEthTxFromSignedMessage(ctx, smsg, sa)
if err != nil {
return ethtypes.EthTx{}, err
}
@ -1739,16 +1725,6 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook
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() {
receipt.Status = 1
}
@ -1756,6 +1732,24 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook
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 {
// 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
@ -1799,14 +1793,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
}
@ -1822,7 +1808,7 @@ func (m *EthTxHashManager) Apply(ctx context.Context, from, to *types.TipSet) er
continue
}
hash, err := EthTxHashFromSignedFilecoinMessage(ctx, smsg, m.StateAPI)
hash, err := EthTxHashFromSignedMessage(ctx, smsg, m.StateAPI)
if err != nil {
return err
}
@ -1859,7 +1845,7 @@ func WaitForMpoolUpdates(ctx context.Context, ch <-chan api.MpoolUpdate, manager
continue
}
ethTx, err := NewEthTxFromFilecoinMessage(ctx, u.Message, manager.StateAPI)
ethTx, err := newEthTxFromSignedMessage(ctx, u.Message, manager.StateAPI)
if err != nil {
log.Errorf("error converting filecoin message to eth tx: %s", err)
}

View File

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

View File

@ -75,7 +75,7 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
m := mux.NewRouter()
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.AliasMethod("rpc.discover", "Filecoin.Discover")
@ -94,8 +94,9 @@ func FullNodeHandler(a v1api.FullNode, permissioned bool, opts ...jsonrpc.Server
fnapi = api.PermissionedFullAPI(fnapi)
}
var v0 v0api.FullNode = &(struct{ v0api.FullNode }{&v0api.WrapperV1Full{FullNode: fnapi}})
serveRpc("/rpc/v1", fnapi)
serveRpc("/rpc/v0", &v0api.WrapperV1Full{FullNode: fnapi})
serveRpc("/rpc/v0", v0)
// Import handler
handleImportFunc := handleImport(a.(*impl.FullNodeAPI))

View File

@ -0,0 +1,12 @@
set -e
if [ -z "$(git status --porcelain)" ]; then
echo "PASSED: Working directory clean"
else
echo "FAILED: Working directory not clean."
echo "This is likely because the .dockerignore file has removed something checked into git."
echo "Add the missing files listed below to the .dockerignore to fix this issue:"
echo "$(git status)"
exit 1
fi

View File

@ -1 +0,0 @@
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0" y="0" viewBox="0 0 127 127" xml:space="preserve" enable-background="new 0 0 127 127"><style type="text/css">.st0{fill:#00d2d6}.st1{fill:#fff}</style><g><path class="st0" d="M63.5,127C28.5,127.1-0.2,98.4,0,63.2C0.2,28.3,28.6-0.2,63.9,0c34.8,0.2,63.3,28.7,63.1,64 C126.7,98.7,98.5,127.1,63.5,127z M71.4,57.6c5.5,0.8,11,1.5,16.5,2.3c0.5-1.7,0.9-3.1,1.3-4.7c-5.7-0.8-11.2-1.7-17.1-2.5 c2-7,3.7-13.7,5.8-20.2c0.7-2.2,2.3-4.2,3.9-5.9c2.1-2.2,5-1.7,6.8,0.7c0.7,1,1.4,2.1,2.3,2.9c1.1,1.1,2.8,1.6,4,0.6 c0.8-0.7,0.7-2.4,0.8-3.6c0-0.5-0.6-1.1-1-1.6c-2-2.3-4.7-3.1-7.5-3.2c-6.3-0.3-10.9,3-14.5,7.8c-3.5,4.8-5.1,10.5-6.8,16.2 c-0.5,1.6-0.9,3.3-1.4,5.1c-6.2-0.9-12.1-1.7-18.2-2.6c-0.2,1.6-0.4,3.2-0.6,4.8c6,0.9,11.8,1.8,17.8,2.7c-0.8,3.4-1.5,6.5-2.3,9.7 c-5.8-0.8-11.4-1.6-17-2.4c-0.2,1.8-0.4,3.2-0.6,4.8c5.6,0.9,11,1.7,16.5,2.5c0,0.6,0.1,1,0,1.3c-1.7,7.4-3.4,14.8-5.3,22.2 c-0.9,3.5-2.4,6.9-5.3,9.3c-2.4,2-5,1.7-6.8-0.8c-0.8-1.1-1.5-2.5-2.6-3.3c-0.8-0.6-2.5-0.9-3.1-0.5c-0.9,0.7-1.5,2.2-1.4,3.3 c0.1,1,1,2.2,1.9,2.8c3,2.3,6.5,2.6,10,1.9c5.9-1.2,10.1-4.9,12.7-10.1c2-4.1,3.6-8.5,5-12.9c1.3-4,2.2-8,3.3-12.2 c5.8,0.8,11.5,1.7,17.3,2.5c0.5-1.7,0.9-3.2,1.4-4.8c-6.1-0.9-11.9-1.7-17.7-2.6C70.1,64,70.7,60.9,71.4,57.6z"/><path class="st1" d="M71.4,57.6c-0.7,3.3-1.3,6.4-2,9.8c5.9,0.9,11.7,1.7,17.7,2.6c-0.5,1.6-0.9,3.1-1.4,4.8 c-5.8-0.8-11.5-1.7-17.3-2.5c-1.1,4.2-2,8.3-3.3,12.2c-1.4,4.4-3,8.7-5,12.9c-2.6,5.2-6.8,8.9-12.7,10.1c-3.5,0.7-7,0.4-10-1.9 c-0.9-0.7-1.8-1.8-1.9-2.8c-0.1-1.1,0.5-2.7,1.4-3.3c0.6-0.5,2.3-0.1,3.1,0.5c1.1,0.8,1.8,2.1,2.6,3.3c1.8,2.5,4.4,2.9,6.8,0.8 c2.9-2.5,4.4-5.8,5.3-9.3c1.9-7.3,3.6-14.8,5.3-22.2c0.1-0.3,0-0.7,0-1.3c-5.4-0.8-10.8-1.7-16.5-2.5c0.2-1.6,0.4-3,0.6-4.8 c5.6,0.8,11.1,1.6,17,2.4c0.8-3.2,1.5-6.4,2.3-9.7c-6-0.9-11.7-1.8-17.8-2.7c0.2-1.6,0.4-3.2,0.6-4.8c6.1,0.9,12,1.7,18.2,2.6 c0.5-1.8,0.9-3.5,1.4-5.1c1.7-5.6,3.2-11.3,6.8-16.2c3.6-4.9,8.1-8.1,14.5-7.8c2.8,0.1,5.5,0.9,7.5,3.2c0.4,0.5,1,1.1,1,1.6 c-0.1,1.2,0,2.9-0.8,3.6c-1.1,1.1-2.8,0.5-4-0.6c-0.9-0.9-1.6-1.9-2.3-2.9c-1.8-2.4-4.7-2.9-6.8-0.7c-1.6,1.7-3.2,3.7-3.9,5.9 C75.7,39.4,74,46,72,53c5.9,0.9,11.4,1.7,17.1,2.5c-0.5,1.6-0.9,3.1-1.3,4.7C82.3,59.1,76.9,58.3,71.4,57.6z"/></g></svg>

Before

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -1,96 +0,0 @@
name: lotus-filecoin
base: core20
version: latest
summary: filecoin daemon/client
icon: snap/local/icon.svg
description: |
Filecoin is a peer-to-peer network that stores files on the internet
with built-in economic incentives to ensure files are stored reliably over time
For documentation and additional information, please see the following resources
https://filecoin.io
https://fil.org
https://lotus.filecoin.io
https://github.com/filecoin-project/lotus
confinement: strict
parts:
lotus:
plugin: make
source: ./
build-snaps:
- go
- rustup
build-packages:
- git
- jq
- libhwloc-dev
- ocl-icd-opencl-dev
- pkg-config
stage-packages:
- libhwloc15
- ocl-icd-libopencl1
override-build: |
LDFLAGS="" make lotus lotus-miner lotus-worker
cp lotus lotus-miner lotus-worker $SNAPCRAFT_PART_INSTALL
cp scripts/snap-lotus-entrypoint.sh $SNAPCRAFT_PART_INSTALL
layout:
/var/lib/lotus:
symlink: $SNAP_COMMON/lotus
/var/lib/lotus-miner:
symlink: $SNAP_COMMON/lotus-miner
/var/lib/lotus-worker:
symlink: $SNAP_COMMON/lotus-worker
apps:
lotus:
command: lotus
plugs:
- network
- network-bind
- home
environment:
FIL_PROOFS_PARAMETER_CACHE: $SNAP_USER_COMMON/filecoin-proof-parameters
LOTUS_PATH: $SNAP_COMMON/lotus
LOTUS_MINER_PATH: $SNAP_COMMON/lotus-miner
LOTUS_WORKER_PATH: $SNAP_COMMON/lotus-worker
lotus-miner:
command: lotus-miner
plugs:
- network
- network-bind
- opengl
environment:
FIL_PROOFS_PARAMETER_CACHE: $SNAP_USER_COMMON/filecoin-proof-parameters
LOTUS_PATH: $SNAP_COMMON/lotus
LOTUS_MINER_PATH: $SNAP_COMMON/lotus-miner
LOTUS_WORKER_PATH: $SNAP_COMMON/lotus-worker
lotus-worker:
command: lotus-worker
plugs:
- network
- network-bind
- opengl
environment:
FIL_PROOFS_PARAMETER_CACHE: $SNAP_USER_COMMON/filecoin-proof-parameters
LOTUS_PATH: $SNAP_COMMON/lotus
LOTUS_MINER_PATH: $SNAP_COMMON/lotus-miner
LOTUS_WORKER_PATH: $SNAP_COMMON/lotus-worker
lotus-daemon:
command: snap-lotus-entrypoint.sh
daemon: simple
install-mode: disable
plugs:
- network
- network-bind
environment:
FIL_PROOFS_PARAMETER_CACHE: $SNAP_COMMON/filecoin-proof-parameters
LOTUS_PATH: $SNAP_COMMON/lotus
LOTUS_MINER_PATH: $SNAP_COMMON/lotus-miner
LOTUS_WORKER_PATH: $SNAP_COMMON/lotus-worker