diff --git a/.circleci/config.yml b/.circleci/config.yml index 42015f00e..e3e7330a5 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -603,6 +603,11 @@ workflows: suite: itest-deals target: "./itests/deals_test.go" + - test: + name: test-itest-decode_params + suite: itest-decode_params + target: "./itests/decode_params_test.go" + - test: name: test-itest-dup_mpool_messages suite: itest-dup_mpool_messages @@ -623,11 +628,31 @@ workflows: suite: itest-eth_block_hash target: "./itests/eth_block_hash_test.go" + - test: + name: test-itest-eth_bytecode + suite: itest-eth_bytecode + target: "./itests/eth_bytecode_test.go" + + - test: + name: test-itest-eth_config + suite: itest-eth_config + target: "./itests/eth_config_test.go" + + - test: + name: test-itest-eth_conformance + suite: itest-eth_conformance + target: "./itests/eth_conformance_test.go" + - test: name: test-itest-eth_deploy suite: itest-eth_deploy target: "./itests/eth_deploy_test.go" + - test: + name: test-itest-eth_fee_history + suite: itest-eth_fee_history + target: "./itests/eth_fee_history_test.go" + - test: name: test-itest-eth_filter suite: itest-eth_filter @@ -703,6 +728,11 @@ workflows: suite: itest-migration_nv19 target: "./itests/migration_nv19_test.go" + - test: + name: test-itest-migration_nv20 + suite: itest-migration_nv20 + target: "./itests/migration_nv20_test.go" + - test: name: test-itest-mpool_msg_uuid suite: itest-mpool_msg_uuid @@ -902,11 +932,6 @@ workflows: - test-conformance: suite: conformance target: "./conformance" - - test-conformance: - name: test-conformance-bleeding-edge - suite: conformance-bleeding-edge - target: "./conformance" - vectors-branch: specs-actors-v7 release: jobs: diff --git a/.circleci/template.yml b/.circleci/template.yml index 7bef6ae41..724571ac2 100644 --- a/.circleci/template.yml +++ b/.circleci/template.yml @@ -527,11 +527,6 @@ workflows: - test-conformance: suite: conformance target: "./conformance" - - test-conformance: - name: test-conformance-bleeding-edge - suite: conformance-bleeding-edge - target: "./conformance" - vectors-branch: specs-actors-v7 release: jobs: diff --git a/Makefile b/Makefile index dedc061b5..81c7b222d 100644 --- a/Makefile +++ b/Makefile @@ -301,7 +301,7 @@ actors-gen: actors-code-gen fiximports .PHONY: actors-gen bundle-gen: - $(GOCC) run ./gen/bundle + $(GOCC) run ./gen/bundle $(RELEASE) $(GOCC) fmt ./build/... .PHONY: bundle-gen @@ -357,7 +357,7 @@ docsgen-openrpc-gateway: docsgen-openrpc-bin fiximports: ./scripts/fiximports -gen: actors-code-gen type-gen cfgdoc-gen docsgen api-gen circleci bundle-gen fiximports +gen: actors-code-gen type-gen cfgdoc-gen docsgen api-gen circleci fiximports @echo ">>> IF YOU'VE MODIFIED THE CLI OR CONFIG, REMEMBER TO ALSO MAKE docsgen-cli" .PHONY: gen diff --git a/api/api_full.go b/api/api_full.go index 2ac92a764..3bd875dc7 100644 --- a/api/api_full.go +++ b/api/api_full.go @@ -786,15 +786,15 @@ type FullNode interface { EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) //perm:read EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum ethtypes.EthUint64, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) //perm:read - EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) //perm:read - EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) //perm:read - EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) //perm:read - EthChainId(ctx context.Context) (ethtypes.EthUint64, error) //perm:read - NetVersion(ctx context.Context) (string, error) //perm:read - NetListening(ctx context.Context) (bool, error) //perm:read - EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) //perm:read - EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read - EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) //perm:read + EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) //perm:read + EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) //perm:read + EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) //perm:read + EthChainId(ctx context.Context) (ethtypes.EthUint64, error) //perm:read + NetVersion(ctx context.Context) (string, error) //perm:read + NetListening(ctx context.Context) (bool, error) //perm:read + EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) //perm:read + EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read + EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) //perm:read EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) //perm:read EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) //perm:read @@ -853,7 +853,7 @@ type FullNode interface { // 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 + EthSubscription(ctx context.Context, r jsonrpc.RawParams) error // rpc_method:eth_subscription notify:true } type StorageAsk struct { diff --git a/api/api_gateway.go b/api/api_gateway.go index 2c91fc6f5..aea6fc3d1 100644 --- a/api/api_gateway.go +++ b/api/api_gateway.go @@ -79,6 +79,8 @@ type Gateway interface { EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error) EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) + EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) + EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*EthTxReceipt, error) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) @@ -91,7 +93,7 @@ type Gateway interface { NetListening(ctx context.Context) (bool, error) EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) - EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) + EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) @@ -105,4 +107,5 @@ type Gateway interface { EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) + Web3ClientVersion(ctx context.Context) (string, error) } diff --git a/api/mocks/mock_full.go b/api/mocks/mock_full.go index c944e2de1..6e4873715 100644 --- a/api/mocks/mock_full.go +++ b/api/mocks/mock_full.go @@ -1014,18 +1014,18 @@ func (mr *MockFullNodeMockRecorder) EthEstimateGas(arg0, arg1 interface{}) *gomo } // EthFeeHistory mocks base method. -func (m *MockFullNode) EthFeeHistory(arg0 context.Context, arg1 ethtypes.EthUint64, arg2 string, arg3 []float64) (ethtypes.EthFeeHistory, error) { +func (m *MockFullNode) EthFeeHistory(arg0 context.Context, arg1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "EthFeeHistory", arg0, arg1, arg2, arg3) + ret := m.ctrl.Call(m, "EthFeeHistory", arg0, arg1) ret0, _ := ret[0].(ethtypes.EthFeeHistory) ret1, _ := ret[1].(error) return ret0, ret1 } // EthFeeHistory indicates an expected call of EthFeeHistory. -func (mr *MockFullNodeMockRecorder) EthFeeHistory(arg0, arg1, arg2, arg3 interface{}) *gomock.Call { +func (mr *MockFullNodeMockRecorder) EthFeeHistory(arg0, arg1 interface{}) *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthFeeHistory", reflect.TypeOf((*MockFullNode)(nil).EthFeeHistory), arg0, arg1, arg2, arg3) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthFeeHistory", reflect.TypeOf((*MockFullNode)(nil).EthFeeHistory), arg0, arg1) } // EthGasPrice mocks base method. diff --git a/api/proxy_gen.go b/api/proxy_gen.go index 7a6b0d508..2cfaa099a 100644 --- a/api/proxy_gen.go +++ b/api/proxy_gen.go @@ -252,7 +252,7 @@ type FullNodeMethods struct { EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) `perm:"read"` - EthFeeHistory func(p0 context.Context, p1 ethtypes.EthUint64, p2 string, p3 []float64) (ethtypes.EthFeeHistory, error) `perm:"read"` + EthFeeHistory func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) `perm:"read"` EthGasPrice func(p0 context.Context) (ethtypes.EthBigInt, error) `perm:"read"` @@ -658,7 +658,7 @@ type GatewayMethods struct { EthEstimateGas func(p0 context.Context, p1 ethtypes.EthCall) (ethtypes.EthUint64, error) `` - EthFeeHistory func(p0 context.Context, p1 ethtypes.EthUint64, p2 string, p3 []float64) (ethtypes.EthFeeHistory, error) `` + EthFeeHistory func(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) `` EthGasPrice func(p0 context.Context) (ethtypes.EthBigInt, error) `` @@ -680,6 +680,8 @@ type GatewayMethods struct { EthGetLogs func(p0 context.Context, p1 *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) `` + EthGetMessageCidByTransactionHash func(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) `` + EthGetStorageAt func(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) `` EthGetTransactionByBlockHashAndIndex func(p0 context.Context, p1 ethtypes.EthHash, p2 ethtypes.EthUint64) (ethtypes.EthTx, error) `` @@ -690,6 +692,8 @@ type GatewayMethods struct { EthGetTransactionCount func(p0 context.Context, p1 ethtypes.EthAddress, p2 string) (ethtypes.EthUint64, error) `` + EthGetTransactionHashByCid func(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) `` + EthGetTransactionReceipt func(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) `` EthMaxPriorityFeePerGas func(p0 context.Context) (ethtypes.EthBigInt, error) `` @@ -761,6 +765,8 @@ type GatewayMethods struct { Version func(p0 context.Context) (APIVersion, error) `` WalletBalance func(p0 context.Context, p1 address.Address) (types.BigInt, error) `` + + Web3ClientVersion func(p0 context.Context) (string, error) `` } type GatewayStub struct { @@ -2045,14 +2051,14 @@ func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall) ( return *new(ethtypes.EthUint64), ErrNotSupported } -func (s *FullNodeStruct) EthFeeHistory(p0 context.Context, p1 ethtypes.EthUint64, p2 string, p3 []float64) (ethtypes.EthFeeHistory, error) { +func (s *FullNodeStruct) EthFeeHistory(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) { if s.Internal.EthFeeHistory == nil { return *new(ethtypes.EthFeeHistory), ErrNotSupported } - return s.Internal.EthFeeHistory(p0, p1, p2, p3) + return s.Internal.EthFeeHistory(p0, p1) } -func (s *FullNodeStub) EthFeeHistory(p0 context.Context, p1 ethtypes.EthUint64, p2 string, p3 []float64) (ethtypes.EthFeeHistory, error) { +func (s *FullNodeStub) EthFeeHistory(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) { return *new(ethtypes.EthFeeHistory), ErrNotSupported } @@ -4212,14 +4218,14 @@ func (s *GatewayStub) EthEstimateGas(p0 context.Context, p1 ethtypes.EthCall) (e return *new(ethtypes.EthUint64), ErrNotSupported } -func (s *GatewayStruct) EthFeeHistory(p0 context.Context, p1 ethtypes.EthUint64, p2 string, p3 []float64) (ethtypes.EthFeeHistory, error) { +func (s *GatewayStruct) EthFeeHistory(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) { if s.Internal.EthFeeHistory == nil { return *new(ethtypes.EthFeeHistory), ErrNotSupported } - return s.Internal.EthFeeHistory(p0, p1, p2, p3) + return s.Internal.EthFeeHistory(p0, p1) } -func (s *GatewayStub) EthFeeHistory(p0 context.Context, p1 ethtypes.EthUint64, p2 string, p3 []float64) (ethtypes.EthFeeHistory, error) { +func (s *GatewayStub) EthFeeHistory(p0 context.Context, p1 jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) { return *new(ethtypes.EthFeeHistory), ErrNotSupported } @@ -4333,6 +4339,17 @@ func (s *GatewayStub) EthGetLogs(p0 context.Context, p1 *ethtypes.EthFilterSpec) return nil, ErrNotSupported } +func (s *GatewayStruct) EthGetMessageCidByTransactionHash(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) { + if s.Internal.EthGetMessageCidByTransactionHash == nil { + return nil, ErrNotSupported + } + return s.Internal.EthGetMessageCidByTransactionHash(p0, p1) +} + +func (s *GatewayStub) EthGetMessageCidByTransactionHash(p0 context.Context, p1 *ethtypes.EthHash) (*cid.Cid, error) { + return nil, ErrNotSupported +} + func (s *GatewayStruct) EthGetStorageAt(p0 context.Context, p1 ethtypes.EthAddress, p2 ethtypes.EthBytes, p3 string) (ethtypes.EthBytes, error) { if s.Internal.EthGetStorageAt == nil { return *new(ethtypes.EthBytes), ErrNotSupported @@ -4388,6 +4405,17 @@ func (s *GatewayStub) EthGetTransactionCount(p0 context.Context, p1 ethtypes.Eth return *new(ethtypes.EthUint64), ErrNotSupported } +func (s *GatewayStruct) EthGetTransactionHashByCid(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) { + if s.Internal.EthGetTransactionHashByCid == nil { + return nil, ErrNotSupported + } + return s.Internal.EthGetTransactionHashByCid(p0, p1) +} + +func (s *GatewayStub) EthGetTransactionHashByCid(p0 context.Context, p1 cid.Cid) (*ethtypes.EthHash, error) { + return nil, ErrNotSupported +} + func (s *GatewayStruct) EthGetTransactionReceipt(p0 context.Context, p1 ethtypes.EthHash) (*EthTxReceipt, error) { if s.Internal.EthGetTransactionReceipt == nil { return nil, ErrNotSupported @@ -4784,6 +4812,17 @@ func (s *GatewayStub) WalletBalance(p0 context.Context, p1 address.Address) (typ return *new(types.BigInt), ErrNotSupported } +func (s *GatewayStruct) Web3ClientVersion(p0 context.Context) (string, error) { + if s.Internal.Web3ClientVersion == nil { + return "", ErrNotSupported + } + return s.Internal.Web3ClientVersion(p0) +} + +func (s *GatewayStub) Web3ClientVersion(p0 context.Context) (string, error) { + return "", ErrNotSupported +} + func (s *NetStruct) ID(p0 context.Context) (peer.ID, error) { if s.Internal.ID == nil { return *new(peer.ID), ErrNotSupported diff --git a/build/actors/pack.sh b/build/actors/pack.sh index 2702c80d5..0b529650e 100755 --- a/build/actors/pack.sh +++ b/build/actors/pack.sh @@ -52,4 +52,4 @@ popd echo "Generating metadata..." -make -C ../../ bundle-gen +make -C ../../ RELEASE="$RELEASE" bundle-gen diff --git a/build/actors/v12.tar.zst b/build/actors/v12.tar.zst new file mode 100644 index 000000000..587c24ea9 Binary files /dev/null and b/build/actors/v12.tar.zst differ diff --git a/build/bootstrap/butterflynet.pi b/build/bootstrap/butterflynet.pi index 556b5d14f..1def01c7c 100644 --- a/build/bootstrap/butterflynet.pi +++ b/build/bootstrap/butterflynet.pi @@ -1,2 +1,2 @@ -/dns4/bootstrap-0.butterfly.fildev.network/tcp/1347/p2p/12D3KooWKeDMuJbouvypr1nL2qRruhNVXzv4QiLsZRh6gnvLkc7p -/dns4/bootstrap-1.butterfly.fildev.network/tcp/1347/p2p/12D3KooWSsACNHLGoJbPqeitNY7tom19Nxq8x5ag36eTwmgcAeLo +/dns4/bootstrap-0.butterfly.fildev.network/tcp/1347/p2p/12D3KooWHkVVMJ1rfVLM5poNrgwTJiaDkpDLkPqQ9zVuNPQ7AJ6p +/dns4/bootstrap-1.butterfly.fildev.network/tcp/1347/p2p/12D3KooWRyzqeQd51HCvVK3nvegmnBsYYPLSZbxR3Q9XAoUrUZ18 diff --git a/build/builtin_actors.go b/build/builtin_actors.go index 4d283919e..50aecde40 100644 --- a/build/builtin_actors.go +++ b/build/builtin_actors.go @@ -95,10 +95,11 @@ func loadManifests(netw string) error { } type BuiltinActorsMetadata struct { - Network string - Version actorstypes.Version - ManifestCid cid.Cid - Actors map[string]cid.Cid + Network string + Version actorstypes.Version + ManifestCid cid.Cid + Actors map[string]cid.Cid + BundleGitTag string } // ReadEmbeddedBuiltinActorsMetadata reads the metadata from the embedded built-in actor bundles. diff --git a/build/builtin_actors_gen.go b/build/builtin_actors_gen.go index 4beb8ff68..a63a10c93 100644 --- a/build/builtin_actors_gen.go +++ b/build/builtin_actors_gen.go @@ -7,8 +7,9 @@ import ( ) var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMetadata{{ - Network: "butterflynet", - Version: 8, + Network: "butterflynet", + Version: 8, + ManifestCid: MustParseCid("bafy2bzaceba5qgs4z3imhlxwds5vamahngatvuuglbv5yl3ftfiosj6ud5chs"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacebd5zetyjtragjwrv2nqktct6u2pmsi4eifbanovxohx3a7lszjxi"), @@ -29,8 +30,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacebu4joy25gneu2qv3qfm3ktakzalndjrbhekeqrqk3zhotv6nyy2g"), }, }, { - Network: "butterflynet", - Version: 9, + Network: "butterflynet", + Version: 9, + ManifestCid: MustParseCid("bafy2bzaceba5qgs4z3imhlxwds5vamahngatvuuglbv5yl3ftfiosj6ud5chs"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacebd5zetyjtragjwrv2nqktct6u2pmsi4eifbanovxohx3a7lszjxi"), @@ -51,8 +53,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacebu4joy25gneu2qv3qfm3ktakzalndjrbhekeqrqk3zhotv6nyy2g"), }, }, { - Network: "butterflynet", - Version: 10, + Network: "butterflynet", + Version: 10, + ManifestCid: MustParseCid("bafy2bzacecjs7xvhtejsh47b2tx2iwe7mbad4kxovbfs7a6wxfl47kcnl25bm"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacebd5zetyjtragjwrv2nqktct6u2pmsi4eifbanovxohx3a7lszjxi"), @@ -73,8 +76,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacebu4joy25gneu2qv3qfm3ktakzalndjrbhekeqrqk3zhotv6nyy2g"), }, }, { - Network: "butterflynet", - Version: 11, + Network: "butterflynet", + Version: 11, + ManifestCid: MustParseCid("bafy2bzaced2wq4k4i2deknam6ehbynaoo37bhysud7eze7su3ftlaggwwjuje"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacebd5zetyjtragjwrv2nqktct6u2pmsi4eifbanovxohx3a7lszjxi"), @@ -95,8 +99,32 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacebu4joy25gneu2qv3qfm3ktakzalndjrbhekeqrqk3zhotv6nyy2g"), }, }, { - Network: "calibrationnet", - Version: 8, + Network: "butterflynet", + Version: 12, + + ManifestCid: MustParseCid("bafy2bzacec4tgdtsrgbdywc5nzf5ekiw5zuefrasiahb4n5yqwcrwjzcdp4nk"), + Actors: map[string]cid.Cid{ + "account": MustParseCid("bafk2bzaceae6holtld4caox2xood5rpcjotrxj7lnfxvfmhivb3s2ddyj22qw"), + "cron": MustParseCid("bafk2bzaceab2vrkun6ps3hactaumfzgm6zk4sdasiqiedxzdttkvw64hndcdg"), + "datacap": MustParseCid("bafk2bzacecixx45mf6chwktsd2g5krlr35p3g7pkkrvzjeujz4ryhlztpl7fq"), + "eam": MustParseCid("bafk2bzaceciekaxrlgnmosmthbgptpdu2bzdoo7mt67p7cqbdvnxup6xpd6ns"), + "ethaccount": MustParseCid("bafk2bzacecb4ttgbjzkaqrg7phqgao2kxsgeet4wnrr5qmftlfad26v6jpk4o"), + "evm": MustParseCid("bafk2bzacecrwejr7bedjww67ppof3abb6df66w76r7wpuzc426arc4oibndeu"), + "init": MustParseCid("bafk2bzacea5ke6q7je2ofrai7dpw67vat463d5f74g4evvwtcu7dhp4ff6ztk"), + "multisig": MustParseCid("bafk2bzacecs6aws25bvqmyzny3vcilr2xw35jymryu4yzodg7l7gf4bhjpolw"), + "paymentchannel": MustParseCid("bafk2bzacedtewkfsicz2rm4hsjsbagrl2mhmqfdikpsq3ggoct5iqa6caka6a"), + "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"), + "reward": MustParseCid("bafk2bzacecqmh47zzzlzbtaueaz2fvhiqnktccfrcqicul4j6tca2bruvtn44"), + "storagemarket": MustParseCid("bafk2bzacecbvqe4k6jyvwovxfnkylj3zpb2vjxkc3ar53x7c2pe5mkokxuyk6"), + "storageminer": MustParseCid("bafk2bzaceczbh2aofwcif4aqycydmnsjkkww4i4yfl4zca5j2dqopbz46dvrg"), + "storagepower": MustParseCid("bafk2bzacealioyiirrvov5rnh63omtsifppcsgba7my2tp7bslhd454wczepy"), + "system": MustParseCid("bafk2bzaceax2qvj3ap2dxvzgjps2vtmfgfrej3hdgk7a5euqdgsmak7ptalaa"), + "verifiedregistry": MustParseCid("bafk2bzaceaeqg3nqpjrgklq6nli6hz73s76hp4bwn6jsa64y22dj3csvmcl32"), + }, +}, { + Network: "calibrationnet", + Version: 8, + ManifestCid: MustParseCid("bafy2bzacedrdn6z3z7xz7lx4wll3tlgktirhllzqxb766dxpaqp3ukxsjfsba"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacecruossn66xqbeutqx5r4k2kjzgd43frmwd4qkw6haez44ubvvpxo"), @@ -112,8 +140,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzaceaihibfu625lbtzdp3tcftscshrmbgghgrc7kzqhxn4455pycpdkm"), }, }, { - Network: "calibrationnet", - Version: 9, + Network: "calibrationnet", + Version: 9, + ManifestCid: MustParseCid("bafy2bzacedbedgynklc4dgpyxippkxmba2mgtw7ecntoneclsvvl4klqwuyyy"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzaceavfgpiw6whqigmskk74z4blm22nwjfnzxb4unlqz2e4wg3c5ujpw"), @@ -130,8 +159,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacebh7dj6j7yi5vadh7lgqjtq42qi2uq4n6zy2g5vjeathacwn2tscu"), }, }, { - Network: "calibrationnet", - Version: 10, + Network: "calibrationnet", + Version: 10, + ManifestCid: MustParseCid("bafy2bzaceaklxgrzd34i53rm4eeq6477nlq2ckpex27evftbsmjd2yrdbj4ba"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacea7zmrdz2rjbzlbmrmx3ko6pm3cbyqxxgogiqldsccbqffuok7m6s"), @@ -152,8 +182,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzaceawecz24xbz7robn7ck7k2mprkewvup6q346whbfiybcrvy63qcsa"), }, }, { - Network: "calibrationnet", - Version: 11, + Network: "calibrationnet", + Version: 11, + ManifestCid: MustParseCid("bafy2bzacearpwvmcqlailxyq2d2wtzmtudxqhvfot77tbdqotek5qiq5hyhzg"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacea7zmrdz2rjbzlbmrmx3ko6pm3cbyqxxgogiqldsccbqffuok7m6s"), @@ -174,8 +205,32 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzaceawecz24xbz7robn7ck7k2mprkewvup6q346whbfiybcrvy63qcsa"), }, }, { - Network: "caterpillarnet", - Version: 8, + Network: "calibrationnet", + Version: 12, + + ManifestCid: MustParseCid("bafy2bzaced25ta3j6ygs34roprilbtb3f6mxifyfnm7z7ndquaruxzdq3y7lo"), + Actors: map[string]cid.Cid{ + "account": MustParseCid("bafk2bzacebhfuz3sv7duvk653544xsxhdn4lsmy7ol7k6gdgancyctvmd7lnq"), + "cron": MustParseCid("bafk2bzacecw2yjb6ysieffa7lk7xd32b3n4ssowvafolt7eq52lp6lk4lkhji"), + "datacap": MustParseCid("bafk2bzaceaot6tv6p4cat3cg5fknq22htosw3p5rwyijmdsraatwqyc4qyero"), + "eam": MustParseCid("bafk2bzacec5untyj6cefdsfm47wckozw6wt6svqqh5dzh63nu4f6dvf26fkco"), + "ethaccount": MustParseCid("bafk2bzacebiyrhz32xwxi6xql67aaq5nrzeelzas472kuwjqmdmgwotpkj35e"), + "evm": MustParseCid("bafk2bzaceblpgzid4qjfavuiht6uwvq2lznshklk2qmf5akm3dzx2fczdqdxc"), + "init": MustParseCid("bafk2bzacedhxbcglnonzruxf2jpczara73eh735wf2kznatx2u4gsuhgqwffq"), + "multisig": MustParseCid("bafk2bzacebv5gdlte2pyovmz6s37me6x2rixaa6a33w6lgqdohmycl23snvwm"), + "paymentchannel": MustParseCid("bafk2bzacea7ngq44gedftjlar3j3ql3dmd7e7xkkb6squgxinfncybfmppmlc"), + "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"), + "reward": MustParseCid("bafk2bzacea3yo22x4dsh4axioshrdp42eoeugef3tqtmtwz5untyvth7uc73o"), + "storagemarket": MustParseCid("bafk2bzacecclsfboql3iraf3e66pzuh3h7qp3vgmfurqz26qh5g5nrexjgknc"), + "storageminer": MustParseCid("bafk2bzacedu4chbl36rilas45py4vhqtuj6o7aa5stlvnwef3kshgwcsmha6y"), + "storagepower": MustParseCid("bafk2bzacedu3c67spbf2dmwo77ymkjel6i2o5gpzyksgu2iuwu2xvcnxgfdjg"), + "system": MustParseCid("bafk2bzacea4mtukm5zazygkdbgdf26cpnwwif5n2no7s6tknpxlwy6fpq3mug"), + "verifiedregistry": MustParseCid("bafk2bzacec67wuchq64k7kgrujguukjvdlsl24pgighqdx5vgjhyk6bycrwnc"), + }, +}, { + Network: "caterpillarnet", + Version: 8, + ManifestCid: MustParseCid("bafy2bzacebsdvrxmdajiyxq2mxxxppvg2zwvqjzz3pgbsxwh6pvdcjofpmnxw"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacedfms6w3ghqtljpgsfuiqa6ztjx7kcuin6myjezj6rypj3zjbqms6"), @@ -196,8 +251,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacebzndvdqtdck2y35smcxezldgh6nm6rbkj3g3fmiknsgg2uah235y"), }, }, { - Network: "caterpillarnet", - Version: 9, + Network: "caterpillarnet", + Version: 9, + ManifestCid: MustParseCid("bafy2bzacebsdvrxmdajiyxq2mxxxppvg2zwvqjzz3pgbsxwh6pvdcjofpmnxw"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacedfms6w3ghqtljpgsfuiqa6ztjx7kcuin6myjezj6rypj3zjbqms6"), @@ -218,8 +274,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacebzndvdqtdck2y35smcxezldgh6nm6rbkj3g3fmiknsgg2uah235y"), }, }, { - Network: "caterpillarnet", - Version: 10, + Network: "caterpillarnet", + Version: 10, + ManifestCid: MustParseCid("bafy2bzaceawh5opc4uqctlzc6xnq3pb7ycchfqwprjysbfa5xlrmiicbbvkrm"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacedfms6w3ghqtljpgsfuiqa6ztjx7kcuin6myjezj6rypj3zjbqms6"), @@ -240,8 +297,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacebzndvdqtdck2y35smcxezldgh6nm6rbkj3g3fmiknsgg2uah235y"), }, }, { - Network: "caterpillarnet", - Version: 11, + Network: "caterpillarnet", + Version: 11, + ManifestCid: MustParseCid("bafy2bzacebxr4uvnf5g3373shjzbaca6pf4th6nnfubytjfbrlxcpvbjw4ane"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacedfms6w3ghqtljpgsfuiqa6ztjx7kcuin6myjezj6rypj3zjbqms6"), @@ -262,8 +320,32 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacebzndvdqtdck2y35smcxezldgh6nm6rbkj3g3fmiknsgg2uah235y"), }, }, { - Network: "devnet", - Version: 8, + Network: "caterpillarnet", + Version: 12, + + ManifestCid: MustParseCid("bafy2bzacedn6med544h6r7frzvyq5cvd7dqgnwgpmzgf42d4agoysx6tf475i"), + Actors: map[string]cid.Cid{ + "account": MustParseCid("bafk2bzaceaysjus6ldo45qnedaopwctamtvvk4ga2l4dh7dmdtltsfin4puuk"), + "cron": MustParseCid("bafk2bzaceatq2ltzs2p37nniek3qpkitzlfd7iu2qtupzjtgvtggxglrhj7ay"), + "datacap": MustParseCid("bafk2bzacecgwe56okusbtsfimiwstxe7ova25uvrkj4osk3w5wl2qclvd64bi"), + "eam": MustParseCid("bafk2bzaceak3xbmmyj5glnm65kv5p7zc6u7x2xallwpz7aphqqouadn3jvumm"), + "ethaccount": MustParseCid("bafk2bzacec5bcn2i4ktsc54wx7gf4gwdd6xjcukh43szfya47jicxlwrhfrhk"), + "evm": MustParseCid("bafk2bzacebbc2iv4tw2kfmmcm7k3uxilpqvbjg6jsqbov4n7wqcxvvmtazfbc"), + "init": MustParseCid("bafk2bzacecvlqa2szdyem4gwgks2yk7bfernmzbxgo5felnpqgikyesdyiury"), + "multisig": MustParseCid("bafk2bzacebvqjxop2ald5f5hvu7qqb7ali7iluxcbdd3ssllawco6kafjteqw"), + "paymentchannel": MustParseCid("bafk2bzacebux5gmkddtur2kfebwzrxwqyqh2almlt4bw3v7f4bg3gy2zqjhbe"), + "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"), + "reward": MustParseCid("bafk2bzacebhm5w7q7wlxrwgbldolzq5qfcdmcu66j6ty37ppvpwafxzlizxn4"), + "storagemarket": MustParseCid("bafk2bzacebgniq7x22ep4oiithvlzepxo5jsvhuang2vqfz3prozihmviw5ey"), + "storageminer": MustParseCid("bafk2bzacecchnrfi4cgbpis4aagiqkhalgqudjlu25da7rer4ltlgpojersgm"), + "storagepower": MustParseCid("bafk2bzaceaxomr5xbhacbbn5ib6unekjz6igoqr4r4a2taxqcpyb4gpwi6zvk"), + "system": MustParseCid("bafk2bzacebmymcxtwkk6dzgtmkbradncjpsxdqkcwtemhio5acvpm742rguv6"), + "verifiedregistry": MustParseCid("bafk2bzaceae5zbc2h6gpyu6uzsvelvww53p5mujq2dvs3zi74w6mvnigwc3va"), + }, +}, { + Network: "devnet", + Version: 8, + ManifestCid: MustParseCid("bafy2bzacedq7tuibavyqxzkq4uybjj7ly22eu42mjkoehwn5d47xfunmtjm4k"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacea4tlgnp7m6tlldpz3termlwxlnyq24nwd4zdzv4r6nsjuaktuuzc"), @@ -279,8 +361,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzaceaajgtglewgitshgdi2nzrvq7eihjtyqj5yiamesqun2hujl3xev2"), }, }, { - Network: "devnet", - Version: 9, + Network: "devnet", + Version: 9, + ManifestCid: MustParseCid("bafy2bzacedozk3jh2j4nobqotkbofodq4chbrabioxbfrygpldgoxs3zwgggk"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzaced5llqnqqhypolyuogz3h2wjomugqkrhyhocvly3aoib4c5xiush6"), @@ -297,8 +380,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacednorhcy446agy7ecpmfms2u4aoa3mj2eqomffuoerbik5yavrxyi"), }, }, { - Network: "devnet", - Version: 10, + Network: "devnet", + Version: 10, + ManifestCid: MustParseCid("bafy2bzacedfwwsn5weycwkqrnusc37m6ut2uf42z5qvbukl67wi76mqtgafw2"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacebb5txxkfexeaxa2th3rckxsxchzyss3ijgqbicf265h7rre2rvhm"), @@ -319,8 +403,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzaceb7odugx7meltvt2gra4vogn2g6avbgysivvdccldylusjcfsnfhy"), }, }, { - Network: "devnet", - Version: 11, + Network: "devnet", + Version: 11, + ManifestCid: MustParseCid("bafy2bzacebixrjysarwxdadewlllfp4rwfoejxstwdutghghei54uvuuxlsbq"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacebb5txxkfexeaxa2th3rckxsxchzyss3ijgqbicf265h7rre2rvhm"), @@ -341,8 +426,32 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzaceb7odugx7meltvt2gra4vogn2g6avbgysivvdccldylusjcfsnfhy"), }, }, { - Network: "hyperspace", - Version: 8, + Network: "devnet", + Version: 12, + + ManifestCid: MustParseCid("bafy2bzaceco52tjbexpijsegolhdkhlziflum4bl27hcjrpnny273t3lsa6tc"), + Actors: map[string]cid.Cid{ + "account": MustParseCid("bafk2bzacec6mdvynyu4cxb53tcdbkcu7w7jzxduxqqadi2y2rx2tsm42627tk"), + "cron": MustParseCid("bafk2bzaceaiscwdpdzdooja7hwz56ee3zztjogl4gljkccpefi7uliqtaq2ek"), + "datacap": MustParseCid("bafk2bzacedmfwrwwqmwxtfpvhp33mvt5btq2ewbjfrvfetwnrxt5r37i3ymzk"), + "eam": MustParseCid("bafk2bzaced2lwwrqtkgwxdgadpdbxpxjoh2gsunjg3bgtoubs47p7fbktbu26"), + "ethaccount": MustParseCid("bafk2bzacea5zob56gdcxudkubhol4gijzgwgtaaix4pmv3vpg4ihtsuxbdhqa"), + "evm": MustParseCid("bafk2bzaceacw53ajmggrugzfiag75suv27hvqoduypk24xru3cumctpxotbya"), + "init": MustParseCid("bafk2bzaceahgtz6647nbarvyjwtvjadzd2i2m75jv6nnrq3kgwrjxnvkykmhs"), + "multisig": MustParseCid("bafk2bzaceduhknhgd45j5aey7c7pszbbwqm4vuih4iwbuffjxekqteebuja5q"), + "paymentchannel": MustParseCid("bafk2bzacec5x44xqfgmdvsxr4nwzbcwaotodzffsjsslw64xblr7xgx7jr6c4"), + "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"), + "reward": MustParseCid("bafk2bzacebzyuwya26rjjxmtzcszf5hcv54b3l7bz6hbflbx3wmpycbqcq6fa"), + "storagemarket": MustParseCid("bafk2bzaceav2nt4ek457u3ngn7da2gaqblwdumpzhsohp4d7whmubvarkllac"), + "storageminer": MustParseCid("bafk2bzacecxr3rdjaive7opvfyqv3p7tft6ragdozlqd6hciprlknda7u5hk6"), + "storagepower": MustParseCid("bafk2bzaceabvb7af55pgm3xpcc7lqjaupw3dyyokbmhjvbo2ebelizalmugfq"), + "system": MustParseCid("bafk2bzacecp7sswldmz6cbl7lh6y5vtwnn43mvdkgcazcyjjcpjjbtydb2f3w"), + "verifiedregistry": MustParseCid("bafk2bzaced27273xjvuyhxlne2kmbrbtupcxxpdchqbkiyr3dcqoijaok2k7e"), + }, +}, { + Network: "hyperspace", + Version: 8, + ManifestCid: MustParseCid("bafy2bzacedvffumcvf72f2btjqvece3kpcdorxq5tq76iwcmqbzvsiu526cqm"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacecim7uybic2qprbkjhowg7qkniv4zywj5h5g4u4ss72urco2akzuo"), @@ -363,8 +472,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacea7rfkjrixaidksnmjehglmavyt56nyeu3sfxu2e3dcpf62oab6tw"), }, }, { - Network: "hyperspace", - Version: 9, + Network: "hyperspace", + Version: 9, + ManifestCid: MustParseCid("bafy2bzacedvffumcvf72f2btjqvece3kpcdorxq5tq76iwcmqbzvsiu526cqm"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacecim7uybic2qprbkjhowg7qkniv4zywj5h5g4u4ss72urco2akzuo"), @@ -385,8 +495,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacea7rfkjrixaidksnmjehglmavyt56nyeu3sfxu2e3dcpf62oab6tw"), }, }, { - Network: "hyperspace", - Version: 10, + Network: "hyperspace", + Version: 10, + ManifestCid: MustParseCid("bafy2bzacedimb4dzty5tsyy3ucbcxai7crli452wn5cguhpmuelq74i4bffoo"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacecim7uybic2qprbkjhowg7qkniv4zywj5h5g4u4ss72urco2akzuo"), @@ -407,8 +518,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacea7rfkjrixaidksnmjehglmavyt56nyeu3sfxu2e3dcpf62oab6tw"), }, }, { - Network: "hyperspace", - Version: 11, + Network: "hyperspace", + Version: 11, + ManifestCid: MustParseCid("bafy2bzaced6hc7ujjmypg6mkrxdmf32oh2udhmhpmwkqyxusdkxoi2uoodyxg"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacecim7uybic2qprbkjhowg7qkniv4zywj5h5g4u4ss72urco2akzuo"), @@ -429,8 +541,32 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacea7rfkjrixaidksnmjehglmavyt56nyeu3sfxu2e3dcpf62oab6tw"), }, }, { - Network: "mainnet", - Version: 8, + Network: "hyperspace", + Version: 12, + + ManifestCid: MustParseCid("bafy2bzacebrhulkwvg46jx5p7kaurdtm5sfs22ecyc7hu7q6t6ckfidkicc6a"), + Actors: map[string]cid.Cid{ + "account": MustParseCid("bafk2bzacebmyadah25cxldsl2nwtravvsqh3cfycn2bf2dhcq5dteehk44yv4"), + "cron": MustParseCid("bafk2bzacedppsrfd3ut6mrdjb5sdgydatqgpjy323rdoim7z2os75llafeeic"), + "datacap": MustParseCid("bafk2bzacebtgwijwtn5jh3j6iyta4q3rij4hm45fgs7bm27qcthlrobzlahj4"), + "eam": MustParseCid("bafk2bzacebkpsxhntyjjnmplk7qmyisjr47vqwmckccy746t6df5qfebage2c"), + "ethaccount": MustParseCid("bafk2bzacecs7b5ncwsdao6km4a65zsxhxsuudn5sqy2h4gbrgfurglia5sv2y"), + "evm": MustParseCid("bafk2bzacebu7shvk7drrgyqopnyj7oscsvpdsyudnzv4ugfwryobxe4eevr4i"), + "init": MustParseCid("bafk2bzacedic7xk4yvcohv6kq22vpmlnra67qfp5wtimqkqlkcqm7t7sonwbs"), + "multisig": MustParseCid("bafk2bzaceafuswfpwoemcfrew4af6zgm2vtfr7dcfultbgsufibw5rita6vac"), + "paymentchannel": MustParseCid("bafk2bzacecgypg4duygqz2cvw3xhu4m6jmzbh2kyizddx75azjj4gh76pt662"), + "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"), + "reward": MustParseCid("bafk2bzaceat2eebo3v3qqk6pofvsvpt5oditu6ur7pkfc33ebxgghkzoacxsm"), + "storagemarket": MustParseCid("bafk2bzaceb7aaaric5jy3dbbawrbyylqkrvwlzmthw65t3ihcuywa3bl6imuu"), + "storageminer": MustParseCid("bafk2bzacedb7nve7h67r26qzmrsyynsfky6wwgdl5s4jdijmjy6ybfmdgxhuu"), + "storagepower": MustParseCid("bafk2bzacedv5mzkuuy6siqjqginpalbty2tkkiehfq7k4fteuupexohyfskfy"), + "system": MustParseCid("bafk2bzacebvwx4r5zcnc2kb42n4i6c4jglgi53aqrva3rlsiqu5omftj7ldkg"), + "verifiedregistry": MustParseCid("bafk2bzacech26voh3qiqv5xgcdndnqxjj5dq7eiwuhbxqxhzo5q7kim4zauig"), + }, +}, { + Network: "mainnet", + Version: 8, + ManifestCid: MustParseCid("bafy2bzacebogjbpiemi7npzxchgcjjki3tfxon4ims55obfyfleqntteljsea"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacedudbf7fc5va57t3tmo63snmt3en4iaidv4vo3qlyacbxaa6hlx6y"), @@ -446,8 +582,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzaceb3zbkjz3auizmoln2unmxep7dyfcmsre64vnqfhdyh7rkqfoxlw4"), }, }, { - Network: "mainnet", - Version: 9, + Network: "mainnet", + Version: 9, + ManifestCid: MustParseCid("bafy2bzaceb6j6666h36xnhksu3ww4kxb6e25niayfgkdnifaqi6m6ooc66i6i"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacect2p7urje3pylrrrjy3tngn6yaih4gtzauuatf2jllk3ksgfiw2y"), @@ -464,8 +601,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacecf3yodlyudzukumehbuabgqljyhjt5ifiv4vetcfohnvsxzynwga"), }, }, { - Network: "mainnet", - Version: 10, + Network: "mainnet", + Version: 10, + ManifestCid: MustParseCid("bafy2bzaceat4ut5xv3qn4lvvkvwvdn6gtlbnqzvueh67fjqlemw6eled5oqnc"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacedsn6i2flkpk6sb4iuejo7gfl5n6fhsdawggtbsihlrrjtvs7oepu"), @@ -486,8 +624,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacea2eehyf7h3m6ydh46piu2gtr4fawpqzh3brtmybgi2tyxf5nwj6m"), }, }, { - Network: "mainnet", - Version: 11, + Network: "mainnet", + Version: 11, + ManifestCid: MustParseCid("bafy2bzacea5vylkbby7rb42fknkk4g4byhj7hkqlxp4z4urydi3vlpwsgllik"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacedsn6i2flkpk6sb4iuejo7gfl5n6fhsdawggtbsihlrrjtvs7oepu"), @@ -508,8 +647,32 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacea2eehyf7h3m6ydh46piu2gtr4fawpqzh3brtmybgi2tyxf5nwj6m"), }, }, { - Network: "testing", - Version: 8, + Network: "mainnet", + Version: 12, + + ManifestCid: MustParseCid("bafy2bzacecyg4biu2uccoekdzen4qxf4qt24deyx6j5rpwfys7qp6lixvzsxo"), + Actors: map[string]cid.Cid{ + "account": MustParseCid("bafk2bzacect72amqxedrtjymuq5lfrskk2itnniyfa5gdvqp5sjoeeb33oi2e"), + "cron": MustParseCid("bafk2bzaceb3nfsu5jbftadzxy24caz4tlgi6476yml25tfq2g7higtgnifys4"), + "datacap": MustParseCid("bafk2bzacec3s7oovwjxdx3hnhtqr75fsvhj7p4545gedt3kf2iuaw7zqhym2c"), + "eam": MustParseCid("bafk2bzacebw4ye6kvufarvrtqebs57idw6ydtzli7egd4h2t33jwoi547trra"), + "ethaccount": MustParseCid("bafk2bzaceavrimh4mcla6zi25uhs3ystix2n5rp4kfqeiewkh337zpjbo3egq"), + "evm": MustParseCid("bafk2bzacecnsk2ydh2q6wxf2fzsvivo6cfrka3jos62io2ds2xou4gl2byqvw"), + "init": MustParseCid("bafk2bzacebb3l4gw6hfszizw5zwho3pfpnmgrmxqm4ie42dgn62325lo4vwc2"), + "multisig": MustParseCid("bafk2bzacedi6ii3ewygygjukn4viros3uiztonnf6tg6b3ppqk4pdtqrwgeuw"), + "paymentchannel": MustParseCid("bafk2bzacebwqqfhodha2jl7tbws3dpy2dkngepbllqx6hy72zdokui3ow6eeq"), + "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"), + "reward": MustParseCid("bafk2bzacedbeexrv2d4kiridcvqgdatcwo2an4xsmg3ckdrptxu6c3y7mm6ji"), + "storagemarket": MustParseCid("bafk2bzacedalaigmokrtimabt7y7bkok5nd2j5gmifdahht3dsz5ulj7vxdgu"), + "storageminer": MustParseCid("bafk2bzacecpq5wjp3frz3b4cd2pozegi7sykgcawnaowjm6ddyai2epbphxvw"), + "storagepower": MustParseCid("bafk2bzacedfxlpyj5uxlh5uuhl55lazmhm7q6pr3qoywxb25qrytbptpy7zb6"), + "system": MustParseCid("bafk2bzacecbscw26oyfgdse6y6ij5fqtaq5uo2ehfom26mle4rk4ann2xf2jq"), + "verifiedregistry": MustParseCid("bafk2bzaceanpda37bvefmzfemikyokwnspzxdxqz5kriaqex4hpevdhphu6sq"), + }, +}, { + Network: "testing", + Version: 8, + ManifestCid: MustParseCid("bafy2bzacedkjpqx27wgsvfxzuxfvixuxtbpt2y6yo6igcasez6gqiowron776"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacebmfbtdj5vruje5auacrhhprcjdd6uclhukb7je7t2f6ozfcgqlu2"), @@ -525,8 +688,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacectzxvtoselhnzsair5nv6k5vokvegnht6z2lfee4p3xexo4kg4m6"), }, }, { - Network: "testing", - Version: 9, + Network: "testing", + Version: 9, + ManifestCid: MustParseCid("bafy2bzacecnnrmekqw2xvud46g3vo6x26cogh3ydgljqajlxqxzzbuxsjlwjm"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzaceaiebfiuu76zoywzltelio2zuvsavirka27ur6kspn7scvcl5cuiy"), @@ -543,8 +707,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzaceatmqip2o3ausbntvdhj7yemu6hb3b5yqv6hm42gylbbmz7geocpm"), }, }, { - Network: "testing", - Version: 10, + Network: "testing", + Version: 10, + ManifestCid: MustParseCid("bafy2bzacearh2dy6uzcfvruckai5qbf7banklkg6uezaa7w6onsmdfxn2qxbs"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzaceds3iy5qjgr3stoywxt4uxvhybca23q7d2kxhitedgudrkhxaxa6o"), @@ -565,8 +730,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacec2ouguts4z335vetmdeifpk5fkqthcmrwshk7yxbw2uohddfu5lo"), }, }, { - Network: "testing", - Version: 11, + Network: "testing", + Version: 11, + ManifestCid: MustParseCid("bafy2bzacea7tbn4p232ecrjvlp2uvpci5pexqjqq2vpv4t5ihktpja2zsj3ek"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzaceds3iy5qjgr3stoywxt4uxvhybca23q7d2kxhitedgudrkhxaxa6o"), @@ -587,8 +753,32 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacec2ouguts4z335vetmdeifpk5fkqthcmrwshk7yxbw2uohddfu5lo"), }, }, { - Network: "testing-fake-proofs", - Version: 8, + Network: "testing", + Version: 12, + + ManifestCid: MustParseCid("bafy2bzacedjgfy7dmbd4i3io5r47eokprndhucdvwaxqyw4ijhhtldhewojk4"), + Actors: map[string]cid.Cid{ + "account": MustParseCid("bafk2bzacedhme5nkedu2x3vwwfakrs4zwqrbdcb7epevety7peisq4bcahyke"), + "cron": MustParseCid("bafk2bzaced5ky3x7w6naz45tsx4omkfztyy4flmr72hfr725ksrxdhvzuulwm"), + "datacap": MustParseCid("bafk2bzacedlhpgrdwzyfhcywoud5n5gwx2arwmyqvg7yogmrpt7h5gfmotjpc"), + "eam": MustParseCid("bafk2bzaceb754ghlrlmiqref2snlqsgvmpmg3tsjokok6eshh4pt4guv64zg4"), + "ethaccount": MustParseCid("bafk2bzaceaa5vdnlqdbf5tamzxvmuva3e5zfvy5vycw4gem2b6hmintnx6ye4"), + "evm": MustParseCid("bafk2bzaceaq5fequmt5jeuixw3qn6exx4atcpveugvblpz5oacy2xnypz2mww"), + "init": MustParseCid("bafk2bzaced275afmy4htdxsgff557owyteyl32vrntbdeemzqf3qkw22n6n4u"), + "multisig": MustParseCid("bafk2bzacebiao7jb3c7svwcphkdvixj6z3genj5ufwtattmp3cmelhqinq4na"), + "paymentchannel": MustParseCid("bafk2bzacecjuud657ydp5fwlbeiqzknzkqcrqh4d5huunmtubhas55fsj3n4k"), + "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"), + "reward": MustParseCid("bafk2bzaceae7eic2i2cmpxmkssezmutfc635tyzxxsmnu6ctnvkd33akrpipq"), + "storagemarket": MustParseCid("bafk2bzaceaj36pbc7sxzdelygq4ew3n2w3l5y442ogvznw3mw54fxafavjcru"), + "storageminer": MustParseCid("bafk2bzacednqbtebg7xxsr2sle42pfxmz5chxn32bsjssgc3mzt7iw2eevaly"), + "storagepower": MustParseCid("bafk2bzacebovido4zxtywaz32ejagvtk4v7z5w5nwxrflaldsf56xd7pecu4w"), + "system": MustParseCid("bafk2bzacecxbbatjxkfjm5f27esjghqe2o5ajmcalp4rbqq5vtxdmpq4ylmb2"), + "verifiedregistry": MustParseCid("bafk2bzacecwzauoon6sngmiiv4ppoxshirgzyfssfircvampxjkv673vlim6m"), + }, +}, { + Network: "testing-fake-proofs", + Version: 8, + ManifestCid: MustParseCid("bafy2bzacecd3lb5v6tzjylnhnrhexslssyaozy6hogzgpkhztoe76exbrgrug"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzacebmfbtdj5vruje5auacrhhprcjdd6uclhukb7je7t2f6ozfcgqlu2"), @@ -604,8 +794,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacectzxvtoselhnzsair5nv6k5vokvegnht6z2lfee4p3xexo4kg4m6"), }, }, { - Network: "testing-fake-proofs", - Version: 9, + Network: "testing-fake-proofs", + Version: 9, + ManifestCid: MustParseCid("bafy2bzacecql2gj2tri4fnbznmldue73qzt6zszvugw4exd64mwb52zrhv7k2"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzaceaiebfiuu76zoywzltelio2zuvsavirka27ur6kspn7scvcl5cuiy"), @@ -622,8 +813,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzaceatmqip2o3ausbntvdhj7yemu6hb3b5yqv6hm42gylbbmz7geocpm"), }, }, { - Network: "testing-fake-proofs", - Version: 10, + Network: "testing-fake-proofs", + Version: 10, + ManifestCid: MustParseCid("bafy2bzacedw6kk3u2vjexzqmm3vtvusd42lllk7wbnsrlxxmt35smsnmiatca"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzaceds3iy5qjgr3stoywxt4uxvhybca23q7d2kxhitedgudrkhxaxa6o"), @@ -644,8 +836,9 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "verifiedregistry": MustParseCid("bafk2bzacec2ouguts4z335vetmdeifpk5fkqthcmrwshk7yxbw2uohddfu5lo"), }, }, { - Network: "testing-fake-proofs", - Version: 11, + Network: "testing-fake-proofs", + Version: 11, + ManifestCid: MustParseCid("bafy2bzacecyqfyzmw72234rvbk6vzq2omnmt3cbfezkq2h3ewnn33w42b2s62"), Actors: map[string]cid.Cid{ "account": MustParseCid("bafk2bzaceds3iy5qjgr3stoywxt4uxvhybca23q7d2kxhitedgudrkhxaxa6o"), @@ -665,4 +858,27 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet "system": MustParseCid("bafk2bzaceaafqf7lwaiqx5po6b3l4dfg4xsr5qhfk3bjgoi7qke2mfy3shla4"), "verifiedregistry": MustParseCid("bafk2bzacec2ouguts4z335vetmdeifpk5fkqthcmrwshk7yxbw2uohddfu5lo"), }, +}, { + Network: "testing-fake-proofs", + Version: 12, + + ManifestCid: MustParseCid("bafy2bzaceblvi7d3yosakpuux27gqzr34gcjr6a2f6uzbyagmwybgfsk4xeu2"), + Actors: map[string]cid.Cid{ + "account": MustParseCid("bafk2bzacedhme5nkedu2x3vwwfakrs4zwqrbdcb7epevety7peisq4bcahyke"), + "cron": MustParseCid("bafk2bzaced5ky3x7w6naz45tsx4omkfztyy4flmr72hfr725ksrxdhvzuulwm"), + "datacap": MustParseCid("bafk2bzacedlhpgrdwzyfhcywoud5n5gwx2arwmyqvg7yogmrpt7h5gfmotjpc"), + "eam": MustParseCid("bafk2bzaceb754ghlrlmiqref2snlqsgvmpmg3tsjokok6eshh4pt4guv64zg4"), + "ethaccount": MustParseCid("bafk2bzaceaa5vdnlqdbf5tamzxvmuva3e5zfvy5vycw4gem2b6hmintnx6ye4"), + "evm": MustParseCid("bafk2bzaceaq5fequmt5jeuixw3qn6exx4atcpveugvblpz5oacy2xnypz2mww"), + "init": MustParseCid("bafk2bzaced275afmy4htdxsgff557owyteyl32vrntbdeemzqf3qkw22n6n4u"), + "multisig": MustParseCid("bafk2bzacebiao7jb3c7svwcphkdvixj6z3genj5ufwtattmp3cmelhqinq4na"), + "paymentchannel": MustParseCid("bafk2bzacecjuud657ydp5fwlbeiqzknzkqcrqh4d5huunmtubhas55fsj3n4k"), + "placeholder": MustParseCid("bafk2bzacedfvut2myeleyq67fljcrw4kkmn5pb5dpyozovj7jpoez5irnc3ro"), + "reward": MustParseCid("bafk2bzaceae7eic2i2cmpxmkssezmutfc635tyzxxsmnu6ctnvkd33akrpipq"), + "storagemarket": MustParseCid("bafk2bzaceaj36pbc7sxzdelygq4ew3n2w3l5y442ogvznw3mw54fxafavjcru"), + "storageminer": MustParseCid("bafk2bzaceaoaduzssj46mse6gizcy764ingsgvuavafkowf4vexluindct3ji"), + "storagepower": MustParseCid("bafk2bzacedqvicpfdcegfsj6p4rb3judx7v5v6trpnca7djbim4pts5appis4"), + "system": MustParseCid("bafk2bzacecxbbatjxkfjm5f27esjghqe2o5ajmcalp4rbqq5vtxdmpq4ylmb2"), + "verifiedregistry": MustParseCid("bafk2bzacecwzauoon6sngmiiv4ppoxshirgzyfssfircvampxjkv673vlim6m"), + }, }} diff --git a/build/builtin_actors_test.go b/build/builtin_actors_test.go index 858a8b3a1..bb133bdab 100644 --- a/build/builtin_actors_test.go +++ b/build/builtin_actors_test.go @@ -17,7 +17,13 @@ func TestEmbeddedMetadata(t *testing.T) { metadata, err := build.ReadEmbeddedBuiltinActorsMetadata() require.NoError(t, err) - require.Equal(t, metadata, build.EmbeddedBuiltinActorsMetadata) + for i, v1 := range metadata { + v2 := build.EmbeddedBuiltinActorsMetadata[i] + require.Equal(t, v1.Network, v2.Network) + require.Equal(t, v1.Version, v2.Version) + require.Equal(t, v1.ManifestCid, v2.ManifestCid) + require.Equal(t, v1.Actors, v2.Actors) + } } // Test that we're registering the manifest correctly. diff --git a/build/genesis/butterflynet.car b/build/genesis/butterflynet.car index 71a1c684e..cb0c4162b 100644 Binary files a/build/genesis/butterflynet.car and b/build/genesis/butterflynet.car differ diff --git a/build/openrpc/full.json.gz b/build/openrpc/full.json.gz index 5aad96eca..eaabc58de 100644 Binary files a/build/openrpc/full.json.gz and b/build/openrpc/full.json.gz differ diff --git a/build/openrpc/gateway.json.gz b/build/openrpc/gateway.json.gz index ebeb88701..530198113 100644 Binary files a/build/openrpc/gateway.json.gz and b/build/openrpc/gateway.json.gz differ diff --git a/build/openrpc/miner.json.gz b/build/openrpc/miner.json.gz index a767c9097..51b4f75ca 100644 Binary files a/build/openrpc/miner.json.gz and b/build/openrpc/miner.json.gz differ diff --git a/build/openrpc/worker.json.gz b/build/openrpc/worker.json.gz index 452cdab55..d8130e9e1 100644 Binary files a/build/openrpc/worker.json.gz and b/build/openrpc/worker.json.gz differ diff --git a/build/params_2k.go b/build/params_2k.go index b6b09bb2e..4fb4df183 100644 --- a/build/params_2k.go +++ b/build/params_2k.go @@ -63,6 +63,8 @@ var UpgradeHyggeHeight = abi.ChainEpoch(30) var UpgradeHyperspaceNV19Height = abi.ChainEpoch(60) +var UpgradeHyperspaceNV20Height = abi.ChainEpoch(120) + var DrandSchedule = map[abi.ChainEpoch]DrandEnum{ 0: DrandMainnet, } diff --git a/build/params_butterfly.go b/build/params_butterfly.go index 91d41e5d4..9e77bfd90 100644 --- a/build/params_butterfly.go +++ b/build/params_butterfly.go @@ -19,7 +19,7 @@ var DrandSchedule = map[abi.ChainEpoch]DrandEnum{ 0: DrandMainnet, } -const GenesisNetworkVersion = network.Version16 +const GenesisNetworkVersion = network.Version17 var NetworkBundle = "butterflynet" var BundleOverrides map[actorstypes.Version]string @@ -51,9 +51,11 @@ const UpgradeChocolateHeight = -17 const UpgradeOhSnapHeight = -18 const UpgradeSkyrHeight = -19 const UpgradeSharkHeight = abi.ChainEpoch(-20) -const UpgradeHyggeHeight = abi.ChainEpoch(600) +const UpgradeHyggeHeight = abi.ChainEpoch(80) -var UpgradeHyperspaceNV19Height = abi.ChainEpoch(99999999999999) +var UpgradeHyperspaceNV19Height = abi.ChainEpoch(100) + +var UpgradeHyperspaceNV20Height = abi.ChainEpoch(120) var SupportedProofTypes = []abi.RegisteredSealProof{ abi.RegisteredSealProof_StackedDrg512MiBV1, diff --git a/build/params_calibnet.go b/build/params_calibnet.go index 729ad1207..4b3ae329f 100644 --- a/build/params_calibnet.go +++ b/build/params_calibnet.go @@ -73,7 +73,11 @@ const UpgradeSharkHeight = 16800 // 6 days after genesis const UpgradeHyggeHeight = math.MaxInt64 -var UpgradeHyperspaceNV19Height = abi.ChainEpoch(99999999999999) +// dummy +var UpgradeHyperspaceNV19Height = abi.ChainEpoch(math.MaxInt64 - 1) + +// dummy +var UpgradeHyperspaceNV20Height = abi.ChainEpoch(math.MaxInt64) var SupportedProofTypes = []abi.RegisteredSealProof{ abi.RegisteredSealProof_StackedDrg32GiBV1, diff --git a/build/params_hyperspace.go b/build/params_hyperspace.go index 5fa7b8f28..bc24e891f 100644 --- a/build/params_hyperspace.go +++ b/build/params_hyperspace.go @@ -58,6 +58,9 @@ var UpgradeHyggeHeight = abi.ChainEpoch(-21) // 2023-01-18T15:00:00Z var UpgradeHyperspaceNV19Height = abi.ChainEpoch(6840) +// 2023-02-15T15:00:00Z +var UpgradeHyperspaceNV20Height = abi.ChainEpoch(87480) + var DrandSchedule = map[abi.ChainEpoch]DrandEnum{ 0: DrandMainnet, } diff --git a/build/params_interop.go b/build/params_interop.go index 36776f4b7..1c2ad61e2 100644 --- a/build/params_interop.go +++ b/build/params_interop.go @@ -4,6 +4,7 @@ package build import ( + "math" "os" "strconv" @@ -52,9 +53,11 @@ var UpgradeSkyrHeight = abi.ChainEpoch(-19) const UpgradeSharkHeight = abi.ChainEpoch(-20) -const UpgradeHyggeHeight = abi.ChainEpoch(99999999999999) +const UpgradeHyggeHeight = abi.ChainEpoch(math.MaxInt64 - 2) -var UpgradeHyperspaceNV19Height = abi.ChainEpoch(99999999999999) +var UpgradeHyperspaceNV19Height = abi.ChainEpoch(math.MaxInt64 - 1) + +var UpgradeHyperspaceNV20Height = abi.ChainEpoch(math.MaxInt64) var DrandSchedule = map[abi.ChainEpoch]DrandEnum{ 0: DrandMainnet, diff --git a/build/params_mainnet.go b/build/params_mainnet.go index bad165187..35561a281 100644 --- a/build/params_mainnet.go +++ b/build/params_mainnet.go @@ -87,10 +87,13 @@ const UpgradeSkyrHeight = 1960320 const UpgradeSharkHeight = 2383680 // ?????????????? -var UpgradeHyggeHeight = abi.ChainEpoch(math.MaxInt64 - 1) +var UpgradeHyggeHeight = abi.ChainEpoch(math.MaxInt64 - 2) // ?????????????? -var UpgradeHyperspaceNV19Height = abi.ChainEpoch(math.MaxInt64) +var UpgradeHyperspaceNV19Height = abi.ChainEpoch(math.MaxInt64 - 1) + +// dummy +var UpgradeHyperspaceNV20Height = abi.ChainEpoch(math.MaxInt64) var SupportedProofTypes = []abi.RegisteredSealProof{ abi.RegisteredSealProof_StackedDrg32GiBV1, diff --git a/build/params_shared_vals.go b/build/params_shared_vals.go index 6d9c2279f..6e2e6d8fb 100644 --- a/build/params_shared_vals.go +++ b/build/params_shared_vals.go @@ -33,7 +33,7 @@ const TestNetworkVersion = network.Version{{.latestNetworkVersion}} /* inline-gen start */ -const TestNetworkVersion = network.Version19 +const TestNetworkVersion = network.Version20 /* inline-gen end */ diff --git a/chain/actors/builtin/account/account.go b/chain/actors/builtin/account/account.go index a29248d56..dcb60f801 100644 --- a/chain/actors/builtin/account/account.go +++ b/chain/actors/builtin/account/account.go @@ -6,7 +6,7 @@ import ( "github.com/filecoin-project/go-address" actorstypes "github.com/filecoin-project/go-state-types/actors" - builtin11 "github.com/filecoin-project/go-state-types/builtin" + builtin12 "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" @@ -22,7 +22,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" ) -var Methods = builtin11.MethodsAccount +var Methods = builtin12.MethodsAccount func Load(store adt.Store, act *types.Actor) (State, error) { if name, av, ok := actors.GetActorMetaByCode(act.Code); ok { @@ -44,6 +44,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -111,6 +114,9 @@ func MakeState(store adt.Store, av actorstypes.Version, addr address.Address) (S case actorstypes.Version11: return make11(store, addr) + case actorstypes.Version12: + return make12(store, addr) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -139,5 +145,6 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/builtin/account/v12.go b/chain/actors/builtin/account/v12.go new file mode 100644 index 000000000..af2c4186f --- /dev/null +++ b/chain/actors/builtin/account/v12.go @@ -0,0 +1,62 @@ +package account + +import ( + "fmt" + + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-address" + actorstypes "github.com/filecoin-project/go-state-types/actors" + account12 "github.com/filecoin-project/go-state-types/builtin/v12/account" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store, addr address.Address) (State, error) { + out := state12{store: store} + out.State = account12.State{Address: addr} + return &out, nil +} + +type state12 struct { + account12.State + store adt.Store +} + +func (s *state12) PubkeyAddress() (address.Address, error) { + return s.Address, nil +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +func (s *state12) ActorKey() string { + return manifest.AccountKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/cron/cron.go b/chain/actors/builtin/cron/cron.go index c2f758698..17b291788 100644 --- a/chain/actors/builtin/cron/cron.go +++ b/chain/actors/builtin/cron/cron.go @@ -5,7 +5,7 @@ import ( "golang.org/x/xerrors" actorstypes "github.com/filecoin-project/go-state-types/actors" - builtin11 "github.com/filecoin-project/go-state-types/builtin" + builtin12 "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/manifest" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" @@ -40,6 +40,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -107,13 +110,16 @@ func MakeState(store adt.Store, av actorstypes.Version) (State, error) { case actorstypes.Version11: return make11(store) + case actorstypes.Version12: + return make12(store) + } return nil, xerrors.Errorf("unknown actor version %d", av) } var ( - Address = builtin11.CronActorAddr - Methods = builtin11.MethodsCron + Address = builtin12.CronActorAddr + Methods = builtin12.MethodsCron ) type State interface { @@ -137,5 +143,6 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/builtin/cron/v12.go b/chain/actors/builtin/cron/v12.go new file mode 100644 index 000000000..44f018d68 --- /dev/null +++ b/chain/actors/builtin/cron/v12.go @@ -0,0 +1,57 @@ +package cron + +import ( + "fmt" + + "github.com/ipfs/go-cid" + + actorstypes "github.com/filecoin-project/go-state-types/actors" + cron12 "github.com/filecoin-project/go-state-types/builtin/v12/cron" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store) (State, error) { + out := state12{store: store} + out.State = *cron12.ConstructState(cron12.BuiltInEntries()) + return &out, nil +} + +type state12 struct { + cron12.State + store adt.Store +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +func (s *state12) ActorKey() string { + return manifest.CronKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/datacap/datacap.go b/chain/actors/builtin/datacap/datacap.go index 3cf557e6c..0c8f04bbf 100644 --- a/chain/actors/builtin/datacap/datacap.go +++ b/chain/actors/builtin/datacap/datacap.go @@ -7,7 +7,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" - builtin11 "github.com/filecoin-project/go-state-types/builtin" + builtin12 "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" @@ -17,8 +17,8 @@ import ( ) var ( - Address = builtin11.DatacapActorAddr - Methods = builtin11.MethodsDatacap + Address = builtin12.DatacapActorAddr + Methods = builtin12.MethodsDatacap ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -38,6 +38,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -56,6 +59,9 @@ func MakeState(store adt.Store, av actorstypes.Version, governor address.Address case actorstypes.Version11: return make11(store, governor, bitwidth) + case actorstypes.Version12: + return make12(store, governor, bitwidth) + default: return nil, xerrors.Errorf("datacap actor only valid for actors v9 and above, got %d", av) } @@ -79,5 +85,6 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/builtin/datacap/v12.go b/chain/actors/builtin/datacap/v12.go new file mode 100644 index 000000000..91563a2b6 --- /dev/null +++ b/chain/actors/builtin/datacap/v12.go @@ -0,0 +1,82 @@ +package datacap + +import ( + "fmt" + + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + datacap12 "github.com/filecoin-project/go-state-types/builtin/v12/datacap" + adt12 "github.com/filecoin-project/go-state-types/builtin/v12/util/adt" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store, governor address.Address, bitwidth uint64) (State, error) { + out := state12{store: store} + s, err := datacap12.ConstructState(store, governor, bitwidth) + if err != nil { + return nil, err + } + + out.State = *s + + return &out, nil +} + +type state12 struct { + datacap12.State + store adt.Store +} + +func (s *state12) Governor() (address.Address, error) { + return s.State.Governor, nil +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +func (s *state12) ForEachClient(cb func(addr address.Address, dcap abi.StoragePower) error) error { + return forEachClient(s.store, actors.Version12, s.verifiedClients, cb) +} + +func (s *state12) verifiedClients() (adt.Map, error) { + return adt12.AsMap(s.store, s.Token.Balances, int(s.Token.HamtBitWidth)) +} + +func (s *state12) VerifiedClientDataCap(addr address.Address) (bool, abi.StoragePower, error) { + return getDataCap(s.store, actors.Version12, s.verifiedClients, addr) +} + +func (s *state12) ActorKey() string { + return manifest.DatacapKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/evm/actor.go.template b/chain/actors/builtin/evm/actor.go.template index d20db6bcb..aa23b5f11 100644 --- a/chain/actors/builtin/evm/actor.go.template +++ b/chain/actors/builtin/evm/actor.go.template @@ -49,4 +49,8 @@ type State interface { Nonce() (uint64, error) GetState() interface{} + + GetBytecode() ([]byte, error) + GetBytecodeCID() (cid.Cid, error) + GetBytecodeHash() ([32]byte, error) } diff --git a/chain/actors/builtin/evm/evm.go b/chain/actors/builtin/evm/evm.go index fb5e1412b..24315cf75 100644 --- a/chain/actors/builtin/evm/evm.go +++ b/chain/actors/builtin/evm/evm.go @@ -5,7 +5,7 @@ import ( "golang.org/x/xerrors" actorstypes "github.com/filecoin-project/go-state-types/actors" - builtin11 "github.com/filecoin-project/go-state-types/builtin" + builtin12 "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" @@ -14,7 +14,7 @@ import ( "github.com/filecoin-project/lotus/chain/types" ) -var Methods = builtin11.MethodsEVM +var Methods = builtin12.MethodsEVM func Load(store adt.Store, act *types.Actor) (State, error) { if name, av, ok := actors.GetActorMetaByCode(act.Code); ok { @@ -30,6 +30,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -45,6 +48,9 @@ func MakeState(store adt.Store, av actorstypes.Version, bytecode cid.Cid) (State case actorstypes.Version11: return make11(store, bytecode) + case actorstypes.Version12: + return make12(store, bytecode) + default: return nil, xerrors.Errorf("evm actor only valid for actors v10 and above, got %d", av) } @@ -55,4 +61,8 @@ type State interface { Nonce() (uint64, error) GetState() interface{} + + GetBytecode() ([]byte, error) + GetBytecodeCID() (cid.Cid, error) + GetBytecodeHash() ([32]byte, error) } diff --git a/chain/actors/builtin/evm/state.go.template b/chain/actors/builtin/evm/state.go.template index acc78dc0f..eb55a8463 100644 --- a/chain/actors/builtin/evm/state.go.template +++ b/chain/actors/builtin/evm/state.go.template @@ -21,12 +21,12 @@ func load{{.v}}(store adt.Store, root cid.Cid) (State, error) { func make{{.v}}(store adt.Store, bytecode cid.Cid) (State, error) { out := state{{.v}}{store: store} - s, err := evm{{.v}}.ConstructState(store, bytecode) - if err != nil { - return nil, err - } + s, err := evm{{.v}}.ConstructState(store, bytecode) + if err != nil { + return nil, err + } - out.State = *s + out.State = *s return &out, nil } @@ -42,4 +42,26 @@ func (s *state{{.v}}) Nonce() (uint64, error) { func (s *state{{.v}}) GetState() interface{} { return &s.State -} \ No newline at end of file +} + +func (s *state{{.v}}) GetBytecodeCID() (cid.Cid, error) { + return s.State.Bytecode, nil +} + +func (s *state{{.v}}) GetBytecodeHash() ([32]byte, error) { + return s.State.BytecodeHash, nil +} + +func (s *state{{.v}}) GetBytecode() ([]byte, error) { + bc, err := s.GetBytecodeCID() + if err != nil { + return nil, err + } + + var byteCode abi.CborBytesTransparent + if err := s.store.Get(s.store.Context(), bc, &byteCode); err != nil { + return nil, err + } + + return byteCode, nil +} diff --git a/chain/actors/builtin/evm/v10.go b/chain/actors/builtin/evm/v10.go index 78a4a2383..77a09037b 100644 --- a/chain/actors/builtin/evm/v10.go +++ b/chain/actors/builtin/evm/v10.go @@ -3,6 +3,7 @@ package evm import ( "github.com/ipfs/go-cid" + "github.com/filecoin-project/go-state-types/abi" evm10 "github.com/filecoin-project/go-state-types/builtin/v10/evm" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -43,3 +44,25 @@ func (s *state10) Nonce() (uint64, error) { func (s *state10) GetState() interface{} { return &s.State } + +func (s *state10) GetBytecodeCID() (cid.Cid, error) { + return s.State.Bytecode, nil +} + +func (s *state10) GetBytecodeHash() ([32]byte, error) { + return s.State.BytecodeHash, nil +} + +func (s *state10) GetBytecode() ([]byte, error) { + bc, err := s.GetBytecodeCID() + if err != nil { + return nil, err + } + + var byteCode abi.CborBytesTransparent + if err := s.store.Get(s.store.Context(), bc, &byteCode); err != nil { + return nil, err + } + + return byteCode, nil +} diff --git a/chain/actors/builtin/evm/v11.go b/chain/actors/builtin/evm/v11.go index 16238b0da..fca095ee2 100644 --- a/chain/actors/builtin/evm/v11.go +++ b/chain/actors/builtin/evm/v11.go @@ -3,6 +3,7 @@ package evm import ( "github.com/ipfs/go-cid" + "github.com/filecoin-project/go-state-types/abi" evm11 "github.com/filecoin-project/go-state-types/builtin/v11/evm" "github.com/filecoin-project/lotus/chain/actors/adt" @@ -43,3 +44,25 @@ func (s *state11) Nonce() (uint64, error) { func (s *state11) GetState() interface{} { return &s.State } + +func (s *state11) GetBytecodeCID() (cid.Cid, error) { + return s.State.Bytecode, nil +} + +func (s *state11) GetBytecodeHash() ([32]byte, error) { + return s.State.BytecodeHash, nil +} + +func (s *state11) GetBytecode() ([]byte, error) { + bc, err := s.GetBytecodeCID() + if err != nil { + return nil, err + } + + var byteCode abi.CborBytesTransparent + if err := s.store.Get(s.store.Context(), bc, &byteCode); err != nil { + return nil, err + } + + return byteCode, nil +} diff --git a/chain/actors/builtin/evm/v12.go b/chain/actors/builtin/evm/v12.go new file mode 100644 index 000000000..821f4bee0 --- /dev/null +++ b/chain/actors/builtin/evm/v12.go @@ -0,0 +1,68 @@ +package evm + +import ( + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-state-types/abi" + evm12 "github.com/filecoin-project/go-state-types/builtin/v12/evm" + + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store, bytecode cid.Cid) (State, error) { + out := state12{store: store} + s, err := evm12.ConstructState(store, bytecode) + if err != nil { + return nil, err + } + + out.State = *s + + return &out, nil +} + +type state12 struct { + evm12.State + store adt.Store +} + +func (s *state12) Nonce() (uint64, error) { + return s.State.Nonce, nil +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +func (s *state12) GetBytecodeCID() (cid.Cid, error) { + return s.State.Bytecode, nil +} + +func (s *state12) GetBytecodeHash() ([32]byte, error) { + return s.State.BytecodeHash, nil +} + +func (s *state12) GetBytecode() ([]byte, error) { + bc, err := s.GetBytecodeCID() + if err != nil { + return nil, err + } + + var byteCode abi.CborBytesTransparent + if err := s.store.Get(s.store.Context(), bc, &byteCode); err != nil { + return nil, err + } + + return byteCode, nil +} diff --git a/chain/actors/builtin/init/init.go b/chain/actors/builtin/init/init.go index 2d9e41275..41a763ecf 100644 --- a/chain/actors/builtin/init/init.go +++ b/chain/actors/builtin/init/init.go @@ -7,7 +7,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" - builtin11 "github.com/filecoin-project/go-state-types/builtin" + builtin12 "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" @@ -25,8 +25,8 @@ import ( ) var ( - Address = builtin11.InitActorAddr - Methods = builtin11.MethodsInit + Address = builtin12.InitActorAddr + Methods = builtin12.MethodsInit ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -49,6 +49,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -116,6 +119,9 @@ func MakeState(store adt.Store, av actorstypes.Version, networkName string) (Sta case actorstypes.Version11: return make11(store, networkName) + case actorstypes.Version12: + return make12(store, networkName) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -167,5 +173,6 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/builtin/init/v12.go b/chain/actors/builtin/init/v12.go new file mode 100644 index 000000000..3eab7a740 --- /dev/null +++ b/chain/actors/builtin/init/v12.go @@ -0,0 +1,147 @@ +package init + +import ( + "crypto/sha256" + "fmt" + + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + builtin12 "github.com/filecoin-project/go-state-types/builtin" + init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" + adt12 "github.com/filecoin-project/go-state-types/builtin/v12/util/adt" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/node/modules/dtypes" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store, networkName string) (State, error) { + out := state12{store: store} + + s, err := init12.ConstructState(store, networkName) + if err != nil { + return nil, err + } + + out.State = *s + + return &out, nil +} + +type state12 struct { + init12.State + store adt.Store +} + +func (s *state12) ResolveAddress(address address.Address) (address.Address, bool, error) { + return s.State.ResolveAddress(s.store, address) +} + +func (s *state12) MapAddressToNewID(address address.Address) (address.Address, error) { + return s.State.MapAddressToNewID(s.store, address) +} + +func (s *state12) ForEachActor(cb func(id abi.ActorID, address address.Address) error) error { + addrs, err := adt12.AsMap(s.store, s.State.AddressMap, builtin12.DefaultHamtBitwidth) + if err != nil { + return err + } + var actorID cbg.CborInt + return addrs.ForEach(&actorID, func(key string) error { + addr, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + return cb(abi.ActorID(actorID), addr) + }) +} + +func (s *state12) NetworkName() (dtypes.NetworkName, error) { + return dtypes.NetworkName(s.State.NetworkName), nil +} + +func (s *state12) SetNetworkName(name string) error { + s.State.NetworkName = name + return nil +} + +func (s *state12) SetNextID(id abi.ActorID) error { + s.State.NextID = id + return nil +} + +func (s *state12) Remove(addrs ...address.Address) (err error) { + m, err := adt12.AsMap(s.store, s.State.AddressMap, builtin12.DefaultHamtBitwidth) + if err != nil { + return err + } + for _, addr := range addrs { + if err = m.Delete(abi.AddrKey(addr)); err != nil { + return xerrors.Errorf("failed to delete entry for address: %s; err: %w", addr, err) + } + } + amr, err := m.Root() + if err != nil { + return xerrors.Errorf("failed to get address map root: %w", err) + } + s.State.AddressMap = amr + return nil +} + +func (s *state12) SetAddressMap(mcid cid.Cid) error { + s.State.AddressMap = mcid + return nil +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +func (s *state12) AddressMap() (adt.Map, error) { + return adt12.AsMap(s.store, s.State.AddressMap, builtin12.DefaultHamtBitwidth) +} + +func (s *state12) AddressMapBitWidth() int { + return builtin12.DefaultHamtBitwidth +} + +func (s *state12) AddressMapHashFunction() func(input []byte) []byte { + return func(input []byte) []byte { + res := sha256.Sum256(input) + return res[:] + } +} + +func (s *state12) ActorKey() string { + return manifest.InitKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/market/market.go b/chain/actors/builtin/market/market.go index 36936e787..39473d560 100644 --- a/chain/actors/builtin/market/market.go +++ b/chain/actors/builtin/market/market.go @@ -55,6 +55,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -122,6 +125,9 @@ func MakeState(store adt.Store, av actorstypes.Version) (State, error) { case actorstypes.Version11: return make11(store) + case actorstypes.Version12: + return make12(store) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -217,6 +223,9 @@ func DecodePublishStorageDealsReturn(b []byte, nv network.Version) (PublishStora case actorstypes.Version11: return decodePublishStorageDealsReturn11(b) + case actorstypes.Version12: + return decodePublishStorageDealsReturn12(b) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -303,5 +312,6 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/builtin/market/v12.go b/chain/actors/builtin/market/v12.go new file mode 100644 index 000000000..3532fc4f4 --- /dev/null +++ b/chain/actors/builtin/market/v12.go @@ -0,0 +1,377 @@ +package market + +import ( + "bytes" + "fmt" + + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-bitfield" + rlepluslazy "github.com/filecoin-project/go-bitfield/rle" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/builtin" + market12 "github.com/filecoin-project/go-state-types/builtin/v12/market" + adt12 "github.com/filecoin-project/go-state-types/builtin/v12/util/adt" + markettypes "github.com/filecoin-project/go-state-types/builtin/v9/market" + verifregtypes "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/types" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store) (State, error) { + out := state12{store: store} + + s, err := market12.ConstructState(store) + if err != nil { + return nil, err + } + + out.State = *s + + return &out, nil +} + +type state12 struct { + market12.State + store adt.Store +} + +func (s *state12) TotalLocked() (abi.TokenAmount, error) { + fml := types.BigAdd(s.TotalClientLockedCollateral, s.TotalProviderLockedCollateral) + fml = types.BigAdd(fml, s.TotalClientStorageFee) + return fml, nil +} + +func (s *state12) BalancesChanged(otherState State) (bool, error) { + otherState12, ok := otherState.(*state12) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.EscrowTable.Equals(otherState12.State.EscrowTable) || !s.State.LockedTable.Equals(otherState12.State.LockedTable), nil +} + +func (s *state12) StatesChanged(otherState State) (bool, error) { + otherState12, ok := otherState.(*state12) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.States.Equals(otherState12.State.States), nil +} + +func (s *state12) States() (DealStates, error) { + stateArray, err := adt12.AsArray(s.store, s.State.States, market12.StatesAmtBitwidth) + if err != nil { + return nil, err + } + return &dealStates12{stateArray}, nil +} + +func (s *state12) ProposalsChanged(otherState State) (bool, error) { + otherState12, ok := otherState.(*state12) + if !ok { + // there's no way to compare different versions of the state, so let's + // just say that means the state of balances has changed + return true, nil + } + return !s.State.Proposals.Equals(otherState12.State.Proposals), nil +} + +func (s *state12) Proposals() (DealProposals, error) { + proposalArray, err := adt12.AsArray(s.store, s.State.Proposals, market12.ProposalsAmtBitwidth) + if err != nil { + return nil, err + } + return &dealProposals12{proposalArray}, nil +} + +func (s *state12) EscrowTable() (BalanceTable, error) { + bt, err := adt12.AsBalanceTable(s.store, s.State.EscrowTable) + if err != nil { + return nil, err + } + return &balanceTable12{bt}, nil +} + +func (s *state12) LockedTable() (BalanceTable, error) { + bt, err := adt12.AsBalanceTable(s.store, s.State.LockedTable) + if err != nil { + return nil, err + } + return &balanceTable12{bt}, nil +} + +func (s *state12) VerifyDealsForActivation( + minerAddr address.Address, deals []abi.DealID, currEpoch, sectorExpiry abi.ChainEpoch, +) (weight, verifiedWeight abi.DealWeight, err error) { + w, vw, _, err := market12.ValidateDealsForActivation(&s.State, s.store, deals, minerAddr, sectorExpiry, currEpoch) + return w, vw, err +} + +func (s *state12) NextID() (abi.DealID, error) { + return s.State.NextID, nil +} + +type balanceTable12 struct { + *adt12.BalanceTable +} + +func (bt *balanceTable12) ForEach(cb func(address.Address, abi.TokenAmount) error) error { + asMap := (*adt12.Map)(bt.BalanceTable) + var ta abi.TokenAmount + return asMap.ForEach(&ta, func(key string) error { + a, err := address.NewFromBytes([]byte(key)) + if err != nil { + return err + } + return cb(a, ta) + }) +} + +type dealStates12 struct { + adt.Array +} + +func (s *dealStates12) Get(dealID abi.DealID) (*DealState, bool, error) { + var deal12 market12.DealState + found, err := s.Array.Get(uint64(dealID), &deal12) + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + deal := fromV12DealState(deal12) + return &deal, true, nil +} + +func (s *dealStates12) ForEach(cb func(dealID abi.DealID, ds DealState) error) error { + var ds12 market12.DealState + return s.Array.ForEach(&ds12, func(idx int64) error { + return cb(abi.DealID(idx), fromV12DealState(ds12)) + }) +} + +func (s *dealStates12) decode(val *cbg.Deferred) (*DealState, error) { + var ds12 market12.DealState + if err := ds12.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return nil, err + } + ds := fromV12DealState(ds12) + return &ds, nil +} + +func (s *dealStates12) array() adt.Array { + return s.Array +} + +func fromV12DealState(v12 market12.DealState) DealState { + ret := DealState{ + SectorStartEpoch: v12.SectorStartEpoch, + LastUpdatedEpoch: v12.LastUpdatedEpoch, + SlashEpoch: v12.SlashEpoch, + VerifiedClaim: 0, + } + + ret.VerifiedClaim = verifregtypes.AllocationId(v12.VerifiedClaim) + + return ret +} + +type dealProposals12 struct { + adt.Array +} + +func (s *dealProposals12) Get(dealID abi.DealID) (*DealProposal, bool, error) { + var proposal12 market12.DealProposal + found, err := s.Array.Get(uint64(dealID), &proposal12) + if err != nil { + return nil, false, err + } + if !found { + return nil, false, nil + } + + proposal, err := fromV12DealProposal(proposal12) + if err != nil { + return nil, true, xerrors.Errorf("decoding proposal: %w", err) + } + + return &proposal, true, nil +} + +func (s *dealProposals12) ForEach(cb func(dealID abi.DealID, dp DealProposal) error) error { + var dp12 market12.DealProposal + return s.Array.ForEach(&dp12, func(idx int64) error { + dp, err := fromV12DealProposal(dp12) + if err != nil { + return xerrors.Errorf("decoding proposal: %w", err) + } + + return cb(abi.DealID(idx), dp) + }) +} + +func (s *dealProposals12) decode(val *cbg.Deferred) (*DealProposal, error) { + var dp12 market12.DealProposal + if err := dp12.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return nil, err + } + + dp, err := fromV12DealProposal(dp12) + if err != nil { + return nil, err + } + + return &dp, nil +} + +func (s *dealProposals12) array() adt.Array { + return s.Array +} + +func fromV12DealProposal(v12 market12.DealProposal) (DealProposal, error) { + + label, err := fromV12Label(v12.Label) + + if err != nil { + return DealProposal{}, xerrors.Errorf("error setting deal label: %w", err) + } + + return DealProposal{ + PieceCID: v12.PieceCID, + PieceSize: v12.PieceSize, + VerifiedDeal: v12.VerifiedDeal, + Client: v12.Client, + Provider: v12.Provider, + + Label: label, + + StartEpoch: v12.StartEpoch, + EndEpoch: v12.EndEpoch, + StoragePricePerEpoch: v12.StoragePricePerEpoch, + + ProviderCollateral: v12.ProviderCollateral, + ClientCollateral: v12.ClientCollateral, + }, nil +} + +func fromV12Label(v12 market12.DealLabel) (DealLabel, error) { + if v12.IsString() { + str, err := v12.ToString() + if err != nil { + return markettypes.EmptyDealLabel, xerrors.Errorf("failed to convert string label to string: %w", err) + } + return markettypes.NewLabelFromString(str) + } + + bs, err := v12.ToBytes() + if err != nil { + return markettypes.EmptyDealLabel, xerrors.Errorf("failed to convert bytes label to bytes: %w", err) + } + return markettypes.NewLabelFromBytes(bs) +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +var _ PublishStorageDealsReturn = (*publishStorageDealsReturn12)(nil) + +func decodePublishStorageDealsReturn12(b []byte) (PublishStorageDealsReturn, error) { + var retval market12.PublishStorageDealsReturn + if err := retval.UnmarshalCBOR(bytes.NewReader(b)); err != nil { + return nil, xerrors.Errorf("failed to unmarshal PublishStorageDealsReturn: %w", err) + } + + return &publishStorageDealsReturn12{retval}, nil +} + +type publishStorageDealsReturn12 struct { + market12.PublishStorageDealsReturn +} + +func (r *publishStorageDealsReturn12) IsDealValid(index uint64) (bool, int, error) { + + set, err := r.ValidDeals.IsSet(index) + if err != nil || !set { + return false, -1, err + } + maskBf, err := bitfield.NewFromIter(&rlepluslazy.RunSliceIterator{ + Runs: []rlepluslazy.Run{rlepluslazy.Run{Val: true, Len: index}}}) + if err != nil { + return false, -1, err + } + before, err := bitfield.IntersectBitField(maskBf, r.ValidDeals) + if err != nil { + return false, -1, err + } + outIdx, err := before.Count() + if err != nil { + return false, -1, err + } + return set, int(outIdx), nil + +} + +func (r *publishStorageDealsReturn12) DealIDs() ([]abi.DealID, error) { + return r.IDs, nil +} + +func (s *state12) GetAllocationIdForPendingDeal(dealId abi.DealID) (verifregtypes.AllocationId, error) { + + allocations, err := adt12.AsMap(s.store, s.PendingDealAllocationIds, builtin.DefaultHamtBitwidth) + if err != nil { + return verifregtypes.NoAllocationID, xerrors.Errorf("failed to load allocation id for %d: %w", dealId, err) + } + + var allocationId cbg.CborInt + found, err := allocations.Get(abi.UIntKey(uint64(dealId)), &allocationId) + if err != nil { + return verifregtypes.NoAllocationID, xerrors.Errorf("failed to load allocation id for %d: %w", dealId, err) + } + if !found { + return verifregtypes.NoAllocationID, nil + } + + return verifregtypes.AllocationId(allocationId), nil + +} + +func (s *state12) ActorKey() string { + return manifest.MarketKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/miner/miner.go b/chain/actors/builtin/miner/miner.go index 3421f2c8d..d2ae301fb 100644 --- a/chain/actors/builtin/miner/miner.go +++ b/chain/actors/builtin/miner/miner.go @@ -48,6 +48,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -115,6 +118,9 @@ func MakeState(store adt.Store, av actors.Version) (State, error) { case actors.Version11: return make11(store) + case actors.Version12: + return make12(store) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -321,5 +327,6 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/builtin/miner/v12.go b/chain/actors/builtin/miner/v12.go new file mode 100644 index 000000000..787da7d0f --- /dev/null +++ b/chain/actors/builtin/miner/v12.go @@ -0,0 +1,591 @@ +package miner + +import ( + "bytes" + "errors" + "fmt" + + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-bitfield" + rle "github.com/filecoin-project/go-bitfield/rle" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + builtin12 "github.com/filecoin-project/go-state-types/builtin" + miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" + adt12 "github.com/filecoin-project/go-state-types/builtin/v12/util/adt" + "github.com/filecoin-project/go-state-types/dline" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store) (State, error) { + out := state12{store: store} + out.State = miner12.State{} + return &out, nil +} + +type state12 struct { + miner12.State + store adt.Store +} + +type deadline12 struct { + miner12.Deadline + store adt.Store +} + +type partition12 struct { + miner12.Partition + store adt.Store +} + +func (s *state12) AvailableBalance(bal abi.TokenAmount) (available abi.TokenAmount, err error) { + defer func() { + if r := recover(); r != nil { + err = xerrors.Errorf("failed to get available balance: %w", r) + available = abi.NewTokenAmount(0) + } + }() + // this panics if the miner doesnt have enough funds to cover their locked pledge + available, err = s.GetAvailableBalance(bal) + return available, err +} + +func (s *state12) VestedFunds(epoch abi.ChainEpoch) (abi.TokenAmount, error) { + return s.CheckVestedFunds(s.store, epoch) +} + +func (s *state12) LockedFunds() (LockedFunds, error) { + return LockedFunds{ + VestingFunds: s.State.LockedFunds, + InitialPledgeRequirement: s.State.InitialPledge, + PreCommitDeposits: s.State.PreCommitDeposits, + }, nil +} + +func (s *state12) FeeDebt() (abi.TokenAmount, error) { + return s.State.FeeDebt, nil +} + +func (s *state12) InitialPledge() (abi.TokenAmount, error) { + return s.State.InitialPledge, nil +} + +func (s *state12) PreCommitDeposits() (abi.TokenAmount, error) { + return s.State.PreCommitDeposits, nil +} + +// Returns nil, nil if sector is not found +func (s *state12) GetSector(num abi.SectorNumber) (*SectorOnChainInfo, error) { + info, ok, err := s.State.GetSector(s.store, num) + if !ok || err != nil { + return nil, err + } + + ret := fromV12SectorOnChainInfo(*info) + return &ret, nil +} + +func (s *state12) FindSector(num abi.SectorNumber) (*SectorLocation, error) { + dlIdx, partIdx, err := s.State.FindSector(s.store, num) + if err != nil { + return nil, err + } + return &SectorLocation{ + Deadline: dlIdx, + Partition: partIdx, + }, nil +} + +func (s *state12) NumLiveSectors() (uint64, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return 0, err + } + var total uint64 + if err := dls.ForEach(s.store, func(dlIdx uint64, dl *miner12.Deadline) error { + total += dl.LiveSectors + return nil + }); err != nil { + return 0, err + } + return total, nil +} + +// GetSectorExpiration returns the effective expiration of the given sector. +// +// If the sector does not expire early, the Early expiration field is 0. +func (s *state12) GetSectorExpiration(num abi.SectorNumber) (*SectorExpiration, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return nil, err + } + // NOTE: this can be optimized significantly. + // 1. If the sector is non-faulty, it will expire on-time (can be + // learned from the sector info). + // 2. If it's faulty, it will expire early within the first 42 entries + // of the expiration queue. + + stopErr := errors.New("stop") + out := SectorExpiration{} + err = dls.ForEach(s.store, func(dlIdx uint64, dl *miner12.Deadline) error { + partitions, err := dl.PartitionsArray(s.store) + if err != nil { + return err + } + quant := s.State.QuantSpecForDeadline(dlIdx) + var part miner12.Partition + return partitions.ForEach(&part, func(partIdx int64) error { + if found, err := part.Sectors.IsSet(uint64(num)); err != nil { + return err + } else if !found { + return nil + } + if found, err := part.Terminated.IsSet(uint64(num)); err != nil { + return err + } else if found { + // already terminated + return stopErr + } + + q, err := miner12.LoadExpirationQueue(s.store, part.ExpirationsEpochs, quant, miner12.PartitionExpirationAmtBitwidth) + if err != nil { + return err + } + var exp miner12.ExpirationSet + return q.ForEach(&exp, func(epoch int64) error { + if early, err := exp.EarlySectors.IsSet(uint64(num)); err != nil { + return err + } else if early { + out.Early = abi.ChainEpoch(epoch) + return nil + } + if onTime, err := exp.OnTimeSectors.IsSet(uint64(num)); err != nil { + return err + } else if onTime { + out.OnTime = abi.ChainEpoch(epoch) + return stopErr + } + return nil + }) + }) + }) + if err == stopErr { + err = nil + } + if err != nil { + return nil, err + } + if out.Early == 0 && out.OnTime == 0 { + return nil, xerrors.Errorf("failed to find sector %d", num) + } + return &out, nil +} + +func (s *state12) GetPrecommittedSector(num abi.SectorNumber) (*SectorPreCommitOnChainInfo, error) { + info, ok, err := s.State.GetPrecommittedSector(s.store, num) + if !ok || err != nil { + return nil, err + } + + ret := fromV12SectorPreCommitOnChainInfo(*info) + + return &ret, nil +} + +func (s *state12) ForEachPrecommittedSector(cb func(SectorPreCommitOnChainInfo) error) error { + precommitted, err := adt12.AsMap(s.store, s.State.PreCommittedSectors, builtin12.DefaultHamtBitwidth) + if err != nil { + return err + } + + var info miner12.SectorPreCommitOnChainInfo + if err := precommitted.ForEach(&info, func(_ string) error { + return cb(fromV12SectorPreCommitOnChainInfo(info)) + }); err != nil { + return err + } + + return nil +} + +func (s *state12) LoadSectors(snos *bitfield.BitField) ([]*SectorOnChainInfo, error) { + sectors, err := miner12.LoadSectors(s.store, s.State.Sectors) + if err != nil { + return nil, err + } + + // If no sector numbers are specified, load all. + if snos == nil { + infos := make([]*SectorOnChainInfo, 0, sectors.Length()) + var info12 miner12.SectorOnChainInfo + if err := sectors.ForEach(&info12, func(_ int64) error { + info := fromV12SectorOnChainInfo(info12) + infos = append(infos, &info) + return nil + }); err != nil { + return nil, err + } + return infos, nil + } + + // Otherwise, load selected. + infos12, err := sectors.Load(*snos) + if err != nil { + return nil, err + } + infos := make([]*SectorOnChainInfo, len(infos12)) + for i, info12 := range infos12 { + info := fromV12SectorOnChainInfo(*info12) + infos[i] = &info + } + return infos, nil +} + +func (s *state12) loadAllocatedSectorNumbers() (bitfield.BitField, error) { + var allocatedSectors bitfield.BitField + err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors) + return allocatedSectors, err +} + +func (s *state12) IsAllocated(num abi.SectorNumber) (bool, error) { + allocatedSectors, err := s.loadAllocatedSectorNumbers() + if err != nil { + return false, err + } + + return allocatedSectors.IsSet(uint64(num)) +} + +func (s *state12) GetProvingPeriodStart() (abi.ChainEpoch, error) { + return s.State.ProvingPeriodStart, nil +} + +func (s *state12) UnallocatedSectorNumbers(count int) ([]abi.SectorNumber, error) { + allocatedSectors, err := s.loadAllocatedSectorNumbers() + if err != nil { + return nil, err + } + + allocatedRuns, err := allocatedSectors.RunIterator() + if err != nil { + return nil, err + } + + unallocatedRuns, err := rle.Subtract( + &rle.RunSliceIterator{Runs: []rle.Run{{Val: true, Len: abi.MaxSectorNumber}}}, + allocatedRuns, + ) + if err != nil { + return nil, err + } + + iter, err := rle.BitsFromRuns(unallocatedRuns) + if err != nil { + return nil, err + } + + sectors := make([]abi.SectorNumber, 0, count) + for iter.HasNext() && len(sectors) < count { + nextNo, err := iter.Next() + if err != nil { + return nil, err + } + sectors = append(sectors, abi.SectorNumber(nextNo)) + } + + return sectors, nil +} + +func (s *state12) GetAllocatedSectors() (*bitfield.BitField, error) { + var allocatedSectors bitfield.BitField + if err := s.store.Get(s.store.Context(), s.State.AllocatedSectors, &allocatedSectors); err != nil { + return nil, err + } + + return &allocatedSectors, nil +} + +func (s *state12) LoadDeadline(idx uint64) (Deadline, error) { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return nil, err + } + dl, err := dls.LoadDeadline(s.store, idx) + if err != nil { + return nil, err + } + return &deadline12{*dl, s.store}, nil +} + +func (s *state12) ForEachDeadline(cb func(uint64, Deadline) error) error { + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return err + } + return dls.ForEach(s.store, func(i uint64, dl *miner12.Deadline) error { + return cb(i, &deadline12{*dl, s.store}) + }) +} + +func (s *state12) NumDeadlines() (uint64, error) { + return miner12.WPoStPeriodDeadlines, nil +} + +func (s *state12) DeadlinesChanged(other State) (bool, error) { + other12, ok := other.(*state12) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + + return !s.State.Deadlines.Equals(other12.Deadlines), nil +} + +func (s *state12) MinerInfoChanged(other State) (bool, error) { + other0, ok := other.(*state12) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Info.Equals(other0.State.Info), nil +} + +func (s *state12) Info() (MinerInfo, error) { + info, err := s.State.GetInfo(s.store) + if err != nil { + return MinerInfo{}, err + } + + mi := MinerInfo{ + Owner: info.Owner, + Worker: info.Worker, + ControlAddresses: info.ControlAddresses, + + PendingWorkerKey: (*WorkerKeyChange)(info.PendingWorkerKey), + + PeerId: info.PeerId, + Multiaddrs: info.Multiaddrs, + WindowPoStProofType: info.WindowPoStProofType, + SectorSize: info.SectorSize, + WindowPoStPartitionSectors: info.WindowPoStPartitionSectors, + ConsensusFaultElapsed: info.ConsensusFaultElapsed, + + Beneficiary: info.Beneficiary, + BeneficiaryTerm: BeneficiaryTerm(info.BeneficiaryTerm), + PendingBeneficiaryTerm: (*PendingBeneficiaryChange)(info.PendingBeneficiaryTerm), + } + + return mi, nil +} + +func (s *state12) DeadlineInfo(epoch abi.ChainEpoch) (*dline.Info, error) { + return s.State.RecordedDeadlineInfo(epoch), nil +} + +func (s *state12) DeadlineCronActive() (bool, error) { + return s.State.DeadlineCronActive, nil +} + +func (s *state12) sectors() (adt.Array, error) { + return adt12.AsArray(s.store, s.Sectors, miner12.SectorsAmtBitwidth) +} + +func (s *state12) decodeSectorOnChainInfo(val *cbg.Deferred) (SectorOnChainInfo, error) { + var si miner12.SectorOnChainInfo + err := si.UnmarshalCBOR(bytes.NewReader(val.Raw)) + if err != nil { + return SectorOnChainInfo{}, err + } + + return fromV12SectorOnChainInfo(si), nil +} + +func (s *state12) precommits() (adt.Map, error) { + return adt12.AsMap(s.store, s.PreCommittedSectors, builtin12.DefaultHamtBitwidth) +} + +func (s *state12) decodeSectorPreCommitOnChainInfo(val *cbg.Deferred) (SectorPreCommitOnChainInfo, error) { + var sp miner12.SectorPreCommitOnChainInfo + err := sp.UnmarshalCBOR(bytes.NewReader(val.Raw)) + if err != nil { + return SectorPreCommitOnChainInfo{}, err + } + + return fromV12SectorPreCommitOnChainInfo(sp), nil +} + +func (s *state12) EraseAllUnproven() error { + + dls, err := s.State.LoadDeadlines(s.store) + if err != nil { + return err + } + + err = dls.ForEach(s.store, func(dindx uint64, dl *miner12.Deadline) error { + ps, err := dl.PartitionsArray(s.store) + if err != nil { + return err + } + + var part miner12.Partition + err = ps.ForEach(&part, func(pindx int64) error { + _ = part.ActivateUnproven() + err = ps.Set(uint64(pindx), &part) + return nil + }) + + if err != nil { + return err + } + + dl.Partitions, err = ps.Root() + if err != nil { + return err + } + + return dls.UpdateDeadline(s.store, dindx, dl) + }) + if err != nil { + return err + } + + return s.State.SaveDeadlines(s.store, dls) + +} + +func (d *deadline12) LoadPartition(idx uint64) (Partition, error) { + p, err := d.Deadline.LoadPartition(d.store, idx) + if err != nil { + return nil, err + } + return &partition12{*p, d.store}, nil +} + +func (d *deadline12) ForEachPartition(cb func(uint64, Partition) error) error { + ps, err := d.Deadline.PartitionsArray(d.store) + if err != nil { + return err + } + var part miner12.Partition + return ps.ForEach(&part, func(i int64) error { + return cb(uint64(i), &partition12{part, d.store}) + }) +} + +func (d *deadline12) PartitionsChanged(other Deadline) (bool, error) { + other12, ok := other.(*deadline12) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + + return !d.Deadline.Partitions.Equals(other12.Deadline.Partitions), nil +} + +func (d *deadline12) PartitionsPoSted() (bitfield.BitField, error) { + return d.Deadline.PartitionsPoSted, nil +} + +func (d *deadline12) DisputableProofCount() (uint64, error) { + + ops, err := d.OptimisticProofsSnapshotArray(d.store) + if err != nil { + return 0, err + } + + return ops.Length(), nil + +} + +func (p *partition12) AllSectors() (bitfield.BitField, error) { + return p.Partition.Sectors, nil +} + +func (p *partition12) FaultySectors() (bitfield.BitField, error) { + return p.Partition.Faults, nil +} + +func (p *partition12) RecoveringSectors() (bitfield.BitField, error) { + return p.Partition.Recoveries, nil +} + +func (p *partition12) UnprovenSectors() (bitfield.BitField, error) { + return p.Partition.Unproven, nil +} + +func fromV12SectorOnChainInfo(v12 miner12.SectorOnChainInfo) SectorOnChainInfo { + info := SectorOnChainInfo{ + SectorNumber: v12.SectorNumber, + SealProof: v12.SealProof, + SealedCID: v12.SealedCID, + DealIDs: v12.DealIDs, + Activation: v12.Activation, + Expiration: v12.Expiration, + DealWeight: v12.DealWeight, + VerifiedDealWeight: v12.VerifiedDealWeight, + InitialPledge: v12.InitialPledge, + ExpectedDayReward: v12.ExpectedDayReward, + ExpectedStoragePledge: v12.ExpectedStoragePledge, + + SectorKeyCID: v12.SectorKeyCID, + } + return info +} + +func fromV12SectorPreCommitOnChainInfo(v12 miner12.SectorPreCommitOnChainInfo) SectorPreCommitOnChainInfo { + ret := SectorPreCommitOnChainInfo{ + Info: SectorPreCommitInfo{ + SealProof: v12.Info.SealProof, + SectorNumber: v12.Info.SectorNumber, + SealedCID: v12.Info.SealedCID, + SealRandEpoch: v12.Info.SealRandEpoch, + DealIDs: v12.Info.DealIDs, + Expiration: v12.Info.Expiration, + UnsealedCid: nil, + }, + PreCommitDeposit: v12.PreCommitDeposit, + PreCommitEpoch: v12.PreCommitEpoch, + } + + ret.Info.UnsealedCid = v12.Info.UnsealedCid + + return ret +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +func (s *state12) ActorKey() string { + return manifest.MinerKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/multisig/message10.go b/chain/actors/builtin/multisig/message10.go index 5f70ea3c1..8f7bb5a6f 100644 --- a/chain/actors/builtin/multisig/message10.go +++ b/chain/actors/builtin/multisig/message10.go @@ -8,7 +8,7 @@ import ( actorstypes "github.com/filecoin-project/go-state-types/actors" builtintypes "github.com/filecoin-project/go-state-types/builtin" multisig10 "github.com/filecoin-project/go-state-types/builtin/v10/multisig" - init11 "github.com/filecoin-project/go-state-types/builtin/v11/init" + init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" "github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/lotus/chain/actors" @@ -57,7 +57,7 @@ func (m message10) Create( } // new actors are created by invoking 'exec' on the init actor with the constructor params - execParams := &init11.ExecParams{ + execParams := &init12.ExecParams{ CodeCID: code, ConstructorParams: enc, } diff --git a/chain/actors/builtin/multisig/message11.go b/chain/actors/builtin/multisig/message11.go index a2c086614..4c7520d5d 100644 --- a/chain/actors/builtin/multisig/message11.go +++ b/chain/actors/builtin/multisig/message11.go @@ -7,8 +7,8 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" builtintypes "github.com/filecoin-project/go-state-types/builtin" - init11 "github.com/filecoin-project/go-state-types/builtin/v11/init" multisig11 "github.com/filecoin-project/go-state-types/builtin/v11/multisig" + init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" "github.com/filecoin-project/go-state-types/manifest" "github.com/filecoin-project/lotus/chain/actors" @@ -57,7 +57,7 @@ func (m message11) Create( } // new actors are created by invoking 'exec' on the init actor with the constructor params - execParams := &init11.ExecParams{ + execParams := &init12.ExecParams{ CodeCID: code, ConstructorParams: enc, } diff --git a/chain/actors/builtin/multisig/message12.go b/chain/actors/builtin/multisig/message12.go new file mode 100644 index 000000000..43658c04b --- /dev/null +++ b/chain/actors/builtin/multisig/message12.go @@ -0,0 +1,77 @@ +package multisig + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + builtintypes "github.com/filecoin-project/go-state-types/builtin" + init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" + multisig12 "github.com/filecoin-project/go-state-types/builtin/v12/multisig" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" + "github.com/filecoin-project/lotus/chain/types" +) + +type message12 struct{ message0 } + +func (m message12) Create( + signers []address.Address, threshold uint64, + unlockStart, unlockDuration abi.ChainEpoch, + initialAmount abi.TokenAmount, +) (*types.Message, error) { + + lenAddrs := uint64(len(signers)) + + if lenAddrs < threshold { + return nil, xerrors.Errorf("cannot require signing of more addresses than provided for multisig") + } + + if threshold == 0 { + threshold = lenAddrs + } + + if m.from == address.Undef { + return nil, xerrors.Errorf("must provide source address") + } + + // Set up constructor parameters for multisig + msigParams := &multisig12.ConstructorParams{ + Signers: signers, + NumApprovalsThreshold: threshold, + UnlockDuration: unlockDuration, + StartEpoch: unlockStart, + } + + enc, actErr := actors.SerializeParams(msigParams) + if actErr != nil { + return nil, actErr + } + + code, ok := actors.GetActorCodeID(actorstypes.Version12, manifest.MultisigKey) + if !ok { + return nil, xerrors.Errorf("failed to get multisig code ID") + } + + // new actors are created by invoking 'exec' on the init actor with the constructor params + execParams := &init12.ExecParams{ + CodeCID: code, + ConstructorParams: enc, + } + + enc, actErr = actors.SerializeParams(execParams) + if actErr != nil { + return nil, actErr + } + + return &types.Message{ + To: init_.Address, + From: m.from, + Method: builtintypes.MethodsInit.Exec, + Params: enc, + Value: initialAmount, + }, nil +} diff --git a/chain/actors/builtin/multisig/message8.go b/chain/actors/builtin/multisig/message8.go index 817d66726..390c94691 100644 --- a/chain/actors/builtin/multisig/message8.go +++ b/chain/actors/builtin/multisig/message8.go @@ -7,7 +7,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" builtintypes "github.com/filecoin-project/go-state-types/builtin" - init11 "github.com/filecoin-project/go-state-types/builtin/v11/init" + init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" multisig8 "github.com/filecoin-project/go-state-types/builtin/v8/multisig" "github.com/filecoin-project/go-state-types/manifest" @@ -57,7 +57,7 @@ func (m message8) Create( } // new actors are created by invoking 'exec' on the init actor with the constructor params - execParams := &init11.ExecParams{ + execParams := &init12.ExecParams{ CodeCID: code, ConstructorParams: enc, } diff --git a/chain/actors/builtin/multisig/message9.go b/chain/actors/builtin/multisig/message9.go index 1472c4e66..907bec7d5 100644 --- a/chain/actors/builtin/multisig/message9.go +++ b/chain/actors/builtin/multisig/message9.go @@ -7,7 +7,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" builtintypes "github.com/filecoin-project/go-state-types/builtin" - init11 "github.com/filecoin-project/go-state-types/builtin/v11/init" + init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" multisig9 "github.com/filecoin-project/go-state-types/builtin/v9/multisig" "github.com/filecoin-project/go-state-types/manifest" @@ -57,7 +57,7 @@ func (m message9) Create( } // new actors are created by invoking 'exec' on the init actor with the constructor params - execParams := &init11.ExecParams{ + execParams := &init12.ExecParams{ CodeCID: code, ConstructorParams: enc, } diff --git a/chain/actors/builtin/multisig/multisig.go b/chain/actors/builtin/multisig/multisig.go index 9ab8fffb5..71a3b7b22 100644 --- a/chain/actors/builtin/multisig/multisig.go +++ b/chain/actors/builtin/multisig/multisig.go @@ -12,7 +12,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" builtintypes "github.com/filecoin-project/go-state-types/builtin" - msig11 "github.com/filecoin-project/go-state-types/builtin/v11/multisig" + msig12 "github.com/filecoin-project/go-state-types/builtin/v12/multisig" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" @@ -48,6 +48,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -115,6 +118,9 @@ func MakeState(store adt.Store, av actorstypes.Version, signers []address.Addres case actorstypes.Version11: return make11(store, signers, threshold, startEpoch, unlockDuration, initialBalance) + case actorstypes.Version12: + return make12(store, signers, threshold, startEpoch, unlockDuration, initialBalance) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -141,7 +147,7 @@ type State interface { GetState() interface{} } -type Transaction = msig11.Transaction +type Transaction = msig12.Transaction var Methods = builtintypes.MethodsMultisig @@ -180,6 +186,9 @@ func Message(version actorstypes.Version, from address.Address) MessageBuilder { case actorstypes.Version11: return message11{message0{from}} + + case actorstypes.Version12: + return message12{message0{from}} default: panic(fmt.Sprintf("unsupported actors version: %d", version)) } @@ -203,13 +212,13 @@ type MessageBuilder interface { } // this type is the same between v0 and v2 -type ProposalHashData = msig11.ProposalHashData -type ProposeReturn = msig11.ProposeReturn -type ProposeParams = msig11.ProposeParams -type ApproveReturn = msig11.ApproveReturn +type ProposalHashData = msig12.ProposalHashData +type ProposeReturn = msig12.ProposeReturn +type ProposeParams = msig12.ProposeParams +type ApproveReturn = msig12.ApproveReturn func txnParams(id uint64, data *ProposalHashData) ([]byte, error) { - params := msig11.TxnIDParams{ID: msig11.TxnID(id)} + params := msig12.TxnIDParams{ID: msig12.TxnID(id)} if data != nil { if data.Requester.Protocol() != address.ID { return nil, xerrors.Errorf("proposer address must be an ID address, was %s", data.Requester) @@ -244,5 +253,6 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/builtin/multisig/v12.go b/chain/actors/builtin/multisig/v12.go new file mode 100644 index 000000000..d3d2f3809 --- /dev/null +++ b/chain/actors/builtin/multisig/v12.go @@ -0,0 +1,138 @@ +package multisig + +import ( + "bytes" + "encoding/binary" + "fmt" + + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + builtin12 "github.com/filecoin-project/go-state-types/builtin" + msig12 "github.com/filecoin-project/go-state-types/builtin/v12/multisig" + adt12 "github.com/filecoin-project/go-state-types/builtin/v12/util/adt" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store, signers []address.Address, threshold uint64, startEpoch abi.ChainEpoch, unlockDuration abi.ChainEpoch, initialBalance abi.TokenAmount) (State, error) { + out := state12{store: store} + out.State = msig12.State{} + out.State.Signers = signers + out.State.NumApprovalsThreshold = threshold + out.State.StartEpoch = startEpoch + out.State.UnlockDuration = unlockDuration + out.State.InitialBalance = initialBalance + + em, err := adt12.StoreEmptyMap(store, builtin12.DefaultHamtBitwidth) + if err != nil { + return nil, err + } + + out.State.PendingTxns = em + + return &out, nil +} + +type state12 struct { + msig12.State + store adt.Store +} + +func (s *state12) LockedBalance(currEpoch abi.ChainEpoch) (abi.TokenAmount, error) { + return s.State.AmountLocked(currEpoch - s.State.StartEpoch), nil +} + +func (s *state12) StartEpoch() (abi.ChainEpoch, error) { + return s.State.StartEpoch, nil +} + +func (s *state12) UnlockDuration() (abi.ChainEpoch, error) { + return s.State.UnlockDuration, nil +} + +func (s *state12) InitialBalance() (abi.TokenAmount, error) { + return s.State.InitialBalance, nil +} + +func (s *state12) Threshold() (uint64, error) { + return s.State.NumApprovalsThreshold, nil +} + +func (s *state12) Signers() ([]address.Address, error) { + return s.State.Signers, nil +} + +func (s *state12) ForEachPendingTxn(cb func(id int64, txn Transaction) error) error { + arr, err := adt12.AsMap(s.store, s.State.PendingTxns, builtin12.DefaultHamtBitwidth) + if err != nil { + return err + } + var out msig12.Transaction + return arr.ForEach(&out, func(key string) error { + txid, n := binary.Varint([]byte(key)) + if n <= 0 { + return xerrors.Errorf("invalid pending transaction key: %v", key) + } + return cb(txid, (Transaction)(out)) //nolint:unconvert + }) +} + +func (s *state12) PendingTxnChanged(other State) (bool, error) { + other12, ok := other.(*state12) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.PendingTxns.Equals(other12.PendingTxns), nil +} + +func (s *state12) transactions() (adt.Map, error) { + return adt12.AsMap(s.store, s.PendingTxns, builtin12.DefaultHamtBitwidth) +} + +func (s *state12) decodeTransaction(val *cbg.Deferred) (Transaction, error) { + var tx msig12.Transaction + if err := tx.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Transaction{}, err + } + return Transaction(tx), nil +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +func (s *state12) ActorKey() string { + return manifest.MultisigKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/paych/message12.go b/chain/actors/builtin/paych/message12.go new file mode 100644 index 000000000..bd821641a --- /dev/null +++ b/chain/actors/builtin/paych/message12.go @@ -0,0 +1,109 @@ +package paych + +import ( + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + builtin12 "github.com/filecoin-project/go-state-types/builtin" + init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" + paych12 "github.com/filecoin-project/go-state-types/builtin/v12/paych" + paychtypes "github.com/filecoin-project/go-state-types/builtin/v8/paych" + + "github.com/filecoin-project/lotus/chain/actors" + init_ "github.com/filecoin-project/lotus/chain/actors/builtin/init" + "github.com/filecoin-project/lotus/chain/types" +) + +type message12 struct{ from address.Address } + +func (m message12) Create(to address.Address, initialAmount abi.TokenAmount) (*types.Message, error) { + + actorCodeID, ok := actors.GetActorCodeID(actorstypes.Version12, "paymentchannel") + if !ok { + return nil, xerrors.Errorf("error getting actor paymentchannel code id for actor version %d", 12) + } + + params, aerr := actors.SerializeParams(&paych12.ConstructorParams{From: m.from, To: to}) + if aerr != nil { + return nil, aerr + } + enc, aerr := actors.SerializeParams(&init12.ExecParams{ + CodeCID: actorCodeID, + ConstructorParams: params, + }) + if aerr != nil { + return nil, aerr + } + + return &types.Message{ + To: init_.Address, + From: m.from, + Value: initialAmount, + Method: builtin12.MethodsInit.Exec, + Params: enc, + }, nil +} + +func (m message12) Update(paych address.Address, sv *paychtypes.SignedVoucher, secret []byte) (*types.Message, error) { + params, aerr := actors.SerializeParams(&paych12.UpdateChannelStateParams{ + + Sv: toV12SignedVoucher(*sv), + + Secret: secret, + }) + if aerr != nil { + return nil, aerr + } + + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin12.MethodsPaych.UpdateChannelState, + Params: params, + }, nil +} + +func toV12SignedVoucher(sv paychtypes.SignedVoucher) paych12.SignedVoucher { + merges := make([]paych12.Merge, len(sv.Merges)) + for i := range sv.Merges { + merges[i] = paych12.Merge{ + Lane: sv.Merges[i].Lane, + Nonce: sv.Merges[i].Nonce, + } + } + + return paych12.SignedVoucher{ + ChannelAddr: sv.ChannelAddr, + TimeLockMin: sv.TimeLockMin, + TimeLockMax: sv.TimeLockMax, + SecretHash: sv.SecretHash, + Extra: (*paych12.ModVerifyParams)(sv.Extra), + Lane: sv.Lane, + Nonce: sv.Nonce, + Amount: sv.Amount, + MinSettleHeight: sv.MinSettleHeight, + Merges: merges, + Signature: sv.Signature, + } +} + +func (m message12) Settle(paych address.Address) (*types.Message, error) { + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin12.MethodsPaych.Settle, + }, nil +} + +func (m message12) Collect(paych address.Address) (*types.Message, error) { + return &types.Message{ + To: paych, + From: m.from, + Value: abi.NewTokenAmount(0), + Method: builtin12.MethodsPaych.Collect, + }, nil +} diff --git a/chain/actors/builtin/paych/paych.go b/chain/actors/builtin/paych/paych.go index ccf48dbce..8a7979e95 100644 --- a/chain/actors/builtin/paych/paych.go +++ b/chain/actors/builtin/paych/paych.go @@ -50,6 +50,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -167,6 +170,9 @@ func Message(version actorstypes.Version, from address.Address) MessageBuilder { case actorstypes.Version11: return message11{from} + case actorstypes.Version12: + return message12{from} + default: panic(fmt.Sprintf("unsupported actors version: %d", version)) } @@ -208,5 +214,6 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/builtin/paych/v12.go b/chain/actors/builtin/paych/v12.go new file mode 100644 index 000000000..5c1330d76 --- /dev/null +++ b/chain/actors/builtin/paych/v12.go @@ -0,0 +1,135 @@ +package paych + +import ( + "fmt" + + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/big" + paych12 "github.com/filecoin-project/go-state-types/builtin/v12/paych" + adt12 "github.com/filecoin-project/go-state-types/builtin/v12/util/adt" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store) (State, error) { + out := state12{store: store} + out.State = paych12.State{} + return &out, nil +} + +type state12 struct { + paych12.State + store adt.Store + lsAmt *adt12.Array +} + +// Channel owner, who has funded the actor +func (s *state12) From() (address.Address, error) { + return s.State.From, nil +} + +// Recipient of payouts from channel +func (s *state12) To() (address.Address, error) { + return s.State.To, nil +} + +// Height at which the channel can be `Collected` +func (s *state12) SettlingAt() (abi.ChainEpoch, error) { + return s.State.SettlingAt, nil +} + +// Amount successfully redeemed through the payment channel, paid out on `Collect()` +func (s *state12) ToSend() (abi.TokenAmount, error) { + return s.State.ToSend, nil +} + +func (s *state12) getOrLoadLsAmt() (*adt12.Array, error) { + if s.lsAmt != nil { + return s.lsAmt, nil + } + + // Get the lane state from the chain + lsamt, err := adt12.AsArray(s.store, s.State.LaneStates, paych12.LaneStatesAmtBitwidth) + if err != nil { + return nil, err + } + + s.lsAmt = lsamt + return lsamt, nil +} + +// Get total number of lanes +func (s *state12) LaneCount() (uint64, error) { + lsamt, err := s.getOrLoadLsAmt() + if err != nil { + return 0, err + } + return lsamt.Length(), nil +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +// Iterate lane states +func (s *state12) ForEachLaneState(cb func(idx uint64, dl LaneState) error) error { + // Get the lane state from the chain + lsamt, err := s.getOrLoadLsAmt() + if err != nil { + return err + } + + // Note: we use a map instead of an array to store laneStates because the + // client sets the lane ID (the index) and potentially they could use a + // very large index. + var ls paych12.LaneState + return lsamt.ForEach(&ls, func(i int64) error { + return cb(uint64(i), &laneState12{ls}) + }) +} + +type laneState12 struct { + paych12.LaneState +} + +func (ls *laneState12) Redeemed() (big.Int, error) { + return ls.LaneState.Redeemed, nil +} + +func (ls *laneState12) Nonce() (uint64, error) { + return ls.LaneState.Nonce, nil +} + +func (s *state12) ActorKey() string { + return manifest.PaychKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/power/power.go b/chain/actors/builtin/power/power.go index f3bcef5bb..9b64ded38 100644 --- a/chain/actors/builtin/power/power.go +++ b/chain/actors/builtin/power/power.go @@ -9,7 +9,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" - builtin11 "github.com/filecoin-project/go-state-types/builtin" + builtin12 "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" @@ -27,8 +27,8 @@ import ( ) var ( - Address = builtin11.StoragePowerActorAddr - Methods = builtin11.MethodsPower + Address = builtin12.StoragePowerActorAddr + Methods = builtin12.MethodsPower ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -51,6 +51,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -118,6 +121,9 @@ func MakeState(store adt.Store, av actorstypes.Version) (State, error) { case actorstypes.Version11: return make11(store) + case actorstypes.Version12: + return make12(store) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -183,5 +189,6 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/builtin/power/v12.go b/chain/actors/builtin/power/v12.go new file mode 100644 index 000000000..2e9109022 --- /dev/null +++ b/chain/actors/builtin/power/v12.go @@ -0,0 +1,207 @@ +package power + +import ( + "bytes" + "fmt" + + "github.com/ipfs/go-cid" + cbg "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + builtin12 "github.com/filecoin-project/go-state-types/builtin" + power12 "github.com/filecoin-project/go-state-types/builtin/v12/power" + adt12 "github.com/filecoin-project/go-state-types/builtin/v12/util/adt" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store) (State, error) { + out := state12{store: store} + + s, err := power12.ConstructState(store) + if err != nil { + return nil, err + } + + out.State = *s + + return &out, nil +} + +type state12 struct { + power12.State + store adt.Store +} + +func (s *state12) TotalLocked() (abi.TokenAmount, error) { + return s.TotalPledgeCollateral, nil +} + +func (s *state12) TotalPower() (Claim, error) { + return Claim{ + RawBytePower: s.TotalRawBytePower, + QualityAdjPower: s.TotalQualityAdjPower, + }, nil +} + +// Committed power to the network. Includes miners below the minimum threshold. +func (s *state12) TotalCommitted() (Claim, error) { + return Claim{ + RawBytePower: s.TotalBytesCommitted, + QualityAdjPower: s.TotalQABytesCommitted, + }, nil +} + +func (s *state12) MinerPower(addr address.Address) (Claim, bool, error) { + claims, err := s.claims() + if err != nil { + return Claim{}, false, err + } + var claim power12.Claim + ok, err := claims.Get(abi.AddrKey(addr), &claim) + if err != nil { + return Claim{}, false, err + } + return Claim{ + RawBytePower: claim.RawBytePower, + QualityAdjPower: claim.QualityAdjPower, + }, ok, nil +} + +func (s *state12) MinerNominalPowerMeetsConsensusMinimum(a address.Address) (bool, error) { + return s.State.MinerNominalPowerMeetsConsensusMinimum(s.store, a) +} + +func (s *state12) TotalPowerSmoothed() (builtin.FilterEstimate, error) { + return builtin.FilterEstimate(s.State.ThisEpochQAPowerSmoothed), nil +} + +func (s *state12) MinerCounts() (uint64, uint64, error) { + return uint64(s.State.MinerAboveMinPowerCount), uint64(s.State.MinerCount), nil +} + +func (s *state12) ListAllMiners() ([]address.Address, error) { + claims, err := s.claims() + if err != nil { + return nil, err + } + + var miners []address.Address + err = claims.ForEach(nil, func(k string) error { + a, err := address.NewFromBytes([]byte(k)) + if err != nil { + return err + } + miners = append(miners, a) + return nil + }) + if err != nil { + return nil, err + } + + return miners, nil +} + +func (s *state12) ForEachClaim(cb func(miner address.Address, claim Claim) error) error { + claims, err := s.claims() + if err != nil { + return err + } + + var claim power12.Claim + return claims.ForEach(&claim, func(k string) error { + a, err := address.NewFromBytes([]byte(k)) + if err != nil { + return err + } + return cb(a, Claim{ + RawBytePower: claim.RawBytePower, + QualityAdjPower: claim.QualityAdjPower, + }) + }) +} + +func (s *state12) ClaimsChanged(other State) (bool, error) { + other12, ok := other.(*state12) + if !ok { + // treat an upgrade as a change, always + return true, nil + } + return !s.State.Claims.Equals(other12.State.Claims), nil +} + +func (s *state12) SetTotalQualityAdjPower(p abi.StoragePower) error { + s.State.TotalQualityAdjPower = p + return nil +} + +func (s *state12) SetTotalRawBytePower(p abi.StoragePower) error { + s.State.TotalRawBytePower = p + return nil +} + +func (s *state12) SetThisEpochQualityAdjPower(p abi.StoragePower) error { + s.State.ThisEpochQualityAdjPower = p + return nil +} + +func (s *state12) SetThisEpochRawBytePower(p abi.StoragePower) error { + s.State.ThisEpochRawBytePower = p + return nil +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +func (s *state12) claims() (adt.Map, error) { + return adt12.AsMap(s.store, s.Claims, builtin12.DefaultHamtBitwidth) +} + +func (s *state12) decodeClaim(val *cbg.Deferred) (Claim, error) { + var ci power12.Claim + if err := ci.UnmarshalCBOR(bytes.NewReader(val.Raw)); err != nil { + return Claim{}, err + } + return fromV12Claim(ci), nil +} + +func fromV12Claim(v12 power12.Claim) Claim { + return Claim{ + RawBytePower: v12.RawBytePower, + QualityAdjPower: v12.QualityAdjPower, + } +} + +func (s *state12) ActorKey() string { + return manifest.PowerKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/registry.go b/chain/actors/builtin/registry.go index 4addbd451..6ba5fef03 100644 --- a/chain/actors/builtin/registry.go +++ b/chain/actors/builtin/registry.go @@ -42,6 +42,22 @@ import ( reward11 "github.com/filecoin-project/go-state-types/builtin/v11/reward" system11 "github.com/filecoin-project/go-state-types/builtin/v11/system" verifreg11 "github.com/filecoin-project/go-state-types/builtin/v11/verifreg" + account12 "github.com/filecoin-project/go-state-types/builtin/v12/account" + cron12 "github.com/filecoin-project/go-state-types/builtin/v12/cron" + datacap12 "github.com/filecoin-project/go-state-types/builtin/v12/datacap" + eam12 "github.com/filecoin-project/go-state-types/builtin/v12/eam" + ethaccount12 "github.com/filecoin-project/go-state-types/builtin/v12/ethaccount" + evm12 "github.com/filecoin-project/go-state-types/builtin/v12/evm" + _init12 "github.com/filecoin-project/go-state-types/builtin/v12/init" + market12 "github.com/filecoin-project/go-state-types/builtin/v12/market" + miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" + multisig12 "github.com/filecoin-project/go-state-types/builtin/v12/multisig" + paych12 "github.com/filecoin-project/go-state-types/builtin/v12/paych" + placeholder12 "github.com/filecoin-project/go-state-types/builtin/v12/placeholder" + power12 "github.com/filecoin-project/go-state-types/builtin/v12/power" + reward12 "github.com/filecoin-project/go-state-types/builtin/v12/reward" + system12 "github.com/filecoin-project/go-state-types/builtin/v12/system" + verifreg12 "github.com/filecoin-project/go-state-types/builtin/v12/verifreg" account8 "github.com/filecoin-project/go-state-types/builtin/v8/account" cron8 "github.com/filecoin-project/go-state-types/builtin/v8/cron" _init8 "github.com/filecoin-project/go-state-types/builtin/v8/init" @@ -497,6 +513,110 @@ func MakeRegistry(av actorstypes.Version) []RegistryEntry { } } + case actorstypes.Version12: + for key, codeID := range codeIDs { + switch key { + case manifest.AccountKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: account12.Methods, + state: new(account12.State), + }) + case manifest.CronKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: cron12.Methods, + state: new(cron12.State), + }) + case manifest.InitKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: _init12.Methods, + state: new(_init12.State), + }) + case manifest.MarketKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: market12.Methods, + state: new(market12.State), + }) + case manifest.MinerKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: miner12.Methods, + state: new(miner12.State), + }) + case manifest.MultisigKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: multisig12.Methods, + state: new(multisig12.State), + }) + case manifest.PaychKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: paych12.Methods, + state: new(paych12.State), + }) + case manifest.PowerKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: power12.Methods, + state: new(power12.State), + }) + case manifest.RewardKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: reward12.Methods, + state: new(reward12.State), + }) + case manifest.SystemKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: system12.Methods, + state: new(system12.State), + }) + case manifest.VerifregKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: verifreg12.Methods, + state: new(verifreg12.State), + }) + case manifest.DatacapKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: datacap12.Methods, + state: new(datacap12.State), + }) + + case manifest.EvmKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: evm12.Methods, + state: new(evm12.State), + }) + case manifest.EamKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: eam12.Methods, + state: nil, + }) + case manifest.PlaceholderKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: placeholder12.Methods, + state: nil, + }) + case manifest.EthAccountKey: + registry = append(registry, RegistryEntry{ + code: codeID, + methods: ethaccount12.Methods, + state: nil, + }) + + } + } + default: panic("expected version v8 and up only, use specs-actors for v0-7") } diff --git a/chain/actors/builtin/reward/reward.go b/chain/actors/builtin/reward/reward.go index b0060a217..3c6463645 100644 --- a/chain/actors/builtin/reward/reward.go +++ b/chain/actors/builtin/reward/reward.go @@ -6,7 +6,7 @@ import ( "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" - builtin11 "github.com/filecoin-project/go-state-types/builtin" + builtin12 "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" @@ -25,8 +25,8 @@ import ( ) var ( - Address = builtin11.RewardActorAddr - Methods = builtin11.MethodsReward + Address = builtin12.RewardActorAddr + Methods = builtin12.MethodsReward ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -49,6 +49,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -116,6 +119,9 @@ func MakeState(store adt.Store, av actorstypes.Version, currRealizedPower abi.St case actorstypes.Version11: return make11(store, currRealizedPower) + case actorstypes.Version12: + return make12(store, currRealizedPower) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -159,5 +165,6 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/builtin/reward/v12.go b/chain/actors/builtin/reward/v12.go new file mode 100644 index 000000000..ecc8ff5a0 --- /dev/null +++ b/chain/actors/builtin/reward/v12.go @@ -0,0 +1,120 @@ +package reward + +import ( + "fmt" + + "github.com/ipfs/go-cid" + + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" + reward12 "github.com/filecoin-project/go-state-types/builtin/v12/reward" + smoothing12 "github.com/filecoin-project/go-state-types/builtin/v12/util/smoothing" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" + "github.com/filecoin-project/lotus/chain/actors/builtin" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store, currRealizedPower abi.StoragePower) (State, error) { + out := state12{store: store} + out.State = *reward12.ConstructState(currRealizedPower) + return &out, nil +} + +type state12 struct { + reward12.State + store adt.Store +} + +func (s *state12) ThisEpochReward() (abi.TokenAmount, error) { + return s.State.ThisEpochReward, nil +} + +func (s *state12) ThisEpochRewardSmoothed() (builtin.FilterEstimate, error) { + + return builtin.FilterEstimate{ + PositionEstimate: s.State.ThisEpochRewardSmoothed.PositionEstimate, + VelocityEstimate: s.State.ThisEpochRewardSmoothed.VelocityEstimate, + }, nil + +} + +func (s *state12) ThisEpochBaselinePower() (abi.StoragePower, error) { + return s.State.ThisEpochBaselinePower, nil +} + +func (s *state12) TotalStoragePowerReward() (abi.TokenAmount, error) { + return s.State.TotalStoragePowerReward, nil +} + +func (s *state12) EffectiveBaselinePower() (abi.StoragePower, error) { + return s.State.EffectiveBaselinePower, nil +} + +func (s *state12) EffectiveNetworkTime() (abi.ChainEpoch, error) { + return s.State.EffectiveNetworkTime, nil +} + +func (s *state12) CumsumBaseline() (reward12.Spacetime, error) { + return s.State.CumsumBaseline, nil +} + +func (s *state12) CumsumRealized() (reward12.Spacetime, error) { + return s.State.CumsumRealized, nil +} + +func (s *state12) InitialPledgeForPower(qaPower abi.StoragePower, networkTotalPledge abi.TokenAmount, networkQAPower *builtin.FilterEstimate, circSupply abi.TokenAmount) (abi.TokenAmount, error) { + return miner12.InitialPledgeForPower( + qaPower, + s.State.ThisEpochBaselinePower, + s.State.ThisEpochRewardSmoothed, + smoothing12.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + circSupply, + ), nil +} + +func (s *state12) PreCommitDepositForPower(networkQAPower builtin.FilterEstimate, sectorWeight abi.StoragePower) (abi.TokenAmount, error) { + return miner12.PreCommitDepositForPower(s.State.ThisEpochRewardSmoothed, + smoothing12.FilterEstimate{ + PositionEstimate: networkQAPower.PositionEstimate, + VelocityEstimate: networkQAPower.VelocityEstimate, + }, + sectorWeight), nil +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +func (s *state12) ActorKey() string { + return manifest.RewardKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/system/system.go b/chain/actors/builtin/system/system.go index 4db8db610..2a2b703bb 100644 --- a/chain/actors/builtin/system/system.go +++ b/chain/actors/builtin/system/system.go @@ -5,7 +5,7 @@ import ( "golang.org/x/xerrors" actorstypes "github.com/filecoin-project/go-state-types/actors" - builtin11 "github.com/filecoin-project/go-state-types/builtin" + builtin12 "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/manifest" builtin0 "github.com/filecoin-project/specs-actors/actors/builtin" builtin2 "github.com/filecoin-project/specs-actors/v2/actors/builtin" @@ -21,7 +21,7 @@ import ( ) var ( - Address = builtin11.SystemActorAddr + Address = builtin12.SystemActorAddr ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -44,6 +44,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -111,6 +114,9 @@ func MakeState(store adt.Store, av actorstypes.Version, builtinActors cid.Cid) ( case actorstypes.Version11: return make11(store, builtinActors) + case actorstypes.Version12: + return make12(store, builtinActors) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -138,5 +144,6 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/builtin/system/v12.go b/chain/actors/builtin/system/v12.go new file mode 100644 index 000000000..71938e799 --- /dev/null +++ b/chain/actors/builtin/system/v12.go @@ -0,0 +1,72 @@ +package system + +import ( + "fmt" + + "github.com/ipfs/go-cid" + + actorstypes "github.com/filecoin-project/go-state-types/actors" + system12 "github.com/filecoin-project/go-state-types/builtin/v12/system" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store, builtinActors cid.Cid) (State, error) { + out := state12{store: store} + out.State = system12.State{ + BuiltinActors: builtinActors, + } + return &out, nil +} + +type state12 struct { + system12.State + store adt.Store +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +func (s *state12) GetBuiltinActors() cid.Cid { + + return s.State.BuiltinActors + +} + +func (s *state12) SetBuiltinActors(c cid.Cid) error { + + s.State.BuiltinActors = c + return nil + +} + +func (s *state12) ActorKey() string { + return manifest.SystemKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/verifreg/v12.go b/chain/actors/builtin/verifreg/v12.go new file mode 100644 index 000000000..98b5c9add --- /dev/null +++ b/chain/actors/builtin/verifreg/v12.go @@ -0,0 +1,152 @@ +package verifreg + +import ( + "fmt" + + "github.com/ipfs/go-cid" + "golang.org/x/xerrors" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/big" + builtin12 "github.com/filecoin-project/go-state-types/builtin" + adt12 "github.com/filecoin-project/go-state-types/builtin/v12/util/adt" + verifreg12 "github.com/filecoin-project/go-state-types/builtin/v12/verifreg" + verifreg9 "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/chain/actors/adt" +) + +var _ State = (*state12)(nil) + +func load12(store adt.Store, root cid.Cid) (State, error) { + out := state12{store: store} + err := store.Get(store.Context(), root, &out) + if err != nil { + return nil, err + } + return &out, nil +} + +func make12(store adt.Store, rootKeyAddress address.Address) (State, error) { + out := state12{store: store} + + s, err := verifreg12.ConstructState(store, rootKeyAddress) + if err != nil { + return nil, err + } + + out.State = *s + + return &out, nil +} + +type state12 struct { + verifreg12.State + store adt.Store +} + +func (s *state12) RootKey() (address.Address, error) { + return s.State.RootKey, nil +} + +func (s *state12) VerifiedClientDataCap(addr address.Address) (bool, abi.StoragePower, error) { + + return false, big.Zero(), xerrors.Errorf("unsupported in actors v12") + +} + +func (s *state12) VerifierDataCap(addr address.Address) (bool, abi.StoragePower, error) { + return getDataCap(s.store, actors.Version12, s.verifiers, addr) +} + +func (s *state12) RemoveDataCapProposalID(verifier address.Address, client address.Address) (bool, uint64, error) { + return getRemoveDataCapProposalID(s.store, actors.Version12, s.removeDataCapProposalIDs, verifier, client) +} + +func (s *state12) ForEachVerifier(cb func(addr address.Address, dcap abi.StoragePower) error) error { + return forEachCap(s.store, actors.Version12, s.verifiers, cb) +} + +func (s *state12) ForEachClient(cb func(addr address.Address, dcap abi.StoragePower) error) error { + + return xerrors.Errorf("unsupported in actors v12") + +} + +func (s *state12) verifiedClients() (adt.Map, error) { + + return nil, xerrors.Errorf("unsupported in actors v12") + +} + +func (s *state12) verifiers() (adt.Map, error) { + return adt12.AsMap(s.store, s.Verifiers, builtin12.DefaultHamtBitwidth) +} + +func (s *state12) removeDataCapProposalIDs() (adt.Map, error) { + return adt12.AsMap(s.store, s.RemoveDataCapProposalIDs, builtin12.DefaultHamtBitwidth) +} + +func (s *state12) GetState() interface{} { + return &s.State +} + +func (s *state12) GetAllocation(clientIdAddr address.Address, allocationId verifreg9.AllocationId) (*Allocation, bool, error) { + + alloc, ok, err := s.FindAllocation(s.store, clientIdAddr, verifreg12.AllocationId(allocationId)) + return (*Allocation)(alloc), ok, err +} + +func (s *state12) GetAllocations(clientIdAddr address.Address) (map[AllocationId]Allocation, error) { + + v12Map, err := s.LoadAllocationsToMap(s.store, clientIdAddr) + + retMap := make(map[AllocationId]Allocation, len(v12Map)) + for k, v := range v12Map { + retMap[AllocationId(k)] = Allocation(v) + } + + return retMap, err + +} + +func (s *state12) GetClaim(providerIdAddr address.Address, claimId verifreg9.ClaimId) (*Claim, bool, error) { + + claim, ok, err := s.FindClaim(s.store, providerIdAddr, verifreg12.ClaimId(claimId)) + return (*Claim)(claim), ok, err + +} + +func (s *state12) GetClaims(providerIdAddr address.Address) (map[ClaimId]Claim, error) { + + v12Map, err := s.LoadClaimsToMap(s.store, providerIdAddr) + + retMap := make(map[ClaimId]Claim, len(v12Map)) + for k, v := range v12Map { + retMap[ClaimId(k)] = Claim(v) + } + + return retMap, err + +} + +func (s *state12) ActorKey() string { + return manifest.VerifregKey +} + +func (s *state12) ActorVersion() actorstypes.Version { + return actorstypes.Version12 +} + +func (s *state12) Code() cid.Cid { + code, ok := actors.GetActorCodeID(s.ActorVersion(), s.ActorKey()) + if !ok { + panic(fmt.Errorf("didn't find actor %v code id for actor version %d", s.ActorKey(), s.ActorVersion())) + } + + return code +} diff --git a/chain/actors/builtin/verifreg/verifreg.go b/chain/actors/builtin/verifreg/verifreg.go index 678a776bd..c5ece39da 100644 --- a/chain/actors/builtin/verifreg/verifreg.go +++ b/chain/actors/builtin/verifreg/verifreg.go @@ -7,7 +7,7 @@ import ( "github.com/filecoin-project/go-address" "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" - builtin11 "github.com/filecoin-project/go-state-types/builtin" + builtin12 "github.com/filecoin-project/go-state-types/builtin" verifregtypes "github.com/filecoin-project/go-state-types/builtin/v9/verifreg" "github.com/filecoin-project/go-state-types/cbor" "github.com/filecoin-project/go-state-types/manifest" @@ -25,8 +25,8 @@ import ( ) var ( - Address = builtin11.VerifiedRegistryActorAddr - Methods = builtin11.MethodsVerifiedRegistry + Address = builtin12.VerifiedRegistryActorAddr + Methods = builtin12.MethodsVerifiedRegistry ) func Load(store adt.Store, act *types.Actor) (State, error) { @@ -49,6 +49,9 @@ func Load(store adt.Store, act *types.Actor) (State, error) { case actorstypes.Version11: return load11(store, act.Head) + case actorstypes.Version12: + return load12(store, act.Head) + } } @@ -116,6 +119,9 @@ func MakeState(store adt.Store, av actorstypes.Version, rootKeyAddress address.A case actorstypes.Version11: return make11(store, rootKeyAddress) + case actorstypes.Version12: + return make12(store, rootKeyAddress) + } return nil, xerrors.Errorf("unknown actor version %d", av) } @@ -153,6 +159,7 @@ func AllCodes() []cid.Cid { (&state9{}).Code(), (&state10{}).Code(), (&state11{}).Code(), + (&state12{}).Code(), } } diff --git a/chain/actors/policy/policy.go b/chain/actors/policy/policy.go index 4b90c46a0..e4c50246f 100644 --- a/chain/actors/policy/policy.go +++ b/chain/actors/policy/policy.go @@ -8,6 +8,7 @@ import ( "github.com/filecoin-project/go-state-types/big" builtin10 "github.com/filecoin-project/go-state-types/builtin" builtin11 "github.com/filecoin-project/go-state-types/builtin" + builtin12 "github.com/filecoin-project/go-state-types/builtin" builtin8 "github.com/filecoin-project/go-state-types/builtin" builtin9 "github.com/filecoin-project/go-state-types/builtin" market10 "github.com/filecoin-project/go-state-types/builtin/v10/market" @@ -15,8 +16,11 @@ import ( verifreg10 "github.com/filecoin-project/go-state-types/builtin/v10/verifreg" market11 "github.com/filecoin-project/go-state-types/builtin/v11/market" miner11 "github.com/filecoin-project/go-state-types/builtin/v11/miner" - paych11 "github.com/filecoin-project/go-state-types/builtin/v11/paych" verifreg11 "github.com/filecoin-project/go-state-types/builtin/v11/verifreg" + market12 "github.com/filecoin-project/go-state-types/builtin/v12/market" + miner12 "github.com/filecoin-project/go-state-types/builtin/v12/miner" + paych12 "github.com/filecoin-project/go-state-types/builtin/v12/paych" + verifreg12 "github.com/filecoin-project/go-state-types/builtin/v12/verifreg" market8 "github.com/filecoin-project/go-state-types/builtin/v8/market" miner8 "github.com/filecoin-project/go-state-types/builtin/v8/miner" verifreg8 "github.com/filecoin-project/go-state-types/builtin/v8/verifreg" @@ -55,10 +59,10 @@ import ( ) const ( - ChainFinality = miner11.ChainFinality + ChainFinality = miner12.ChainFinality SealRandomnessLookback = ChainFinality - PaychSettleDelay = paych11.SettleDelay - MaxPreCommitRandomnessLookback = builtin11.EpochsInDay + SealRandomnessLookback + PaychSettleDelay = paych12.SettleDelay + MaxPreCommitRandomnessLookback = builtin12.EpochsInDay + SealRandomnessLookback ) // SetSupportedProofTypes sets supported proof types, across all actor versions. @@ -171,11 +175,13 @@ func SetPreCommitChallengeDelay(delay abi.ChainEpoch) { miner11.PreCommitChallengeDelay = delay + miner12.PreCommitChallengeDelay = delay + } // TODO: this function shouldn't really exist. Instead, the API should expose the precommit delay. func GetPreCommitChallengeDelay() abi.ChainEpoch { - return miner11.PreCommitChallengeDelay + return miner12.PreCommitChallengeDelay } // SetConsensusMinerMinPower sets the minimum power of an individual miner must @@ -225,6 +231,10 @@ func SetConsensusMinerMinPower(p abi.StoragePower) { policy.ConsensusMinerMinPower = p } + for _, policy := range builtin12.PoStProofPolicies { + policy.ConsensusMinerMinPower = p + } + } // SetMinVerifiedDealSize sets the minimum size of a verified deal. This should @@ -253,6 +263,8 @@ func SetMinVerifiedDealSize(size abi.StoragePower) { verifreg11.MinVerifiedDealSize = size + verifreg12.MinVerifiedDealSize = size + } func GetMaxProveCommitDuration(ver actorstypes.Version, t abi.RegisteredSealProof) (abi.ChainEpoch, error) { @@ -302,6 +314,10 @@ func GetMaxProveCommitDuration(ver actorstypes.Version, t abi.RegisteredSealProo return miner11.MaxProveCommitDuration[t], nil + case actorstypes.Version12: + + return miner12.MaxProveCommitDuration[t], nil + default: return 0, xerrors.Errorf("unsupported actors version") } @@ -362,6 +378,11 @@ func SetProviderCollateralSupplyTarget(num, denom big.Int) { Denominator: denom, } + market12.ProviderCollateralSupplyTarget = builtin12.BigFrac{ + Numerator: num, + Denominator: denom, + } + } func DealProviderCollateralBounds( @@ -430,13 +451,18 @@ func DealProviderCollateralBounds( min, max := market11.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) return min, max, nil + case actorstypes.Version12: + + min, max := market12.DealProviderCollateralBounds(size, verified, rawBytePower, qaPower, baselinePower, circulatingFil) + return min, max, nil + default: return big.Zero(), big.Zero(), xerrors.Errorf("unsupported actors version") } } func DealDurationBounds(pieceSize abi.PaddedPieceSize) (min, max abi.ChainEpoch) { - return market11.DealDurationBounds(pieceSize) + return market12.DealDurationBounds(pieceSize) } // Sets the challenge window and scales the proving period to match (such that @@ -512,6 +538,13 @@ func SetWPoStChallengeWindow(period abi.ChainEpoch) { // scale it if we're scaling the challenge period. miner11.WPoStDisputeWindow = period * 30 + miner12.WPoStChallengeWindow = period + miner12.WPoStProvingPeriod = period * abi.ChainEpoch(miner12.WPoStPeriodDeadlines) + + // by default, this is 2x finality which is 30 periods. + // scale it if we're scaling the challenge period. + miner12.WPoStDisputeWindow = period * 30 + } func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { @@ -524,15 +557,15 @@ func GetWinningPoStSectorSetLookback(nwVer network.Version) abi.ChainEpoch { } func GetMaxSectorExpirationExtension() abi.ChainEpoch { - return miner11.MaxSectorExpirationExtension + return miner12.MaxSectorExpirationExtension } func GetMinSectorExpiration() abi.ChainEpoch { - return miner11.MinSectorExpiration + return miner12.MinSectorExpiration } func GetMaxPoStPartitions(nv network.Version, p abi.RegisteredPoStProof) (int, error) { - sectorsPerPart, err := builtin11.PoStProofWindowPoStPartitionSectors(p) + sectorsPerPart, err := builtin12.PoStProofWindowPoStPartitionSectors(p) if err != nil { return 0, err } @@ -552,7 +585,7 @@ func GetSectorMaxLifetime(proof abi.RegisteredSealProof, nwVer network.Version) return builtin4.SealProofPoliciesV0[proof].SectorMaxLifetime } - return builtin11.SealProofPoliciesV11[proof].SectorMaxLifetime + return builtin12.SealProofPoliciesV11[proof].SectorMaxLifetime } func GetAddressedSectorsMax(nwVer network.Version) (int, error) { @@ -595,6 +628,9 @@ func GetAddressedSectorsMax(nwVer network.Version) (int, error) { case actorstypes.Version11: return miner11.AddressedSectorsMax, nil + case actorstypes.Version12: + return miner12.AddressedSectorsMax, nil + default: return 0, xerrors.Errorf("unsupported network version") } @@ -652,6 +688,10 @@ func GetDeclarationsMax(nwVer network.Version) (int, error) { return miner11.DeclarationsMax, nil + case actorstypes.Version12: + + return miner12.DeclarationsMax, nil + default: return 0, xerrors.Errorf("unsupported network version") } @@ -708,6 +748,10 @@ func AggregateProveCommitNetworkFee(nwVer network.Version, aggregateSize int, ba return miner11.AggregateProveCommitNetworkFee(aggregateSize, baseFee), nil + case actorstypes.Version12: + + return miner12.AggregateProveCommitNetworkFee(aggregateSize, baseFee), nil + default: return big.Zero(), xerrors.Errorf("unsupported network version") } @@ -764,6 +808,10 @@ func AggregatePreCommitNetworkFee(nwVer network.Version, aggregateSize int, base return miner11.AggregatePreCommitNetworkFee(aggregateSize, baseFee), nil + case actorstypes.Version12: + + return miner12.AggregatePreCommitNetworkFee(aggregateSize, baseFee), nil + default: return big.Zero(), xerrors.Errorf("unsupported network version") } diff --git a/chain/actors/version.go b/chain/actors/version.go index 3a5b935bf..92c0da006 100644 --- a/chain/actors/version.go +++ b/chain/actors/version.go @@ -14,9 +14,9 @@ const ({{range .actorVersions}} /* inline-gen start */ -var LatestVersion = 11 +var LatestVersion = 12 -var Versions = []int{0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11} +var Versions = []int{0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12} const ( Version0 Version = 0 @@ -30,6 +30,7 @@ const ( Version9 Version = 9 Version10 Version = 10 Version11 Version = 11 + Version12 Version = 12 ) /* inline-gen end */ diff --git a/chain/consensus/filcns/compute_state.go b/chain/consensus/filcns/compute_state.go index a2956636f..5bafb981d 100644 --- a/chain/consensus/filcns/compute_state.go +++ b/chain/consensus/filcns/compute_state.go @@ -5,11 +5,13 @@ import ( "sync/atomic" "github.com/ipfs/go-cid" + cbor "github.com/ipfs/go-ipld-cbor" cbg "github.com/whyrusleeping/cbor-gen" "go.opencensus.io/stats" "go.opencensus.io/trace" "golang.org/x/xerrors" + amt4 "github.com/filecoin-project/go-amt-ipld/v4" "github.com/filecoin-project/go-state-types/abi" actorstypes "github.com/filecoin-project/go-state-types/actors" "github.com/filecoin-project/go-state-types/big" @@ -48,8 +50,11 @@ func NewActorRegistry() *vm.ActorRegistry { inv.Register(actorstypes.Version7, vm.ActorsVersionPredicate(actorstypes.Version7), builtin.MakeRegistryLegacy(exported7.BuiltinActors())) inv.Register(actorstypes.Version8, vm.ActorsVersionPredicate(actorstypes.Version8), builtin.MakeRegistry(actorstypes.Version8)) inv.Register(actorstypes.Version9, vm.ActorsVersionPredicate(actorstypes.Version9), builtin.MakeRegistry(actorstypes.Version9)) + + // Hyperspace started with actors v10. inv.Register(actorstypes.Version10, vm.ActorsVersionPredicate(actorstypes.Version10), builtin.MakeRegistry(actorstypes.Version10)) inv.Register(actorstypes.Version11, vm.ActorsVersionPredicate(actorstypes.Version11), builtin.MakeRegistry(actorstypes.Version11)) + inv.Register(actorstypes.Version12, vm.ActorsVersionPredicate(actorstypes.Version12), builtin.MakeRegistry(actorstypes.Version12)) return inv } @@ -90,11 +95,11 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, }() ctx = blockstore.WithHotView(ctx) - makeVmWithBaseStateAndEpoch := func(base cid.Cid, e abi.ChainEpoch) (vm.Interface, error) { + makeVm := func(base cid.Cid, e abi.ChainEpoch, timestamp uint64) (vm.Interface, error) { vmopt := &vm.VMOpts{ StateBase: base, Epoch: e, - Timestamp: ts.MinTimestamp(), + Timestamp: timestamp, Rand: r, Bstore: sm.ChainStore().StateBlockstore(), Actors: NewActorRegistry(), @@ -105,6 +110,7 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, LookbackState: stmgr.LookbackStateGetterForTipset(sm, ts), TipSetGetter: stmgr.TipSetGetterForTipset(sm.ChainStore(), ts), Tracing: vmTracing, + ReturnEvents: sm.ChainStore().IsStoringEvents(), } return sm.VMConstructor()(ctx, vmopt) @@ -139,10 +145,22 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, return nil } + // May get filled with the genesis block header if there are null rounds + // for which to backfill cron execution. + var genesis *types.BlockHeader + + // There were null rounds in between the current epoch and the parent epoch. for i := parentEpoch; i < epoch; i++ { var err error if i > parentEpoch { - vmCron, err := makeVmWithBaseStateAndEpoch(pstate, i) + if genesis == nil { + if genesis, err = sm.ChainStore().GetGenesis(ctx); err != nil { + return cid.Undef, cid.Undef, xerrors.Errorf("failed to get genesis when backfilling null rounds: %w", err) + } + } + + ts := genesis.Timestamp + build.BlockDelaySecs*(uint64(i)) + vmCron, err := makeVm(pstate, i, ts) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("making cron vm: %w", err) } @@ -169,13 +187,18 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, partDone() partDone = metrics.Timer(ctx, metrics.VMApplyMessages) - vmi, err := makeVmWithBaseStateAndEpoch(pstate, epoch) + vmi, err := makeVm(pstate, epoch, ts.MinTimestamp()) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("making vm: %w", err) } - var receipts []cbg.CBORMarshaler - processedMsgs := make(map[cid.Cid]struct{}) + var ( + receipts []*types.MessageReceipt + storingEvents = sm.ChainStore().IsStoringEvents() + events [][]cbg.CBORMarshaler + processedMsgs = make(map[cid.Cid]struct{}) + ) + for _, b := range bms { penalty := types.NewInt(0) gasReward := big.Zero() @@ -194,6 +217,11 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, gasReward = big.Add(gasReward, r.GasCosts.MinerTip) penalty = big.Add(penalty, r.GasCosts.MinerPenalty) + if storingEvents { + // Appends nil when no events are returned to preserve positional alignment. + events = append(events, r.Events) + } + if em != nil { if err := em.MessageApplied(ctx, ts, cm.Cid(), m, r, false); err != nil { return cid.Undef, cid.Undef, err @@ -259,6 +287,26 @@ func (t *TipSetExecutor) ApplyBlocks(ctx context.Context, return cid.Undef, cid.Undef, xerrors.Errorf("failed to build receipts amt: %w", err) } + cst := cbor.NewCborStore(sm.ChainStore().ChainBlockstore()) + + // Slice will be empty if not storing events. + for i, evs := range events { + if len(evs) == 0 { + continue + } + + switch root, err := amt4.FromArray(ctx, cst, evs, amt4.UseTreeBitWidth(types.EventAMTBitwidth)); { + case err != nil: + return cid.Undef, cid.Undef, xerrors.Errorf("failed to store events amt: %w", err) + case i >= len(receipts): + return cid.Undef, cid.Undef, xerrors.Errorf("assertion failed: receipt and events array lengths inconsistent") + case receipts[i].EventsRoot == nil: + return cid.Undef, cid.Undef, xerrors.Errorf("assertion failed: VM returned events with no events root") + case root != *receipts[i].EventsRoot: + return cid.Undef, cid.Undef, xerrors.Errorf("assertion failed: returned events AMT root does not match derived") + } + } + st, err := vmi.Flush(ctx) if err != nil { return cid.Undef, cid.Undef, xerrors.Errorf("vm flush failed: %w", err) diff --git a/chain/consensus/filcns/filecoin.go b/chain/consensus/filcns/filecoin.go index 2cd680746..7f94e4806 100644 --- a/chain/consensus/filcns/filecoin.go +++ b/chain/consensus/filcns/filecoin.go @@ -592,7 +592,7 @@ func (filec *FilecoinEC) checkBlockMessages(ctx context.Context, b *types.FullBl return xerrors.Errorf("failed to resolve key addr: %w", err) } - if err := chain.AuthenticateMessage(m, kaddr); err != nil { + if err := chain.AuthenticateMessage(m, kaddr, nv); err != nil { return xerrors.Errorf("failed to validate signature: %w", err) } diff --git a/chain/consensus/filcns/upgrades.go b/chain/consensus/filcns/upgrades.go index b82ce6fff..a8ce5c9c6 100644 --- a/chain/consensus/filcns/upgrades.go +++ b/chain/consensus/filcns/upgrades.go @@ -249,6 +249,10 @@ func DefaultUpgradeSchedule() stmgr.UpgradeSchedule { Height: build.UpgradeHyperspaceNV19Height, Network: network.Version19, Migration: UpgradeActorsV11, + }, { + Height: build.UpgradeHyperspaceNV20Height, + Network: network.Version20, + Migration: UpgradeActorsV12, }, } @@ -1705,7 +1709,6 @@ func upgradeActorsV10Common( func UpgradeActorsV11(ctx context.Context, sm *stmgr.StateManager, _ stmgr.MigrationCache, _ stmgr.ExecMonitor, root cid.Cid, _ abi.ChainEpoch, _ *types.TipSet) (cid.Cid, error) { oldAv := actorstypes.Version10 newAv := actorstypes.Version11 - // This may change for upgrade oldStateTreeVersion := types.StateTreeVersion5 newStateTreeVersion := types.StateTreeVersion5 @@ -1723,6 +1726,26 @@ func UpgradeActorsV11(ctx context.Context, sm *stmgr.StateManager, _ stmgr.Migra return LiteMigration(ctx, bstore, newActorsManifestCid, root, oldAv, newAv, oldStateTreeVersion, newStateTreeVersion) } +func UpgradeActorsV12(ctx context.Context, sm *stmgr.StateManager, _ stmgr.MigrationCache, _ stmgr.ExecMonitor, root cid.Cid, _ abi.ChainEpoch, _ *types.TipSet) (cid.Cid, error) { + oldAv := actorstypes.Version11 + newAv := actorstypes.Version12 + oldStateTreeVersion := types.StateTreeVersion5 + newStateTreeVersion := types.StateTreeVersion5 + + // ensure that the manifest is loaded in the blockstore + if err := bundle.LoadBundles(ctx, sm.ChainStore().StateBlockstore(), newAv); err != nil { + return cid.Undef, xerrors.Errorf("failed to load manifest bundle: %w", err) + } + + newActorsManifestCid, ok := actors.GetManifest(newAv) + if !ok { + return cid.Undef, xerrors.Errorf("no manifest CID for actors v12 upgrade") + } + + bstore := sm.ChainStore().StateBlockstore() + return LiteMigration(ctx, bstore, newActorsManifestCid, root, oldAv, newAv, oldStateTreeVersion, newStateTreeVersion) +} + func LiteMigration(ctx context.Context, bstore blockstore.Blockstore, newActorsManifestCid cid.Cid, root cid.Cid, oldAv actorstypes.Version, newAv actorstypes.Version, oldStateTreeVersion types.StateTreeVersion, newStateTreeVersion types.StateTreeVersion) (cid.Cid, error) { buf := blockstore.NewTieredBstore(bstore, blockstore.NewMemorySync()) store := store.ActorStore(ctx, buf) diff --git a/chain/events/filter/event.go b/chain/events/filter/event.go index dfb61f111..a823855a0 100644 --- a/chain/events/filter/event.go +++ b/chain/events/filter/event.go @@ -3,16 +3,13 @@ package filter import ( "bytes" "context" - "math" "sync" "time" "github.com/ipfs/go-cid" - cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" - amt4 "github.com/filecoin-project/go-amt-ipld/v4" "github.com/filecoin-project/go-state-types/abi" blockadt "github.com/filecoin-project/specs-actors/actors/util/adt" @@ -105,18 +102,9 @@ func (f *EventFilter) CollectEvents(ctx context.Context, te *TipSetEvents, rever continue } - entries := make([]types.EventEntry, len(ev.Entries)) - for i, entry := range ev.Entries { - entries[i] = types.EventEntry{ - Flags: entry.Flags, - Key: entry.Key, - Value: entry.Value, - } - } - // event matches filter, so record it cev := &CollectedEvent{ - Entries: entries, + Entries: ev.Entries, EmitterAddr: addr, EventIdx: evIdx, Reverted: revert, @@ -300,6 +288,7 @@ func (e *executedMessage) Events() []*types.Event { type EventFilterManager struct { ChainStore *cstore.ChainStore + EventGetter func(context.Context, cid.Cid) ([]types.Event, error) AddressResolver func(ctx context.Context, emitter abi.ActorID, ts *types.TipSet) (address.Address, bool) MaxFilterResults int EventIndex *EventIndex @@ -458,30 +447,15 @@ func (m *EventFilterManager) loadExecutedMessages(ctx context.Context, msgTs, rc continue } - evtArr, err := amt4.LoadAMT(ctx, st, *rct.EventsRoot, amt4.UseTreeBitWidth(types.EventAMTBitwidth)) + evs, err := m.EventGetter(ctx, *rct.EventsRoot) if err != nil { - return nil, xerrors.Errorf("load events amt: %w", err) + return nil, xerrors.Errorf("failed to get events: %w", err) } - - ems[i].evs = make([]*types.Event, evtArr.Len()) - var evt types.Event - err = evtArr.ForEach(ctx, func(u uint64, deferred *cbg.Deferred) error { - if u > math.MaxInt { - return xerrors.Errorf("too many events") - } - if err := evt.UnmarshalCBOR(bytes.NewReader(deferred.Raw)); err != nil { - return err - } - - cpy := evt - ems[i].evs[int(u)] = &cpy //nolint:scopelint - return nil - }) - - if err != nil { - return nil, xerrors.Errorf("read events: %w", err) + ems[i].evs = make([]*types.Event, 0, len(evs)) + for _, ev := range evs { + ev := ev + ems[i].evs = append(ems[i].evs, &ev) } - } return ems, nil diff --git a/chain/events/filter/event_test.go b/chain/events/filter/event_test.go index 80c4c7fc0..329573bc1 100644 --- a/chain/events/filter/event_test.go +++ b/chain/events/filter/event_test.go @@ -287,6 +287,7 @@ func fakeEvent(emitter abi.ActorID, indexed []kv, unindexed []kv) *types.Event { ev.Entries = append(ev.Entries, types.EventEntry{ Flags: 0x01, Key: in.k, + Codec: cid.Raw, Value: in.v, }) } @@ -295,6 +296,7 @@ func fakeEvent(emitter abi.ActorID, indexed []kv, unindexed []kv) *types.Event { ev.Entries = append(ev.Entries, types.EventEntry{ Flags: 0x00, Key: in.k, + Codec: cid.Raw, Value: in.v, }) } diff --git a/chain/events/filter/index.go b/chain/events/filter/index.go index 1b69dfd10..6962fcaa3 100644 --- a/chain/events/filter/index.go +++ b/chain/events/filter/index.go @@ -1,15 +1,20 @@ package filter import ( + "bytes" "context" "database/sql" + "encoding/hex" "errors" "fmt" "sort" + "strconv" "strings" "github.com/ipfs/go-cid" + logging "github.com/ipfs/go-log/v2" _ "github.com/mattn/go-sqlite3" + cbg "github.com/whyrusleeping/cbor-gen" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -47,6 +52,7 @@ var ddls = []string{ indexed INTEGER NOT NULL, flags BLOB NOT NULL, key TEXT NOT NULL, + codec INTEGER, value BLOB NOT NULL )`, @@ -55,11 +61,11 @@ var ddls = []string{ version UINT64 NOT NULL UNIQUE )`, - // version 1. - `INSERT OR IGNORE INTO _meta (version) VALUES (1)`, + // set the version + `INSERT OR IGNORE INTO _meta (version) VALUES (` + strconv.Itoa(schemaVersion) + `)`, } -const schemaVersion = 1 +const schemaVersion = 2 const ( insertEvent = `INSERT OR IGNORE INTO event @@ -67,10 +73,12 @@ const ( VALUES(?, ?, ?, ?, ?, ?, ?, ?)` insertEntry = `INSERT OR IGNORE INTO event_entry - (event_id, indexed, flags, key, value) - VALUES(?, ?, ?, ?, ?)` + (event_id, indexed, flags, key, codec, value) + VALUES(?, ?, ?, ?, ?, ?)` ) +var log = logging.Logger("event_index") + type EventIndex struct { db *sql.DB } @@ -88,6 +96,7 @@ func NewEventIndex(path string) (*EventIndex, error) { } } + var version int q, err := db.Query("SELECT name FROM sqlite_master WHERE type='table' AND name='_meta';") if err == sql.ErrNoRows || !q.Next() { // empty database, create the schema @@ -100,25 +109,25 @@ func NewEventIndex(path string) (*EventIndex, error) { } else if err != nil { _ = db.Close() return nil, xerrors.Errorf("looking for _meta table: %w", err) - } else { - // Ensure we don't open a database from a different schema version + } - row := db.QueryRow("SELECT max(version) FROM _meta") - var version int - err := row.Scan(&version) - if err != nil { - _ = db.Close() - return nil, xerrors.Errorf("invalid database version: no version found") - } - if version != schemaVersion { - _ = db.Close() - return nil, xerrors.Errorf("invalid database version: got %d, expected %d", version, schemaVersion) + row := db.QueryRow("SELECT max(version) FROM _meta") + if err := row.Scan(&version); err != nil { + _ = db.Close() + return nil, xerrors.Errorf("invalid database version: no version found") + } + + // Create the event index. + idx := &EventIndex{db: db} + + // Run any necessary migrations. + if version != schemaVersion { + if err := idx.RunMigration(version, schemaVersion); err != nil { + return nil, xerrors.Errorf("failed to run event index migration from v%d to v%d: %w", version, schemaVersion, err) } } - return &EventIndex{ - db: db, - }, nil + return idx, nil } func (ei *EventIndex) Close() error { @@ -194,6 +203,7 @@ func (ei *EventIndex) CollectEvents(ctx context.Context, te *TipSetEvents, rever isIndexedValue(entry.Flags), // indexed []byte{entry.Flags}, // flags entry.Key, // key + entry.Codec, // codec entry.Value, // value ) if err != nil { @@ -251,7 +261,7 @@ func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter) error { subclauses := []string{} for _, val := range vals { subclauses = append(subclauses, fmt.Sprintf("%s.value=?", joinAlias)) - values = append(values, trimLeadingZeros(val)) + values = append(values, val) } clauses = append(clauses, "("+strings.Join(subclauses, " OR ")+")") } @@ -270,6 +280,7 @@ func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter) error { event.reverted, event_entry.flags, event_entry.key, + event_entry.codec, event_entry.value FROM event JOIN event_entry ON event.id=event_entry.event_id` @@ -319,6 +330,7 @@ func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter) error { reverted bool flags []byte key string + codec uint64 value []byte } @@ -334,6 +346,7 @@ func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter) error { &row.reverted, &row.flags, &row.key, + &row.codec, &row.value, ); err != nil { return xerrors.Errorf("read prefill row: %w", err) @@ -378,6 +391,7 @@ func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter) error { ce.Entries = append(ce.Entries, types.EventEntry{ Flags: row.flags[0], Key: row.key, + Codec: row.codec, Value: row.value, }) @@ -399,11 +413,117 @@ func (ei *EventIndex) PrefillFilter(ctx context.Context, f *EventFilter) error { return nil } -func trimLeadingZeros(b []byte) []byte { - for i := range b { - if b[i] != 0 { - return b[i:] +func (ei *EventIndex) RunMigration(from, to int) error { + if from != 1 && to != 2 { + return nil + } + + tx, err := ei.db.Begin() + if err != nil { + return err + } + defer tx.Rollback() //nolint + + if _, err = tx.Exec(`ALTER TABLE event_entry ADD COLUMN codec INTEGER`); err != nil { + return xerrors.Errorf("failed to alter table event_entry to add codec column: %w", err) + } + + log.Warnw("[migration v1=>v2] event_entry table schema updated") + + res, err := tx.Exec(`UPDATE event_entry SET codec = ? WHERE codec IS NULL`, 0x55) + if err != nil { + return xerrors.Errorf("failed to update table event_entry to set codec 0x55: %w", err) + } + n, _ := res.RowsAffected() + log.Warnw("[migration v1=>v2] updated %d event entries with codec=0x55", "affected", n) + + var keyRewrites = [][]string{ + {"topic1", "t1"}, + {"topic2", "t2"}, + {"topic3", "t3"}, + {"topic4", "t4"}, + {"data", "d"}, + } + + for _, r := range keyRewrites { + from, to := r[0], r[1] + res, err := tx.Exec(`UPDATE event_entry SET key = ? WHERE key = ?`, to, from) + if err != nil { + return xerrors.Errorf("failed to update entries from key %s to key %s: %w", from, to, err) + } + n, _ := res.RowsAffected() + log.Warnw("[migration v1=>v2] rewrote entry keys", "from", from, "to", to, "affected", n) + } + + update, err := tx.Prepare(`UPDATE event_entry SET value = ? WHERE value = ?`) + if err != nil { + return xerrors.Errorf("failed to prepare update statement: %w", err) + } + + log.Warnw("[migration v1=>v2] processing values") + + topics := map[string]struct{}{ + "t1": {}, + "t2": {}, + "t3": {}, + "t4": {}, + } + + if _, err = tx.Exec(`CREATE TABLE tmp AS SELECT key, value FROM event_entry`); err != nil { + return xerrors.Errorf("failed to create temporary table: %w", err) + } + + // Pull the values out and process them in Go land before updating. + rows, err := tx.Query(`SELECT key, value FROM tmp`) + if err != nil { + return xerrors.Errorf("failed to select values: %w", err) + } + + var i int + for rows.Next() { + var key string + var before []byte + if err := rows.Scan(&key, &before); err != nil { + return xerrors.Errorf("failed to scan from query: %w", err) + } + after, err := cbg.ReadByteArray(bytes.NewReader(before), 4<<20) + if err != nil { + return xerrors.Errorf("failed to decode cbor: %w; value: %s", err, hex.EncodeToString(before)) + } + if _, ok := topics[key]; ok && len(before) < 32 { + // if this is a topic, leftpad to 32 bytes + pvalue := make([]byte, 32) + copy(pvalue[32-len(after):], after) + after = pvalue + } + if _, err := update.Exec(after, before); err != nil { + return xerrors.Errorf("failed to scan from query: %w", err) + } + + if i++; i%500 == 0 { + log.Warnw("[migration v1=>v2] processed values", "count", i) } } - return []byte{} + + log.Warnw("[migration v1=>v2] processed all values", "count", i) + + if _, err = tx.Exec(`DROP TABLE tmp`); err != nil { + return xerrors.Errorf("failed to drop temporary table: %w", err) + } + + log.Warnw("[migration v1=>v2] dropped temporary table") + + if _, err = tx.Exec(`INSERT INTO _meta (version) VALUES (2)`); err != nil { + return xerrors.Errorf("failed to update schema version: %w", err) + } + + log.Warnw("[migration v1=>v2] schema version updated to v2") + + if err := tx.Commit(); err != nil { + return xerrors.Errorf("failed to commit transaction: %w", err) + } + + log.Warnw("[migration v1=>v2] transaction committed; ALL DONE") + + return nil } diff --git a/chain/messagepool/messagepool.go b/chain/messagepool/messagepool.go index dabd2cb33..e3354d3e6 100644 --- a/chain/messagepool/messagepool.go +++ b/chain/messagepool/messagepool.go @@ -794,7 +794,12 @@ func (mp *MessagePool) VerifyMsgSig(m *types.SignedMessage) error { return nil } - if err := chain.AuthenticateMessage(m, m.Message.From); err != nil { + nv, err := mp.getNtwkVersion(mp.curTs.Height()) + if err != nil { + return xerrors.Errorf("failedl to get network version: %w", err) + } + + if err := chain.AuthenticateMessage(m, m.Message.From, nv); err != nil { return xerrors.Errorf("failed to validate signature: %w", err) } diff --git a/chain/messagesigner/messagesigner.go b/chain/messagesigner/messagesigner.go index 84dd6d0aa..cd31a3b73 100644 --- a/chain/messagesigner/messagesigner.go +++ b/chain/messagesigner/messagesigner.go @@ -13,13 +13,11 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-address" - "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/messagepool" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" - "github.com/filecoin-project/lotus/chain/wallet/key" "github.com/filecoin-project/lotus/node/modules/dtypes" ) @@ -69,11 +67,7 @@ func (ms *MessageSigner) SignMessage(ctx context.Context, msg *types.Message, sp // Sign the message with the nonce msg.Nonce = nonce - keyInfo, err := ms.wallet.WalletExport(ctx, msg.From) - if err != nil { - return nil, err - } - sb, err := SigningBytes(msg, key.ActSigType(keyInfo.Type)) + sb, err := SigningBytes(msg, msg.From.Protocol()) if err != nil { return nil, err } @@ -200,8 +194,8 @@ func (ms *MessageSigner) dstoreKey(addr address.Address) datastore.Key { return datastore.KeyWithNamespaces([]string{dsKeyActorNonce, addr.String()}) } -func SigningBytes(msg *types.Message, sigType crypto.SigType) ([]byte, error) { - if sigType == crypto.SigTypeDelegated { +func SigningBytes(msg *types.Message, sigType address.Protocol) ([]byte, error) { + if sigType == address.Delegated { txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(msg) if err != nil { return nil, xerrors.Errorf("failed to reconstruct eth transaction: %w", err) diff --git a/chain/signatures.go b/chain/signatures.go index 1dc67fd2c..72cbab3e6 100644 --- a/chain/signatures.go +++ b/chain/signatures.go @@ -4,6 +4,7 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/crypto" "github.com/filecoin-project/go-state-types/network" @@ -16,21 +17,36 @@ import ( // SignedMessage was signed by the indicated Address, computing the correct // signature payload depending on the signature type. The supplied Address type // must be recognized by the registered verifier for the signature type. -func AuthenticateMessage(msg *types.SignedMessage, signer address.Address) error { +func AuthenticateMessage(msg *types.SignedMessage, signer address.Address, nv network.Version) error { var digest []byte typ := msg.Signature.Type switch typ { case crypto.SigTypeDelegated: + if nv >= network.Version20 && msg.Message.Method == builtin.MethodSend { + return xerrors.Errorf("nv20 and above no longer admits method 0 on messages with Ethereum delegated signatures") + } txArgs, err := ethtypes.EthTxArgsFromUnsignedEthMessage(&msg.Message) if err != nil { return xerrors.Errorf("failed to reconstruct eth transaction: %w", err) } + + // Prior to nv20, we rejected empty initcode. + if len(txArgs.Input) == 0 && nv < network.Version20 && txArgs.To == nil { + return xerrors.Errorf("nv19 and below don't support empty initcode") + } + roundTripMsg, err := txArgs.ToUnsignedMessage(msg.Message.From) if err != nil { return xerrors.Errorf("failed to reconstruct filecoin msg: %w", err) } + // Prior to nv20, delegated signature messages with no parameters carried MethodSend. + // Reset the value so we'll roundtrip. + if nv < network.Version20 && roundTripMsg.Method == builtin.MethodsEVM.InvokeContract && len(roundTripMsg.Params) == 0 { + roundTripMsg.Method = builtin.MethodSend + } + if !msg.Message.Equals(roundTripMsg) { return xerrors.New("ethereum tx failed to roundtrip") } diff --git a/chain/state/statetree.go b/chain/state/statetree.go index 7a5908c08..3142a07d8 100644 --- a/chain/state/statetree.go +++ b/chain/state/statetree.go @@ -156,7 +156,7 @@ func VersionForNetwork(ver network.Version) (types.StateTreeVersion, error) { case network.Version13, network.Version14, network.Version15, network.Version16, network.Version17: return types.StateTreeVersion4, nil - case network.Version18, network.Version19: + case network.Version18, network.Version19, network.Version20: return types.StateTreeVersion5, nil default: diff --git a/chain/stmgr/call.go b/chain/stmgr/call.go index 61be26e56..901fc2d12 100644 --- a/chain/stmgr/call.go +++ b/chain/stmgr/call.go @@ -31,6 +31,10 @@ var ErrExpensiveFork = errors.New("refusing explicit call due to state fork at e // tipset's parent. In the presence of null blocks, the height at which the message is invoked may // be less than the specified tipset. func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types.TipSet) (*api.InvocResult, error) { + // Copy the message as we modify it below. + msgCopy := *msg + msg = &msgCopy + if msg.GasLimit == 0 { msg.GasLimit = build.BlockGasLimit } @@ -44,12 +48,12 @@ func (sm *StateManager) Call(ctx context.Context, msg *types.Message, ts *types. msg.Value = types.NewInt(0) } - return sm.callInternal(ctx, msg, nil, ts, cid.Undef, sm.GetNetworkVersion, false) + return sm.callInternal(ctx, msg, nil, ts, cid.Undef, sm.GetNetworkVersion, false, false) } // CallWithGas calculates the state for a given tipset, and then applies the given message on top of that state. func (sm *StateManager) CallWithGas(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet) (*api.InvocResult, error) { - return sm.callInternal(ctx, msg, priorMsgs, ts, cid.Undef, sm.GetNetworkVersion, true) + return sm.callInternal(ctx, msg, priorMsgs, ts, cid.Undef, sm.GetNetworkVersion, true, true) } // CallAtStateAndVersion allows you to specify a message to execute on the given stateCid and network version. @@ -61,13 +65,13 @@ func (sm *StateManager) CallAtStateAndVersion(ctx context.Context, msg *types.Me return v } - return sm.callInternal(ctx, msg, nil, nil, stateCid, nvGetter, true) + return sm.callInternal(ctx, msg, nil, nil, stateCid, nvGetter, true, false) } // - If no tipset is specified, the first tipset without an expensive migration or one in its parent is used. // - If executing a message at a given tipset or its parent would trigger an expensive migration, the call will // fail with ErrExpensiveFork. -func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet, stateCid cid.Cid, nvGetter rand.NetworkVersionGetter, checkGas bool) (*api.InvocResult, error) { +func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, priorMsgs []types.ChainMsg, ts *types.TipSet, stateCid cid.Cid, nvGetter rand.NetworkVersionGetter, checkGas, applyTsMessages bool) (*api.InvocResult, error) { ctx, span := trace.StartSpan(ctx, "statemanager.callInternal") defer span.End() @@ -107,22 +111,18 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr } } - var vmHeight abi.ChainEpoch - if checkGas { - // Since we're simulating a future message, pretend we're applying it in the "next" tipset - vmHeight = ts.Height() + 1 - if stateCid == cid.Undef { - stateCid, _, err = sm.TipSetState(ctx, ts) - if err != nil { - return nil, xerrors.Errorf("computing tipset state: %w", err) - } - } - } else { - // If we're not checking gas, we don't want to have to execute the tipset like above. This saves a lot of computation time - vmHeight = pts.Height() + 1 - if stateCid == cid.Undef { - stateCid = ts.ParentState() + // Unless executing on a specific state cid, apply all the messages from the current tipset + // first. Unfortunately, we can't just execute the tipset, because that will run cron. We + // don't want to apply miner messages after cron runs in a given epoch. + if stateCid == cid.Undef { + stateCid = ts.ParentState() + } + if applyTsMessages { + tsMsgs, err := sm.cs.MessagesForTipset(ctx, ts) + if err != nil { + return nil, xerrors.Errorf("failed to lookup messages for parent tipset: %w", err) } + priorMsgs = append(tsMsgs, priorMsgs...) } // Technically, the tipset we're passing in here should be ts+1, but that may not exist. @@ -142,14 +142,14 @@ func (sm *StateManager) callInternal(ctx context.Context, msg *types.Message, pr buffStore := blockstore.NewTieredBstore(sm.cs.StateBlockstore(), blockstore.NewMemorySync()) vmopt := &vm.VMOpts{ StateBase: stateCid, - Epoch: vmHeight, + Epoch: ts.Height(), Timestamp: ts.MinTimestamp(), Rand: rand.NewStateRand(sm.cs, ts.Cids(), sm.beacon, nvGetter), Bstore: buffStore, Actors: sm.tsExec.NewActorRegistry(), Syscalls: sm.Syscalls, CircSupplyCalc: sm.GetVMCirculatingSupply, - NetworkVersion: nvGetter(ctx, vmHeight), + NetworkVersion: nvGetter(ctx, ts.Height()), BaseFee: ts.Blocks()[0].ParentBaseFee, LookbackState: LookbackStateGetterForTipset(sm, ts), TipSetGetter: TipSetGetterForTipset(sm.cs, ts), diff --git a/chain/store/messages.go b/chain/store/messages.go index 5e2880c4a..1ee1f8ad4 100644 --- a/chain/store/messages.go +++ b/chain/store/messages.go @@ -256,6 +256,20 @@ func (cs *ChainStore) MessagesForBlock(ctx context.Context, b *types.BlockHeader return blsmsgs, secpkmsgs, nil } +func (cs *ChainStore) SecpkMessagesForBlock(ctx context.Context, b *types.BlockHeader) ([]*types.SignedMessage, error) { + _, secpkcids, err := cs.ReadMsgMetaCids(ctx, b.Messages) + if err != nil { + return nil, err + } + + secpkmsgs, err := cs.LoadSignedMessagesFromCids(ctx, secpkcids) + if err != nil { + return nil, xerrors.Errorf("loading secpk messages for block: %w", err) + } + + return secpkmsgs, nil +} + func (cs *ChainStore) GetParentReceipt(ctx context.Context, b *types.BlockHeader, i int) (*types.MessageReceipt, error) { // block headers use adt0, for now. a, err := blockadt.AsArray(cs.ActorStore(ctx), b.ParentMessageReceipts) diff --git a/chain/store/store.go b/chain/store/store.go index 6dc17d766..2773f1ad3 100644 --- a/chain/store/store.go +++ b/chain/store/store.go @@ -126,6 +126,8 @@ type ChainStore struct { evtTypes [1]journal.EventType journal journal.Journal + storeEvents bool + cancelFn context.CancelFunc wg sync.WaitGroup } @@ -680,7 +682,7 @@ func FlushValidationCache(ctx context.Context, ds dstore.Batching) error { // If this is addressed (blockcache goes into its own sub-namespace) then // strings.HasPrefix(...) below can be skipped // - //Prefix: blockValidationCacheKeyPrefix.String() + // Prefix: blockValidationCacheKeyPrefix.String() KeysOnly: true, }) if err != nil { @@ -1202,6 +1204,16 @@ func (cs *ChainStore) Weight(ctx context.Context, hts *types.TipSet) (types.BigI return cs.weight(ctx, cs.StateBlockstore(), hts) } +// StoreEvents marks this ChainStore as storing events. +func (cs *ChainStore) StoreEvents(store bool) { + cs.storeEvents = store +} + +// IsStoringEvents indicates if this ChainStore is storing events. +func (cs *ChainStore) IsStoringEvents() bool { + return cs.storeEvents +} + // true if ts1 wins according to the filecoin tie-break rule func breakWeightTie(ts1, ts2 *types.TipSet) bool { s := len(ts1.Blocks()) diff --git a/chain/types/cbor_gen.go b/chain/types/cbor_gen.go index 709f7dd88..fd2462756 100644 --- a/chain/types/cbor_gen.go +++ b/chain/types/cbor_gen.go @@ -1943,7 +1943,7 @@ func (t *Event) UnmarshalCBOR(r io.Reader) (err error) { return nil } -var lengthBufEventEntry = []byte{131} +var lengthBufEventEntry = []byte{132} func (t *EventEntry) MarshalCBOR(w io.Writer) error { if t == nil { @@ -1974,6 +1974,247 @@ func (t *EventEntry) MarshalCBOR(w io.Writer) error { return err } + // t.Codec (uint64) (uint64) + + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.Codec)); err != nil { + return err + } + + // t.Value ([]uint8) (slice) + if len(t.Value) > cbg.ByteArrayMaxLen { + return xerrors.Errorf("Byte array in field t.Value was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajByteString, uint64(len(t.Value))); err != nil { + return err + } + + if _, err := cw.Write(t.Value[:]); err != nil { + return err + } + return nil +} + +func (t *EventEntry) UnmarshalCBOR(r io.Reader) (err error) { + *t = EventEntry{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 4 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.Flags (uint8) (uint8) + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint8 field") + } + if extra > math.MaxUint8 { + return fmt.Errorf("integer in input was too large for uint8 field") + } + t.Flags = uint8(extra) + // t.Key (string) (string) + + { + sval, err := cbg.ReadString(cr) + if err != nil { + return err + } + + t.Key = string(sval) + } + // t.Codec (uint64) (uint64) + + { + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.Codec = uint64(extra) + + } + // t.Value ([]uint8) (slice) + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + + if extra > cbg.ByteArrayMaxLen { + return fmt.Errorf("t.Value: byte array too large (%d)", extra) + } + if maj != cbg.MajByteString { + return fmt.Errorf("expected byte array") + } + + if extra > 0 { + t.Value = make([]uint8, extra) + } + + if _, err := io.ReadFull(cr, t.Value[:]); err != nil { + return err + } + return nil +} + +var lengthBufLegacyEvent = []byte{130} + +func (t *LegacyEvent) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write(lengthBufLegacyEvent); err != nil { + return err + } + + // t.Emitter (abi.ActorID) (uint64) + + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.Emitter)); err != nil { + return err + } + + // t.Entries ([]types.LegacyEventEntry) (slice) + if len(t.Entries) > cbg.MaxLength { + return xerrors.Errorf("Slice value in field t.Entries was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajArray, uint64(len(t.Entries))); err != nil { + return err + } + for _, v := range t.Entries { + if err := v.MarshalCBOR(cw); err != nil { + return err + } + } + return nil +} + +func (t *LegacyEvent) UnmarshalCBOR(r io.Reader) (err error) { + *t = LegacyEvent{} + + cr := cbg.NewCborReader(r) + + maj, extra, err := cr.ReadHeader() + if err != nil { + return err + } + defer func() { + if err == io.EOF { + err = io.ErrUnexpectedEOF + } + }() + + if maj != cbg.MajArray { + return fmt.Errorf("cbor input should be of type array") + } + + if extra != 2 { + return fmt.Errorf("cbor input had wrong number of fields") + } + + // t.Emitter (abi.ActorID) (uint64) + + { + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + if maj != cbg.MajUnsignedInt { + return fmt.Errorf("wrong type for uint64 field") + } + t.Emitter = abi.ActorID(extra) + + } + // t.Entries ([]types.LegacyEventEntry) (slice) + + maj, extra, err = cr.ReadHeader() + if err != nil { + return err + } + + if extra > cbg.MaxLength { + return fmt.Errorf("t.Entries: array too large (%d)", extra) + } + + if maj != cbg.MajArray { + return fmt.Errorf("expected cbor array") + } + + if extra > 0 { + t.Entries = make([]LegacyEventEntry, extra) + } + + for i := 0; i < int(extra); i++ { + + var v LegacyEventEntry + if err := v.UnmarshalCBOR(cr); err != nil { + return err + } + + t.Entries[i] = v + } + + return nil +} + +var lengthBufLegacyEventEntry = []byte{131} + +func (t *LegacyEventEntry) MarshalCBOR(w io.Writer) error { + if t == nil { + _, err := w.Write(cbg.CborNull) + return err + } + + cw := cbg.NewCborWriter(w) + + if _, err := cw.Write(lengthBufLegacyEventEntry); err != nil { + return err + } + + // t.Flags (uint8) (uint8) + if err := cw.WriteMajorTypeHeader(cbg.MajUnsignedInt, uint64(t.Flags)); err != nil { + return err + } + + // t.Key (string) (string) + if len(t.Key) > cbg.MaxLength { + return xerrors.Errorf("Value in field t.Key was too long") + } + + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Key))); err != nil { + return err + } + if _, err := io.WriteString(w, string(t.Key)); err != nil { + return err + } + // t.Value ([]uint8) (slice) if len(t.Value) > cbg.ByteArrayMaxLen { return xerrors.Errorf("Byte array in field t.Value was too long") @@ -1989,8 +2230,8 @@ func (t *EventEntry) MarshalCBOR(w io.Writer) error { return nil } -func (t *EventEntry) UnmarshalCBOR(r io.Reader) (err error) { - *t = EventEntry{} +func (t *LegacyEventEntry) UnmarshalCBOR(r io.Reader) (err error) { + *t = LegacyEventEntry{} cr := cbg.NewCborReader(r) diff --git a/chain/types/ethtypes/eth_transactions.go b/chain/types/ethtypes/eth_transactions.go index 9f9df2381..2eabde6e5 100644 --- a/chain/types/ethtypes/eth_transactions.go +++ b/chain/types/ethtypes/eth_transactions.go @@ -12,6 +12,7 @@ import ( "github.com/filecoin-project/go-address" gocrypto "github.com/filecoin-project/go-crypto" + "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" typescrypto "github.com/filecoin-project/go-state-types/crypto" @@ -37,11 +38,20 @@ type EthTx struct { Gas EthUint64 `json:"gas"` MaxFeePerGas EthBigInt `json:"maxFeePerGas"` MaxPriorityFeePerGas EthBigInt `json:"maxPriorityFeePerGas"` + AccessList []EthHash `json:"accessList"` V EthBigInt `json:"v"` R EthBigInt `json:"r"` S EthBigInt `json:"s"` } +func (tx *EthTx) Reward(blkBaseFee big.Int) EthBigInt { + availablePriorityFee := big.Sub(big.Int(tx.MaxFeePerGas), blkBaseFee) + if big.Cmp(big.Int(tx.MaxPriorityFeePerGas), availablePriorityFee) <= 0 { + return tx.MaxPriorityFeePerGas + } + return EthBigInt(availablePriorityFee) +} + type EthTxArgs struct { ChainID int `json:"chainId"` Nonce int `json:"nonce"` @@ -86,6 +96,7 @@ func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) { Gas: EthUint64(txArgs.GasLimit), MaxFeePerGas: EthBigInt(txArgs.MaxFeePerGas), MaxPriorityFeePerGas: EthBigInt(txArgs.MaxPriorityFeePerGas), + AccessList: []EthHash{}, V: v, R: r, S: s, @@ -95,59 +106,49 @@ func EthTxFromSignedEthMessage(smsg *types.SignedMessage) (EthTx, error) { func EthTxArgsFromUnsignedEthMessage(msg *types.Message) (EthTxArgs, error) { var ( - to *EthAddress - params []byte - paramsReader = bytes.NewReader(msg.Params) - err error + to *EthAddress + params []byte + err error ) if msg.Version != 0 { return EthTxArgs{}, xerrors.Errorf("unsupported msg version: %d", msg.Version) } + if len(msg.Params) > 0 { + paramsReader := bytes.NewReader(msg.Params) + params, err = cbg.ReadByteArray(paramsReader, uint64(len(msg.Params))) + if err != nil { + return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err) + } + if paramsReader.Len() != 0 { + return EthTxArgs{}, xerrors.Errorf("extra data found in params") + } + if len(params) == 0 { + return EthTxArgs{}, xerrors.Errorf("non-empty params encode empty byte array") + } + } + if msg.To == builtintypes.EthereumAddressManagerActorAddr { - switch msg.Method { - case builtintypes.MethodsEAM.CreateExternal: - params, err = cbg.ReadByteArray(paramsReader, uint64(len(msg.Params))) - if err != nil { - return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err) - } - default: + if msg.Method != builtintypes.MethodsEAM.CreateExternal { return EthTxArgs{}, fmt.Errorf("unsupported EAM method") } - } else { + } else if msg.Method == builtintypes.MethodsEVM.InvokeContract || msg.Method == builtintypes.MethodSend { + // < nv20 (Hyperspace): The condition here is technically too lenient. + // The correct behavior would be to _only_ allow MethodSend on < nv20. + // However, snaking through the network version here is too expensive, and not worth it, + // given that the transition period will only last for ~24h anyway. + // AuthenticateMessage is the _crucial_ spot, and it's already applying a stricter check. + // The Eth API endpoint is also selecting the right method depending on the network version. addr, err := EthAddressFromFilecoinAddress(msg.To) if err != nil { return EthTxArgs{}, err } to = &addr - - if len(msg.Params) == 0 { - if msg.Method != builtintypes.MethodSend { - return EthTxArgs{}, xerrors.Errorf("cannot invoke method %d on non-EAM actor without params", msg.Method) - } - } else { - if msg.Method != builtintypes.MethodsEVM.InvokeContract { - return EthTxArgs{}, - xerrors.Errorf("invalid methodnum %d: only allowed non-send method is InvokeContract(%d)", - msg.Method, - builtintypes.MethodsEVM.InvokeContract) - } - - params, err = cbg.ReadByteArray(paramsReader, uint64(len(msg.Params))) - if err != nil { - return EthTxArgs{}, xerrors.Errorf("failed to read params byte array: %w", err) - } - } - } - - if paramsReader.Len() != 0 { - return EthTxArgs{}, xerrors.Errorf("extra data found in params") - } - - if len(params) == 0 && msg.Method != builtintypes.MethodSend { - // Otherwise, we don't get a guaranteed round-trip. - return EthTxArgs{}, xerrors.Errorf("msgs with empty parameters from an eth-account must be Sends (MethodNum: %d)", msg.Method) + } else { + return EthTxArgs{}, + xerrors.Errorf("invalid methodnum %d: only allowed method is InvokeContract(%d)", + msg.Method, builtintypes.MethodsEVM.InvokeContract) } return EthTxArgs{ @@ -168,42 +169,27 @@ func (tx *EthTxArgs) ToUnsignedMessage(from address.Address) (*types.Message, er } var err error - method := builtintypes.MethodSend var params []byte - var to address.Address - // nil indicates the EAM, only CreateExternal is allowed - if tx.To == nil { - to = builtintypes.EthereumAddressManagerActorAddr - method = builtintypes.MethodsEAM.CreateExternal - if len(tx.Input) == 0 { - return nil, xerrors.New("cannot call CreateExternal without params") - } - + if len(tx.Input) > 0 { buf := new(bytes.Buffer) if err = cbg.WriteByteArray(buf, tx.Input); err != nil { - return nil, xerrors.Errorf("failed to serialize Create params: %w", err) + return nil, xerrors.Errorf("failed to write input args: %w", err) } - params = buf.Bytes() + } + + var to address.Address + var method abi.MethodNum + // nil indicates the EAM, only CreateExternal is allowed + if tx.To == nil { + method = builtintypes.MethodsEAM.CreateExternal + to = builtintypes.EthereumAddressManagerActorAddr } else { + method = builtintypes.MethodsEVM.InvokeContract to, err = tx.To.ToFilecoinAddress() if err != nil { return nil, xerrors.Errorf("failed to convert To into filecoin addr: %w", err) } - if len(tx.Input) == 0 { - // Yes, this is redundant, but let's be sure what we're doing - method = builtintypes.MethodSend - params = make([]byte, 0) - } else { - // must be InvokeContract - method = builtintypes.MethodsEVM.InvokeContract - buf := new(bytes.Buffer) - if err = cbg.WriteByteArray(buf, tx.Input); err != nil { - return nil, xerrors.Errorf("failed to write input args: %w", err) - } - - params = buf.Bytes() - } } return &types.Message{ diff --git a/chain/types/ethtypes/eth_types.go b/chain/types/ethtypes/eth_types.go index 584a7d2be..1539a638b 100644 --- a/chain/types/ethtypes/eth_types.go +++ b/chain/types/ethtypes/eth_types.go @@ -22,13 +22,7 @@ import ( builtintypes "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/lotus/build" -) - -var ( - EthTopic1 = "topic1" - EthTopic2 = "topic2" - EthTopic3 = "topic3" - EthTopic4 = "topic4" + "github.com/filecoin-project/lotus/lib/must" ) var ErrInvalidAddress = errors.New("invalid Filecoin Eth address") @@ -39,18 +33,29 @@ func (e EthUint64) MarshalJSON() ([]byte, error) { return json.Marshal(e.Hex()) } +// UnmarshalJSON should be able to parse these types of input: +// 1. a JSON string containing a hex-encoded uint64 starting with 0x +// 2. a JSON string containing an uint64 in decimal +// 3. a string containing an uint64 in decimal func (e *EthUint64) UnmarshalJSON(b []byte) error { var s string - if err := json.Unmarshal(b, &s); err != nil { - return err + if err := json.Unmarshal(b, &s); err == nil { + base := 10 + if strings.HasPrefix(s, "0x") { + base = 16 + } + parsedInt, err := strconv.ParseUint(strings.Replace(s, "0x", "", -1), base, 64) + if err != nil { + return err + } + eint := EthUint64(parsedInt) + *e = eint + return nil + } else if eint, err := strconv.ParseUint(string(b), 10, 64); err == nil { + *e = EthUint64(eint) + return nil } - parsedInt, err := strconv.ParseUint(strings.Replace(s, "0x", "", -1), 16, 64) - if err != nil { - return err - } - eint := EthUint64(parsedInt) - *e = eint - return nil + return fmt.Errorf("cannot interpret %s as a hex-encoded uint64, or a number", string(b)) } func EthUint64FromHex(s string) (EthUint64, error) { @@ -144,7 +149,7 @@ type EthBlock struct { GasLimit EthUint64 `json:"gasLimit"` GasUsed EthUint64 `json:"gasUsed"` Timestamp EthUint64 `json:"timestamp"` - Extradata []byte `json:"extraData"` + Extradata EthBytes `json:"extraData"` MixHash EthHash `json:"mixHash"` Nonce EthNonce `json:"nonce"` BaseFeePerGas EthBigInt `json:"baseFeePerGas"` @@ -154,21 +159,32 @@ type EthBlock struct { Uncles []EthHash `json:"uncles"` } +const EthBloomSize = 2048 + var ( - EmptyEthBloom = [256]byte{} - EmptyEthHash = EthHash{} - EmptyEthInt = EthUint64(0) - EmptyEthNonce = [8]byte{0, 0, 0, 0, 0, 0, 0, 0} + EmptyEthBloom = [EthBloomSize / 8]byte{} + FullEthBloom = [EthBloomSize / 8]byte{} + EmptyEthHash = EthHash{} + EmptyUncleHash = must.One(ParseEthHash("0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347")) // Keccak-256 of an RLP of an empty array + EmptyRootHash = must.One(ParseEthHash("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")) // Keccak-256 hash of the RLP of null + EmptyEthInt = EthUint64(0) + EmptyEthNonce = [8]byte{0, 0, 0, 0, 0, 0, 0, 0} ) -func NewEthBlock() EthBlock { - return EthBlock{ - Sha3Uncles: EmptyEthHash, +func init() { + for i := range FullEthBloom { + FullEthBloom[i] = 0xff + } +} + +func NewEthBlock(hasTransactions bool) EthBlock { + b := EthBlock{ + Sha3Uncles: EmptyUncleHash, // Sha3Uncles set to a hardcoded value which is used by some clients to determine if has no uncles. StateRoot: EmptyEthHash, - TransactionsRoot: EmptyEthHash, + TransactionsRoot: EmptyRootHash, // TransactionsRoot set to a hardcoded value which is used by some clients to determine if has no transactions. ReceiptsRoot: EmptyEthHash, Difficulty: EmptyEthInt, - LogsBloom: EmptyEthBloom[:], + LogsBloom: FullEthBloom[:], Extradata: []byte{}, MixHash: EmptyEthHash, Nonce: EmptyEthNonce, @@ -176,6 +192,11 @@ func NewEthBlock() EthBlock { Uncles: []EthHash{}, Transactions: []interface{}{}, } + if hasTransactions { + b.TransactionsRoot = EmptyEthHash + } + + return b } type EthCall struct { @@ -396,6 +417,10 @@ func DecodeHexString(s string) ([]byte, error) { return b, nil } +func DecodeHexStringTrimSpace(s string) ([]byte, error) { + return DecodeHexString(strings.TrimSpace(s)) +} + func handleHexStringPrefix(s string) string { // Strip the leading 0x or 0X prefix since hex.DecodeString does not support it. if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") { @@ -432,8 +457,19 @@ func EthHashFromTxBytes(b []byte) EthHash { return ethHash } +func EthBloomSet(f EthBytes, data []byte) { + hasher := sha3.NewLegacyKeccak256() + hasher.Write(data) + hash := hasher.Sum(nil) + + for i := 0; i < 3; i++ { + n := binary.BigEndian.Uint16(hash[i*2:]) % EthBloomSize + f[(EthBloomSize/8)-(n/8)-1] |= 1 << (n % 8) + } +} + type EthFeeHistory struct { - OldestBlock uint64 `json:"oldestBlock"` + OldestBlock EthUint64 `json:"oldestBlock"` BaseFeePerGas []EthBigInt `json:"baseFeePerGas"` GasUsedRatio []float64 `json:"gasUsedRatio"` Reward *[][]EthBigInt `json:"reward,omitempty"` @@ -594,7 +630,7 @@ type EthLog struct { Data EthBytes `json:"data"` // List of topics associated with the event log. - Topics []EthBytes `json:"topics"` + Topics []EthHash `json:"topics"` // Following fields are derived from the transaction containing the log @@ -660,6 +696,12 @@ type EthSubscriptionParams struct { // List of topics to be matched. // Optional, default: empty list Topics EthTopicSpec `json:"topics,omitempty"` + + // Actor address or a list of addresses from which event logs should originate. + // Optional, default nil. + // The JSON decoding must treat a string as equivalent to an array with one value, for example + // "0x8888f1f195afa192cfee86069858" must be decoded as [ "0x8888f1f195afa192cfee86069858" ] + Address EthAddressList `json:"address"` } type EthSubscriptionResponse struct { @@ -688,3 +730,45 @@ func GetContractEthAddressFromCode(sender EthAddress, salt [32]byte, initcode [] return ethAddr, nil } + +// EthFeeHistoryParams handles raw jsonrpc params for eth_feeHistory +type EthFeeHistoryParams struct { + BlkCount EthUint64 + NewestBlkNum string + RewardPercentiles *[]float64 +} + +func (e *EthFeeHistoryParams) UnmarshalJSON(b []byte) error { + var params []json.RawMessage + err := json.Unmarshal(b, ¶ms) + if err != nil { + return err + } + switch len(params) { + case 3: + err = json.Unmarshal(params[2], &e.RewardPercentiles) + if err != nil { + return err + } + fallthrough + case 2: + err = json.Unmarshal(params[1], &e.NewestBlkNum) + if err != nil { + return err + } + err = json.Unmarshal(params[0], &e.BlkCount) + if err != nil { + return err + } + default: + return xerrors.Errorf("expected 2 or 3 params, got %d", len(params)) + } + return nil +} + +func (e EthFeeHistoryParams) MarshalJSON() ([]byte, error) { + if e.RewardPercentiles != nil { + return json.Marshal([]interface{}{e.BlkCount, e.NewestBlkNum, e.RewardPercentiles}) + } + return json.Marshal([]interface{}{e.BlkCount, e.NewestBlkNum}) +} diff --git a/chain/types/ethtypes/eth_types_test.go b/chain/types/ethtypes/eth_types_test.go index e42d2bdc3..118fbc901 100644 --- a/chain/types/ethtypes/eth_types_test.go +++ b/chain/types/ethtypes/eth_types_test.go @@ -36,13 +36,19 @@ func TestEthIntUnmarshalJSON(t *testing.T) { {[]byte("\"0x0\""), EthUint64(0)}, {[]byte("\"0x41\""), EthUint64(65)}, {[]byte("\"0x400\""), EthUint64(1024)}, + {[]byte("\"0\""), EthUint64(0)}, + {[]byte("\"41\""), EthUint64(41)}, + {[]byte("\"400\""), EthUint64(400)}, + {[]byte("0"), EthUint64(0)}, + {[]byte("100"), EthUint64(100)}, + {[]byte("1024"), EthUint64(1024)}, } for _, tc := range testcases { var i EthUint64 err := i.UnmarshalJSON(tc.Input.([]byte)) require.Nil(t, err) - require.Equal(t, i, tc.Output) + require.Equal(t, tc.Output, i) } } @@ -216,7 +222,7 @@ func TestEthFilterResultMarshalJSON(t *testing.T) { TransactionHash: hash1, BlockHash: hash2, BlockNumber: 53, - Topics: []EthBytes{hash1[:]}, + Topics: []EthHash{hash1}, Data: EthBytes(hash1[:]), Address: addr, } diff --git a/chain/types/event.go b/chain/types/event.go index 00c25ca4c..106a120e2 100644 --- a/chain/types/event.go +++ b/chain/types/event.go @@ -4,6 +4,12 @@ import ( "github.com/filecoin-project/go-state-types/abi" ) +// EventEntry flags defined in fvm_shared +const ( + EventFlagIndexedKey = 0b00000001 + EventFlagIndexedValue = 0b00000010 +) + type Event struct { // The ID of the actor that emitted this event. Emitter abi.ActorID @@ -19,14 +25,11 @@ type EventEntry struct { // The key of this event entry Key string - // Any DAG-CBOR encodeable type. + // The event value's codec + Codec uint64 + + // The event value Value []byte } type FilterID [32]byte // compatible with EthHash - -// EventEntry flags defined in fvm_shared -const ( - EventFlagIndexedKey = 0b00000001 - EventFlagIndexedValue = 0b00000010 -) diff --git a/chain/types/legacy_event.go b/chain/types/legacy_event.go new file mode 100644 index 000000000..d37a19585 --- /dev/null +++ b/chain/types/legacy_event.go @@ -0,0 +1,91 @@ +package types + +import ( + "bytes" + "fmt" + + "github.com/multiformats/go-multicodec" + cbg "github.com/whyrusleeping/cbor-gen" + + "github.com/filecoin-project/go-state-types/abi" +) + +type LegacyEvent struct { + // The ID of the actor that emitted this event. + Emitter abi.ActorID + + // Key values making up this event. + Entries []LegacyEventEntry +} + +type LegacyEventEntry struct { + // A bitmap conveying metadata or hints about this entry. + Flags uint8 + + // The key of this event entry + Key string + + // The event value + Value []byte +} + +var keyRewrites = map[string]string{ + "topic1": "t1", + "topic2": "t2", + "topic3": "t3", + "topic4": "t4", + "data": "d", +} + +// Adapt method assumes that all events are EVM events (which is the case for +// nv<20, the network versions for which this code is active), and performs the +// following adaptations: +// - Upgrades the schema to new Events, setting codec = Raw. +// - Removes the CBOR framing from values. +// - Left pads EVM log topic entry values to 32 bytes. +func (e *LegacyEvent) Adapt() (Event, error) { + entries := make([]EventEntry, 0, len(e.Entries)) + for _, ee := range e.Entries { + entry := EventEntry{ + Flags: EventFlagIndexedKey | EventFlagIndexedValue, // override the flags as per final version of FIP-0054 + Key: ee.Key, + Codec: uint64(multicodec.Raw), + Value: ee.Value, + } + var err error + entry.Value, err = cbg.ReadByteArray(bytes.NewReader(ee.Value), 4<<20) + if err != nil { + return Event{}, fmt.Errorf("failed to decode event value while adapting: %w", err) + } + if l := len(entry.Value); l < 32 { + pvalue := make([]byte, 32) + copy(pvalue[32-len(entry.Value):], entry.Value) + entry.Value = pvalue + } + if r, ok := keyRewrites[entry.Key]; ok { + entry.Key = r + } + entries = append(entries, entry) + } + return Event{ + Emitter: e.Emitter, + Entries: entries, + }, nil +} + +// AsLegacy strips the codec off an Event object and returns a LegacyEvent. +func (e *Event) AsLegacy() *LegacyEvent { + entries := make([]LegacyEventEntry, 0, len(e.Entries)) + for _, ee := range e.Entries { + entry := LegacyEventEntry{ + Flags: ee.Flags, + Key: ee.Key, + Value: ee.Value, + } + entries = append(entries, entry) + } + return &LegacyEvent{ + Emitter: e.Emitter, + Entries: entries, + } +} diff --git a/chain/vm/fvm.go b/chain/vm/fvm.go index a81bc10d6..ffee730d6 100644 --- a/chain/vm/fvm.go +++ b/chain/vm/fvm.go @@ -287,6 +287,9 @@ func (x *FvmExtern) workerKeyAtLookback(ctx context.Context, minerId address.Add type FVM struct { fvm *ffi.FVM nv network.Version + + // returnEvents specifies whether to parse and return events when applying messages. + returnEvents bool } func defaultFVMOpts(ctx context.Context, opts *VMOpts) (*ffi.FVMOpts, error) { @@ -335,10 +338,13 @@ func NewFVM(ctx context.Context, opts *VMOpts) (*FVM, error) { return nil, xerrors.Errorf("failed to create FVM: %w", err) } - return &FVM{ - fvm: fvm, - nv: opts.NetworkVersion, - }, nil + ret := &FVM{ + fvm: fvm, + nv: opts.NetworkVersion, + returnEvents: opts.ReturnEvents, + } + + return ret, nil } func NewDebugFVM(ctx context.Context, opts *VMOpts) (*FVM, error) { @@ -438,10 +444,13 @@ func NewDebugFVM(ctx context.Context, opts *VMOpts) (*FVM, error) { return nil, err } - return &FVM{ - fvm: fvm, - nv: opts.NetworkVersion, - }, nil + ret := &FVM{ + fvm: fvm, + nv: opts.NetworkVersion, + returnEvents: opts.ReturnEvents, + } + + return ret, nil } func (vm *FVM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet, error) { @@ -493,7 +502,7 @@ func (vm *FVM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet et.Error = aerr.Error() } - return &ApplyRet{ + applyRet := &ApplyRet{ MessageReceipt: receipt, GasCosts: &GasOutputs{ BaseFeeBurn: ret.BaseFeeBurn, @@ -507,7 +516,16 @@ func (vm *FVM) ApplyMessage(ctx context.Context, cmsg types.ChainMsg) (*ApplyRet ActorErr: aerr, ExecutionTrace: et, Duration: duration, - }, nil + } + + if vm.returnEvents && len(ret.EventsBytes) > 0 { + applyRet.Events, err = DecodeEvents(ret.EventsBytes) + if err != nil { + return nil, fmt.Errorf("failed to decode events returned by the FVM: %w", err) + } + } + + return applyRet, nil } func (vm *FVM) ApplyImplicitMessage(ctx context.Context, cmsg *types.Message) (*ApplyRet, error) { @@ -565,6 +583,13 @@ func (vm *FVM) ApplyImplicitMessage(ctx context.Context, cmsg *types.Message) (* Duration: duration, } + if vm.returnEvents && len(ret.EventsBytes) > 0 { + applyRet.Events, err = DecodeEvents(ret.EventsBytes) + if err != nil { + return nil, fmt.Errorf("failed to decode events returned by the FVM: %w", err) + } + } + if ret.ExitCode != 0 { return applyRet, fmt.Errorf("implicit message failed with exit code: %d and error: %w", ret.ExitCode, applyRet.ActorErr) } @@ -682,3 +707,29 @@ func (r *xRedirect) MarshalCBOR(w io.Writer) error { return nil } + +func DecodeEvents(input []byte) ([]cbg.CBORMarshaler, error) { + r := bytes.NewReader(input) + typ, l, err := cbg.NewCborReader(r).ReadHeader() + if err != nil { + return nil, fmt.Errorf("failed to read events: %w", err) + } + if typ != cbg.MajArray { + return nil, fmt.Errorf("expected a CBOR list, was major type %d", typ) + } + + events := make([]cbg.CBORMarshaler, 0, l) + for i := 0; i < int(l); i++ { + var evt types.Event + if err := evt.UnmarshalCBOR(r); err != nil { + return nil, fmt.Errorf("failed to parse event: %w", err) + } + if len(evt.Entries) > 0 && evt.Entries[0].Codec == 0 { + // Codec 0 means this is a legacy event. + events = append(events, evt.AsLegacy()) + } else { + events = append(events, &evt) + } + } + return events, nil +} diff --git a/chain/vm/gas.go b/chain/vm/gas.go index c9007d3f1..cb0c5def9 100644 --- a/chain/vm/gas.go +++ b/chain/vm/gas.go @@ -213,6 +213,16 @@ var Prices = map[abi.ChainEpoch]Pricelist{ verifyReplicaUpdate: 36316136, }, + build.UpgradeHyggeHeight: &pricelistV0{ + computeGasMulti: 1, + storageGasMulti: 1300, // only applies to messages/return values. + + onChainMessageComputeBase: 38863 + 475000, // includes the actor update cost + onChainMessageStorageBase: 36, + onChainMessageStoragePerByte: 1, + + onChainReturnValuePerByte: 1, + }, } // PricelistByEpoch finds the latest prices for the given epoch diff --git a/chain/vm/invoker.go b/chain/vm/invoker.go index 2dd1f37e6..cea17f61d 100644 --- a/chain/vm/invoker.go +++ b/chain/vm/invoker.go @@ -291,7 +291,7 @@ func DumpActorState(i *ActorRegistry, act *types.Actor, b []byte) (interface{}, um := actInfo.vmActor.State() if um == nil { - if act.Code != EmptyObjectCid { + if act.Head != EmptyObjectCid { return nil, xerrors.Errorf("actor with code %s should only have empty object (%s) as its Head, instead has %s", act.Code, EmptyObjectCid, act.Head) } diff --git a/chain/vm/vm.go b/chain/vm/vm.go index e18c4aea9..62d27ee04 100644 --- a/chain/vm/vm.go +++ b/chain/vm/vm.go @@ -236,6 +236,8 @@ type VMOpts struct { LookbackState LookbackStateGetter TipSetGetter TipSetGetter Tracing bool + // ReturnEvents decodes and returns emitted events. + ReturnEvents bool } func NewLegacyVM(ctx context.Context, opts *VMOpts) (*LegacyVM, error) { @@ -282,6 +284,7 @@ type ApplyRet struct { ExecutionTrace types.ExecutionTrace Duration time.Duration GasCosts *GasOutputs + Events []cbg.CBORMarshaler } func (vm *LegacyVM) send(ctx context.Context, msg *types.Message, parent *Runtime, diff --git a/chain/vm/vmi.go b/chain/vm/vmi.go index 01b32d4ad..16bc89162 100644 --- a/chain/vm/vmi.go +++ b/chain/vm/vmi.go @@ -4,7 +4,7 @@ import ( "context" "os" - cid "github.com/ipfs/go-cid" + "github.com/ipfs/go-cid" "github.com/filecoin-project/go-state-types/network" diff --git a/cli/evm.go b/cli/evm.go index 1a1dfa47f..f7b77c870 100644 --- a/cli/evm.go +++ b/cli/evm.go @@ -13,13 +13,13 @@ import ( "golang.org/x/xerrors" "github.com/filecoin-project/go-address" - amt4 "github.com/filecoin-project/go-amt-ipld/v4" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" builtintypes "github.com/filecoin-project/go-state-types/builtin" "github.com/filecoin-project/go-state-types/builtin/v10/eam" "github.com/filecoin-project/lotus/api/v0api" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/actors" "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/types" @@ -112,7 +112,7 @@ var EvmCallSimulateCmd = &cli.Command{ return err } - params, err := ethtypes.DecodeHexString(cctx.Args().Get(2)) + params, err := ethtypes.DecodeHexStringTrimSpace(cctx.Args().Get(2)) if err != nil { return err } @@ -156,7 +156,7 @@ var EvmGetContractAddress = &cli.Command{ return err } - salt, err := ethtypes.DecodeHexString(cctx.Args().Get(1)) + salt, err := ethtypes.DecodeHexStringTrimSpace(cctx.Args().Get(1)) if err != nil { return xerrors.Errorf("Could not decode salt: %w", err) } @@ -175,7 +175,7 @@ var EvmGetContractAddress = &cli.Command{ return err } - contract, err := ethtypes.DecodeHexString(string(contractHex)) + contract, err := ethtypes.DecodeHexStringTrimSpace(string(contractHex)) if err != nil { return xerrors.Errorf("Could not decode contract file: %w", err) } @@ -224,7 +224,7 @@ var EvmDeployCmd = &cli.Command{ return xerrors.Errorf("failed to read contract: %w", err) } if cctx.Bool("hex") { - contract, err = ethtypes.DecodeHexString(string(contract)) + contract, err = ethtypes.DecodeHexStringTrimSpace(string(contract)) if err != nil { return xerrors.Errorf("failed to decode contract: %w", err) } @@ -329,7 +329,7 @@ var EvmInvokeCmd = &cli.Command{ Action: func(cctx *cli.Context) error { afmt := NewAppFmt(cctx.App) - api, closer, err := GetFullNodeAPI(cctx) + api, closer, err := GetFullNodeAPIV1(cctx) if err != nil { return err } @@ -346,7 +346,7 @@ var EvmInvokeCmd = &cli.Command{ } var calldata []byte - calldata, err = ethtypes.DecodeHexString(cctx.Args().Get(1)) + calldata, err = ethtypes.DecodeHexStringTrimSpace(cctx.Args().Get(1)) if err != nil { return xerrors.Errorf("decoding hex input data: %w", err) } @@ -390,7 +390,7 @@ var EvmInvokeCmd = &cli.Command{ } afmt.Println("waiting for message to execute...") - wait, err := api.StateWaitMsg(ctx, smsg.Cid(), 0) + wait, err := api.StateWaitMsg(ctx, smsg.Cid(), 0, build.Finality, true) if err != nil { return xerrors.Errorf("error waiting for message: %w", err) } @@ -415,37 +415,21 @@ var EvmInvokeCmd = &cli.Command{ if eventsRoot := wait.Receipt.EventsRoot; eventsRoot != nil { afmt.Println("Events emitted:") - s := &apiIpldStore{ctx, api} - amt, err := amt4.LoadAMT(ctx, s, *eventsRoot, amt4.UseTreeBitWidth(types.EventAMTBitwidth)) + events, err := api.ChainGetEvents(ctx, *eventsRoot) if err != nil { - return err + return xerrors.Errorf("failed to get events: %w", err) } - - var evt types.Event - err = amt.ForEach(ctx, func(u uint64, deferred *cbg.Deferred) error { - fmt.Printf("%x\n", deferred.Raw) - if err := evt.UnmarshalCBOR(bytes.NewReader(deferred.Raw)); err != nil { - return err - } - if err != nil { - return err - } + for _, evt := range events { fmt.Printf("\tEmitter ID: %s\n", evt.Emitter) for _, e := range evt.Entries { value, err := cbg.ReadByteArray(bytes.NewBuffer(e.Value), uint64(len(e.Value))) if err != nil { return err } - fmt.Printf("\t\tKey: %s, Value: 0x%x, Flags: b%b\n", e.Key, value, e.Flags) + fmt.Printf("\t\tKey: %s, Codec: %d, Value: 0x%x, Flags: b%b\n", e.Key, e.Codec, value, e.Flags) } - return nil - - }) + } } - if err != nil { - return err - } - return nil }, } diff --git a/cli/send.go b/cli/send.go index 3e390584d..e18aa544e 100644 --- a/cli/send.go +++ b/cli/send.go @@ -117,6 +117,17 @@ var sendCmd = &cli.Command{ params.From = faddr } + if params.From.Protocol() == address.Delegated { + if !(params.To.Protocol() == address.ID || params.To.Protocol() == address.Delegated) { + api := srv.FullNodeAPI() + // Resolve id addr if possible. + params.To, err = api.StateLookupID(ctx, params.To, types.EmptyTSK) + if err != nil { + return xerrors.Errorf("f4 addresses can only send to other f4 or id addresses. could not find id address for %s", params.To.String()) + } + } + } + if cctx.IsSet("gas-premium") { gp, err := types.BigFromString(cctx.String("gas-premium")) if err != nil { diff --git a/cli/state.go b/cli/state.go index b904e4ac7..b5f19a4db 100644 --- a/cli/state.go +++ b/cli/state.go @@ -1355,7 +1355,7 @@ func ComputeStateHTMLTempl(w io.Writer, ts *types.TipSet, o *api.ComputeStateOut "GetMethod": getMethod, "ToFil": toFil, "JsonParams": JsonParams, - "JsonReturn": jsonReturn, + "JsonReturn": JsonReturn, "IsSlow": isSlow, "IsVerySlow": isVerySlow, "IntExit": func(i exitcode.ExitCode) int64 { return int64(i) }, @@ -1441,7 +1441,7 @@ func JsonParams(code cid.Cid, method abi.MethodNum, params []byte) (string, erro return string(b), err } -func jsonReturn(code cid.Cid, method abi.MethodNum, ret []byte) (string, error) { +func JsonReturn(code cid.Cid, method abi.MethodNum, ret []byte) (string, error) { methodMeta, found := filcns.NewActorRegistry().Methods[code][method] // TODO: use remote if !found { return "", fmt.Errorf("method %d not found on actor %s", method, code) @@ -1550,7 +1550,7 @@ func printReceiptReturn(ctx context.Context, api v0api.FullNode, m *types.Messag return err } - jret, err := jsonReturn(act.Code, m.Method, r.Return) + jret, err := JsonReturn(act.Code, m.Method, r.Return) if err != nil { return err } @@ -1690,7 +1690,7 @@ var StateCallCmd = &cli.Command{ return xerrors.Errorf("getting actor: %w", err) } - retStr, err := jsonReturn(act.Code, abi.MethodNum(method), ret.MsgRct.Return) + retStr, err := JsonReturn(act.Code, abi.MethodNum(method), ret.MsgRct.Return) if err != nil { return xerrors.Errorf("decoding return: %w", err) } diff --git a/cmd/lotus-shed/balances.go b/cmd/lotus-shed/balances.go index 9ce4faf72..c647f3b8e 100644 --- a/cmd/lotus-shed/balances.go +++ b/cmd/lotus-shed/balances.go @@ -454,8 +454,9 @@ var chainBalanceStateCmd = &cli.Command{ Description: "Produces a csv file of all account balances from a given stateroot", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, &cli.BoolFlag{ Name: "miner-info", @@ -677,8 +678,9 @@ var chainPledgeCmd = &cli.Command{ Description: "Calculate sector pledge numbers", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, }, ArgsUsage: "[stateroot epoch]", diff --git a/cmd/lotus-shed/datastore.go b/cmd/lotus-shed/datastore.go index 5614e34f6..d6b932954 100644 --- a/cmd/lotus-shed/datastore.go +++ b/cmd/lotus-shed/datastore.go @@ -41,6 +41,11 @@ var datastoreListCmd = &cli.Command{ Name: "list", Description: "list datastore keys", Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, + }, &cli.StringFlag{ Name: "repo-type", Usage: "node type (FullNode, StorageMiner, Worker, Wallet)", @@ -110,6 +115,11 @@ var datastoreGetCmd = &cli.Command{ Name: "get", Description: "list datastore keys", Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, + }, &cli.StringFlag{ Name: "repo-type", Usage: "node type (FullNode, StorageMiner, Worker, Wallet)", @@ -123,7 +133,7 @@ var datastoreGetCmd = &cli.Command{ }, ArgsUsage: "[namespace key]", Action: func(cctx *cli.Context) error { - logging.SetLogLevel("badger", "ERROR") // nolint:errcheck + _ = logging.SetLogLevel("badger", "ERROR") r, err := repo.NewFS(cctx.String("repo")) if err != nil { diff --git a/cmd/lotus-shed/deal-label.go b/cmd/lotus-shed/deal-label.go index 417d13701..2baa751a9 100644 --- a/cmd/lotus-shed/deal-label.go +++ b/cmd/lotus-shed/deal-label.go @@ -24,8 +24,9 @@ var dealLabelCmd = &cli.Command{ Usage: "Scrape state to report on how many deals have non UTF-8 labels", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, }, Action: func(cctx *cli.Context) error { diff --git a/cmd/lotus-shed/diff.go b/cmd/lotus-shed/diff.go index 981dc850c..e29d9ca6d 100644 --- a/cmd/lotus-shed/diff.go +++ b/cmd/lotus-shed/diff.go @@ -33,8 +33,9 @@ var diffMinerStates = &cli.Command{ ArgsUsage: " ", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, }, Action: func(cctx *cli.Context) error { diff --git a/cmd/lotus-shed/eth.go b/cmd/lotus-shed/eth.go new file mode 100644 index 000000000..1ebe2fb59 --- /dev/null +++ b/cmd/lotus-shed/eth.go @@ -0,0 +1,72 @@ +package main + +import ( + "fmt" + "reflect" + + "github.com/urfave/cli/v2" + + "github.com/filecoin-project/go-state-types/abi" + + "github.com/filecoin-project/lotus/chain/types" + lcli "github.com/filecoin-project/lotus/cli" +) + +var ethCmd = &cli.Command{ + Name: "eth", + Description: "Ethereum compatibility related commands", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + }, + }, + Subcommands: []*cli.Command{ + checkTipsetsCmd, + }, +} + +var checkTipsetsCmd = &cli.Command{ + Name: "check-tipsets", + Description: "Check that eth_getBlockByNumber and eth_getBlockByHash consistently return tipsets", + Action: func(cctx *cli.Context) error { + api, closer, err := lcli.GetFullNodeAPIV1(cctx) + if err != nil { + return err + } + + defer closer() + ctx := lcli.ReqContext(cctx) + + head, err := api.ChainHead(ctx) + if err != nil { + return err + } + + height := head.Height() + fmt.Println("Current height:", height) + for i := int64(height); i > 0; i-- { + if _, err := api.ChainGetTipSetByHeight(ctx, abi.ChainEpoch(i), types.EmptyTSK); err != nil { + fmt.Printf("[FAIL] failed to get tipset @%d from Lotus: %s\n", i, err) + continue + } + hex := fmt.Sprintf("0x%x", i) + ethBlockA, err := api.EthGetBlockByNumber(ctx, hex, false) + if err != nil { + fmt.Printf("[FAIL] failed to get tipset @%d via eth_getBlockByNumber: %s\n", i, err) + continue + } + ethBlockB, err := api.EthGetBlockByHash(ctx, ethBlockA.Hash, false) + if err != nil { + fmt.Printf("[FAIL] failed to get tipset @%d via eth_getBlockByHash: %s\n", i, err) + continue + } + if equal := reflect.DeepEqual(ethBlockA, ethBlockB); equal { + fmt.Printf("[OK] blocks received via eth_getBlockByNumber and eth_getBlockByHash for tipset @%d are identical\n", i) + } else { + fmt.Printf("[FAIL] blocks received via eth_getBlockByNumber and eth_getBlockByHash for tipset @%d are NOT identical\n", i) + } + } + return nil + }, +} diff --git a/cmd/lotus-shed/export-car.go b/cmd/lotus-shed/export-car.go index 5cb4737ea..e32dbb2cf 100644 --- a/cmd/lotus-shed/export-car.go +++ b/cmd/lotus-shed/export-car.go @@ -35,8 +35,9 @@ var exportCarCmd = &cli.Command{ Description: "Export a car from repo", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, }, ArgsUsage: "[outfile] [root cid]", diff --git a/cmd/lotus-shed/export.go b/cmd/lotus-shed/export.go index 459de3383..ab6d88698 100644 --- a/cmd/lotus-shed/export.go +++ b/cmd/lotus-shed/export.go @@ -42,8 +42,9 @@ var exportChainCmd = &cli.Command{ Description: "Export chain from repo (requires node to be offline)", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, &cli.StringFlag{ Name: "tipset", @@ -146,8 +147,9 @@ var exportRawCmd = &cli.Command{ Description: "Export raw blocks from repo (requires node to be offline)", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, &cli.StringFlag{ Name: "car-size", diff --git a/cmd/lotus-shed/fip-0036.go b/cmd/lotus-shed/fip-0036.go index 485302b9b..1929eb4dd 100644 --- a/cmd/lotus-shed/fip-0036.go +++ b/cmd/lotus-shed/fip-0036.go @@ -58,12 +58,6 @@ var fip36PollCmd = &cli.Command{ Name: "fip36poll", Usage: "Process the FIP0036 FilPoll result", ArgsUsage: "[state root, votes]", - Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", - }, - }, Subcommands: []*cli.Command{ finalResultCmd, }, @@ -75,8 +69,9 @@ var finalResultCmd = &cli.Command{ ArgsUsage: "[state root] [height] [votes json]", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, }, diff --git a/cmd/lotus-shed/gas-estimation.go b/cmd/lotus-shed/gas-estimation.go index cf56ea03d..669c1c345 100644 --- a/cmd/lotus-shed/gas-estimation.go +++ b/cmd/lotus-shed/gas-estimation.go @@ -42,8 +42,9 @@ var gasTraceCmd = &cli.Command{ ArgsUsage: "[migratedStateRootCid networkVersion messageCid]", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, }, Action: func(cctx *cli.Context) error { @@ -146,8 +147,9 @@ var replayOfflineCmd = &cli.Command{ ArgsUsage: "[messageCid]", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, &cli.Int64Flag{ Name: "lookback-limit", diff --git a/cmd/lotus-shed/import-car.go b/cmd/lotus-shed/import-car.go index 973e7b31b..6bba1d980 100644 --- a/cmd/lotus-shed/import-car.go +++ b/cmd/lotus-shed/import-car.go @@ -19,6 +19,13 @@ import ( var importCarCmd = &cli.Command{ Name: "import-car", Description: "Import a car file into node chain blockstore", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, + }, + }, Action: func(cctx *cli.Context) error { r, err := repo.NewFS(cctx.String("repo")) if err != nil { @@ -96,6 +103,13 @@ var importCarCmd = &cli.Command{ var importObjectCmd = &cli.Command{ Name: "import-obj", Usage: "import a raw ipld object into your datastore", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, + }, + }, Action: func(cctx *cli.Context) error { r, err := repo.NewFS(cctx.String("repo")) if err != nil { diff --git a/cmd/lotus-shed/invariants.go b/cmd/lotus-shed/invariants.go index e48f301c4..953674ed0 100644 --- a/cmd/lotus-shed/invariants.go +++ b/cmd/lotus-shed/invariants.go @@ -35,8 +35,9 @@ var invariantsCmd = &cli.Command{ ArgsUsage: "[StateRootCid, height]", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, }, Action: func(cctx *cli.Context) error { diff --git a/cmd/lotus-shed/keyinfo.go b/cmd/lotus-shed/keyinfo.go index 38f5ee6fe..1c322f7d6 100644 --- a/cmd/lotus-shed/keyinfo.go +++ b/cmd/lotus-shed/keyinfo.go @@ -146,9 +146,14 @@ var keyinfoImportCmd = &cli.Command{ Examples env LOTUS_PATH=/var/lib/lotus lotus-shed keyinfo import libp2p-host.keyinfo`, + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, + }, + }, Action: func(cctx *cli.Context) error { - flagRepo := cctx.String("repo") - var input io.Reader if cctx.NArg() == 0 { input = os.Stdin @@ -177,7 +182,7 @@ var keyinfoImportCmd = &cli.Command{ return err } - fsrepo, err := repo.NewFS(flagRepo) + fsrepo, err := repo.NewFS(cctx.String("repo")) if err != nil { return err } diff --git a/cmd/lotus-shed/main.go b/cmd/lotus-shed/main.go index 6f84739fa..c0d0043f1 100644 --- a/cmd/lotus-shed/main.go +++ b/cmd/lotus-shed/main.go @@ -1,7 +1,6 @@ package main import ( - "fmt" "os" logging "github.com/ipfs/go-log/v2" @@ -49,6 +48,7 @@ func main() { minerCmd, mpoolStatsCmd, exportChainCmd, + ethCmd, exportCarCmd, consensusCmd, syncCmd, @@ -88,19 +88,6 @@ func main() { Version: build.UserVersion(), Commands: local, Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "repo", - EnvVars: []string{"LOTUS_PATH"}, - Hidden: true, - Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME - }, - &cli.StringFlag{ - Name: "miner-repo", - Aliases: []string{"storagerepo"}, - EnvVars: []string{"LOTUS_MINER_PATH", "LOTUS_STORAGE_PATH"}, - Value: "~/.lotusminer", // TODO: Consider XDG_DATA_HOME - Usage: fmt.Sprintf("Specify miner repo path. flag storagerepo and env LOTUS_STORAGE_PATH are DEPRECATION, will REMOVE SOON"), - }, &cli.StringFlag{ Name: "log-level", Value: "info", diff --git a/cmd/lotus-shed/market.go b/cmd/lotus-shed/market.go index 0c5e7c81d..6d86c90b9 100644 --- a/cmd/lotus-shed/market.go +++ b/cmd/lotus-shed/market.go @@ -124,8 +124,9 @@ var marketExportDatastoreCmd = &cli.Command{ Description: "export markets datastore key/values to a file", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Usage: "path to the repo", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, &cli.StringFlag{ Name: "backup-dir", @@ -241,8 +242,9 @@ var marketImportDatastoreCmd = &cli.Command{ Description: "import markets datastore key/values from a backup file", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Usage: "path to the repo", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, &cli.StringFlag{ Name: "backup-path", diff --git a/cmd/lotus-shed/migrations.go b/cmd/lotus-shed/migrations.go index a7e0ee34f..e0336283a 100644 --- a/cmd/lotus-shed/migrations.go +++ b/cmd/lotus-shed/migrations.go @@ -54,8 +54,9 @@ var migrationsCmd = &cli.Command{ ArgsUsage: "[new network version, block to look back from]", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, &cli.BoolFlag{ Name: "skip-pre-migration", diff --git a/cmd/lotus-shed/miner-peerid.go b/cmd/lotus-shed/miner-peerid.go index e43063797..fdf8dae8d 100644 --- a/cmd/lotus-shed/miner-peerid.go +++ b/cmd/lotus-shed/miner-peerid.go @@ -26,10 +26,12 @@ import ( var minerPeeridCmd = &cli.Command{ Name: "miner-peerid", - Usage: "Scrape state to find a miner based on peerid", Flags: []cli.Flag{ + Usage: "Scrape state to find a miner based on peerid", + Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, }, Action: func(cctx *cli.Context) error { diff --git a/cmd/lotus-shed/miner-types.go b/cmd/lotus-shed/miner-types.go index 822d037aa..15e5db265 100644 --- a/cmd/lotus-shed/miner-types.go +++ b/cmd/lotus-shed/miner-types.go @@ -28,10 +28,12 @@ import ( var minerTypesCmd = &cli.Command{ Name: "miner-types", - Usage: "Scrape state to report on how many miners of each WindowPoStProofType exist", Flags: []cli.Flag{ + Usage: "Scrape state to report on how many miners of each WindowPoStProofType exist", + Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, }, Action: func(cctx *cli.Context) error { diff --git a/cmd/lotus-shed/msig.go b/cmd/lotus-shed/msig.go index ccc932c93..96ba91219 100644 --- a/cmd/lotus-shed/msig.go +++ b/cmd/lotus-shed/msig.go @@ -43,8 +43,9 @@ var multisigGetAllCmd = &cli.Command{ ArgsUsage: "[state root]", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, }, Action: func(cctx *cli.Context) error { diff --git a/cmd/lotus-shed/nonce-fix.go b/cmd/lotus-shed/nonce-fix.go index d69c8a48d..d68f22e0b 100644 --- a/cmd/lotus-shed/nonce-fix.go +++ b/cmd/lotus-shed/nonce-fix.go @@ -17,12 +17,6 @@ import ( var noncefix = &cli.Command{ Name: "noncefix", Flags: []cli.Flag{ - &cli.StringFlag{ - Name: "repo", - EnvVars: []string{"LOTUS_PATH"}, - Hidden: true, - Value: "~/.lotus", // TODO: Consider XDG_DATA_HOME - }, &cli.Uint64Flag{ Name: "start", }, diff --git a/cmd/lotus-shed/pruning.go b/cmd/lotus-shed/pruning.go index 275f3bc0a..1b6553057 100644 --- a/cmd/lotus-shed/pruning.go +++ b/cmd/lotus-shed/pruning.go @@ -86,8 +86,9 @@ var stateTreePruneCmd = &cli.Command{ Description: "Deletes old state root data from local chainstore", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, &cli.Int64Flag{ Name: "keep-from-lookback", diff --git a/cmd/lotus-shed/splitstore.go b/cmd/lotus-shed/splitstore.go index e8c45a0c5..348f2e36d 100644 --- a/cmd/lotus-shed/splitstore.go +++ b/cmd/lotus-shed/splitstore.go @@ -39,8 +39,9 @@ var splitstoreRollbackCmd = &cli.Command{ Description: "rollbacks a splitstore installation", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, &cli.BoolFlag{ Name: "gc-coldstore", @@ -129,8 +130,9 @@ var splitstoreClearCmd = &cli.Command{ Description: "clears a splitstore installation for restart from snapshot", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, &cli.BoolFlag{ Name: "keys-only", diff --git a/cmd/lotus-shed/terminations.go b/cmd/lotus-shed/terminations.go index c5f35995a..c9e3d26bb 100644 --- a/cmd/lotus-shed/terminations.go +++ b/cmd/lotus-shed/terminations.go @@ -33,8 +33,9 @@ var terminationsCmd = &cli.Command{ ArgsUsage: "[block to look back from] [lookback period (epochs)]", Flags: []cli.Flag{ &cli.StringFlag{ - Name: "repo", - Value: "~/.lotus", + Name: "repo", + Value: "~/.lotus", + EnvVars: []string{"LOTUS_PATH"}, }, }, Action: func(cctx *cli.Context) error { diff --git a/documentation/en/api-v0-methods-miner.md b/documentation/en/api-v0-methods-miner.md index 969cf82df..448412c63 100644 --- a/documentation/en/api-v0-methods-miner.md +++ b/documentation/en/api-v0-methods-miner.md @@ -474,7 +474,7 @@ Inputs: ], "Bw==", 10101, - 19 + 20 ] ``` diff --git a/documentation/en/api-v0-methods.md b/documentation/en/api-v0-methods.md index c10baca18..9bea4b986 100644 --- a/documentation/en/api-v0-methods.md +++ b/documentation/en/api-v0-methods.md @@ -4727,7 +4727,7 @@ Perms: read Inputs: ```json [ - 19 + 20 ] ``` @@ -4742,7 +4742,7 @@ Perms: read Inputs: ```json [ - 19 + 20 ] ``` @@ -6458,7 +6458,7 @@ Inputs: ] ``` -Response: `19` +Response: `20` ### StateReadState StateReadState returns the indicated actor's state. diff --git a/documentation/en/api-v1-unstable-methods.md b/documentation/en/api-v1-unstable-methods.md index 764c77c9b..02509170a 100644 --- a/documentation/en/api-v1-unstable-methods.md +++ b/documentation/en/api-v1-unstable-methods.md @@ -644,6 +644,7 @@ Response: { "Flags": 7, "Key": "string value", + "Codec": 42, "Value": "Ynl0ZSBhcnJheQ==" } ] @@ -2331,18 +2332,14 @@ Perms: read Inputs: ```json [ - "0x5", - "string value", - [ - 12.3 - ] + "Bw==" ] ``` Response: ```json { - "oldestBlock": 42, + "oldestBlock": "0x5", "baseFeePerGas": [ "0x0" ], @@ -2407,7 +2404,7 @@ Response: "gasLimit": "0x5", "gasUsed": "0x5", "timestamp": "0x5", - "extraData": "Ynl0ZSBhcnJheQ==", + "extraData": "0x07", "mixHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", "nonce": "0x0707070707070707", "baseFeePerGas": "0x0", @@ -2451,7 +2448,7 @@ Response: "gasLimit": "0x5", "gasUsed": "0x5", "timestamp": "0x5", - "extraData": "Ynl0ZSBhcnJheQ==", + "extraData": "0x07", "mixHash": "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e", "nonce": "0x0707070707070707", "baseFeePerGas": "0x0", @@ -2643,6 +2640,9 @@ Response: "gas": "0x5", "maxFeePerGas": "0x0", "maxPriorityFeePerGas": "0x0", + "accessList": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], "v": "0x0", "r": "0x0", "s": "0x0" @@ -2679,6 +2679,9 @@ Response: "gas": "0x5", "maxFeePerGas": "0x0", "maxPriorityFeePerGas": "0x0", + "accessList": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], "v": "0x0", "r": "0x0", "s": "0x0" @@ -2714,6 +2717,9 @@ Response: "gas": "0x5", "maxFeePerGas": "0x0", "maxPriorityFeePerGas": "0x0", + "accessList": [ + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" + ], "v": "0x0", "r": "0x0", "s": "0x0" @@ -2784,7 +2790,7 @@ Response: "address": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031", "data": "0x07", "topics": [ - "0x07" + "0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e" ], "removed": true, "logIndex": "0x5", @@ -5955,7 +5961,7 @@ Perms: read Inputs: ```json [ - 19 + 20 ] ``` @@ -5970,7 +5976,7 @@ Perms: read Inputs: ```json [ - 19 + 20 ] ``` @@ -7769,7 +7775,7 @@ Inputs: ] ``` -Response: `19` +Response: `20` ### StateReadState StateReadState returns the indicated actor's state. diff --git a/extern/filecoin-ffi b/extern/filecoin-ffi index d2e527cf6..fead3c628 160000 --- a/extern/filecoin-ffi +++ b/extern/filecoin-ffi @@ -1 +1 @@ -Subproject commit d2e527cf6881fd4f8a9547f15ffa1674a8004ea3 +Subproject commit fead3c628794a332ebc2ef0a153885bfab87a901 diff --git a/extern/test-vectors b/extern/test-vectors index d9a75a787..28b0c45ea 160000 --- a/extern/test-vectors +++ b/extern/test-vectors @@ -1 +1 @@ -Subproject commit d9a75a7873aee0db28b87e3970d2ea16a2f37c6a +Subproject commit 28b0c45eab4c302864af0aeaaff813625cfafe97 diff --git a/gateway/node.go b/gateway/node.go index 56f1f6134..778f9e6f2 100644 --- a/gateway/node.go +++ b/gateway/node.go @@ -94,6 +94,8 @@ type TargetAPI interface { EthGetBlockByHash(ctx context.Context, blkHash ethtypes.EthHash, fullTxInfo bool) (ethtypes.EthBlock, error) EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (ethtypes.EthBlock, error) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.EthHash) (*ethtypes.EthTx, error) + EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) + EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) EthGetTransactionReceipt(ctx context.Context, txHash ethtypes.EthHash) (*api.EthTxReceipt, error) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, txIndex ethtypes.EthUint64) (ethtypes.EthTx, error) @@ -106,7 +108,7 @@ type TargetAPI interface { NetListening(ctx context.Context) (bool, error) EthProtocolVersion(ctx context.Context) (ethtypes.EthUint64, error) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) - EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) + EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (ethtypes.EthUint64, error) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) @@ -120,6 +122,7 @@ type TargetAPI interface { EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) + Web3ClientVersion(ctx context.Context) (string, error) } var _ TargetAPI = *new(api.FullNode) // gateway depends on latest diff --git a/gateway/proxy_eth.go b/gateway/proxy_eth.go index cf65f484c..838a9ba3f 100644 --- a/gateway/proxy_eth.go +++ b/gateway/proxy_eth.go @@ -8,6 +8,7 @@ import ( "sync" "time" + "github.com/ipfs/go-cid" "golang.org/x/xerrors" "github.com/filecoin-project/go-jsonrpc" @@ -19,6 +20,14 @@ import ( "github.com/filecoin-project/lotus/chain/types/ethtypes" ) +func (gw *Node) Web3ClientVersion(ctx context.Context) (string, error) { + if err := gw.limit(ctx, stateRateLimitTokens); err != nil { + return "", err + } + + return gw.target.Web3ClientVersion(ctx) +} + func (gw *Node) EthAccounts(ctx context.Context) ([]ethtypes.EthAddress, error) { // gateway provides public API, so it can't hold user accounts return []ethtypes.EthAddress{}, nil @@ -143,6 +152,22 @@ func (gw *Node) EthGetTransactionByHash(ctx context.Context, txHash *ethtypes.Et return gw.target.EthGetTransactionByHash(ctx, txHash) } +func (gw *Node) EthGetTransactionHashByCid(ctx context.Context, cid cid.Cid) (*ethtypes.EthHash, error) { + if err := gw.limit(ctx, stateRateLimitTokens); err != nil { + return nil, err + } + + return gw.target.EthGetTransactionHashByCid(ctx, cid) +} + +func (gw *Node) EthGetMessageCidByTransactionHash(ctx context.Context, txHash *ethtypes.EthHash) (*cid.Cid, error) { + if err := gw.limit(ctx, stateRateLimitTokens); err != nil { + return nil, err + } + + return gw.target.EthGetMessageCidByTransactionHash(ctx, txHash) +} + func (gw *Node) EthGetTransactionCount(ctx context.Context, sender ethtypes.EthAddress, blkOpt string) (ethtypes.EthUint64, error) { if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return 0, err @@ -269,20 +294,25 @@ func (gw *Node) EthGasPrice(ctx context.Context) (ethtypes.EthBigInt, error) { var EthFeeHistoryMaxBlockCount = 128 // this seems to be expensive; todo: figure out what is a good number that works with everything -func (gw *Node) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) { +func (gw *Node) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) { + params, err := jsonrpc.DecodeParams[ethtypes.EthFeeHistoryParams](p) + if err != nil { + return ethtypes.EthFeeHistory{}, xerrors.Errorf("decoding params: %w", err) + } + if err := gw.limit(ctx, stateRateLimitTokens); err != nil { return ethtypes.EthFeeHistory{}, err } - if err := gw.checkBlkParam(ctx, newestBlk); err != nil { + if err := gw.checkBlkParam(ctx, params.NewestBlkNum); err != nil { return ethtypes.EthFeeHistory{}, err } - if blkCount > ethtypes.EthUint64(EthFeeHistoryMaxBlockCount) { + if params.BlkCount > ethtypes.EthUint64(EthFeeHistoryMaxBlockCount) { return ethtypes.EthFeeHistory{}, fmt.Errorf("block count too high") } - return gw.target.EthFeeHistory(ctx, blkCount, newestBlk, rewardPercentiles) + return gw.target.EthFeeHistory(ctx, p) } func (gw *Node) EthMaxPriorityFeePerGas(ctx context.Context) (ethtypes.EthBigInt, error) { diff --git a/gen/bundle/bundle.go b/gen/bundle/bundle.go index c7655157e..d953b99c9 100644 --- a/gen/bundle/bundle.go +++ b/gen/bundle/bundle.go @@ -20,6 +20,7 @@ var EmbeddedBuiltinActorsMetadata []*BuiltinActorsMetadata = []*BuiltinActorsMet {{- range . }} { Network: {{printf "%q" .Network}}, Version: {{.Version}}, + {{if .BundleGitTag}} BundleGitTag: {{printf "%q" .BundleGitTag}}, {{end}} ManifestCid: MustParseCid({{printf "%q" .ManifestCid}}), Actors: map[string]cid.Cid { {{- range $name, $cid := .Actors }} @@ -37,6 +38,14 @@ func main() { panic(err) } + // TODO: Re-enable this when we can set the tag for ONLY the appropriate version + // https://github.com/filecoin-project/lotus/issues/10185#issuecomment-1422864836 + //if len(os.Args) > 1 { + // for _, m := range metadata { + // m.BundleGitTag = os.Args[1] + // } + //} + fi, err := os.Create("./build/builtin_actors_gen.go") if err != nil { panic(err) diff --git a/gen/inlinegen-data.json b/gen/inlinegen-data.json index ec55f0d52..b8353b96e 100644 --- a/gen/inlinegen-data.json +++ b/gen/inlinegen-data.json @@ -1,7 +1,7 @@ { - "actorVersions": [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], - "latestActorsVersion": 11, + "actorVersions": [0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], + "latestActorsVersion": 12, - "networkVersions": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], - "latestNetworkVersion": 19 + "networkVersions": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], + "latestNetworkVersion": 20 } diff --git a/gen/main.go b/gen/main.go index a3891778a..d4c603ee1 100644 --- a/gen/main.go +++ b/gen/main.go @@ -37,6 +37,8 @@ func main() { types.StateInfo0{}, types.Event{}, types.EventEntry{}, + types.LegacyEvent{}, + types.LegacyEventEntry{}, ) if err != nil { fmt.Println(err) diff --git a/go.mod b/go.mod index de76b6833..6aadba214 100644 --- a/go.mod +++ b/go.mod @@ -44,7 +44,7 @@ require ( 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-9.0.20230127204632-9a067d394297 + github.com/filecoin-project/go-state-types v0.10.0-rc2.0.20230213183937-7f1ede218b87 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 @@ -62,10 +62,12 @@ require ( github.com/gbrlsnchs/jwt/v3 v3.0.1 github.com/gdamore/tcell/v2 v2.2.0 github.com/go-kit/kit v0.12.0 + github.com/go-openapi/spec v0.19.11 github.com/golang/mock v1.6.0 github.com/google/uuid v1.3.0 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.5.0 + github.com/gregdhill/go-openrpc v0.0.0-20220114144539-ae6f44720487 github.com/hako/durafmt v0.0.0-20200710122514-c0fb7b4da026 github.com/hannahhoward/go-pubsub v0.0.0-20200423002714-8d62886cc36e github.com/hashicorp/go-multierror v1.1.1 @@ -108,7 +110,7 @@ require ( github.com/ipld/go-car v0.4.0 github.com/ipld/go-car/v2 v2.5.0 github.com/ipld/go-codec-dagpb v1.5.0 - github.com/ipld/go-ipld-prime v0.18.0 + github.com/ipld/go-ipld-prime v0.20.0 github.com/ipld/go-ipld-selector-text-lite v0.0.1 github.com/kelseyhightower/envconfig v1.4.0 github.com/koalacxr/quantile v0.0.1 @@ -131,6 +133,7 @@ require ( github.com/multiformats/go-multiaddr v0.7.0 github.com/multiformats/go-multiaddr-dns v0.3.1 github.com/multiformats/go-multibase v0.1.1 + github.com/multiformats/go-multicodec v0.8.0 github.com/multiformats/go-multihash v0.2.1 github.com/multiformats/go-varint v0.0.6 github.com/open-rpc/meta-schema v0.0.0-20201029221707-1b72ef2ea333 @@ -145,6 +148,7 @@ require ( github.com/whyrusleeping/cbor-gen v0.0.0-20221021053955-c138aae13722 github.com/whyrusleeping/ledger-filecoin-go v0.9.1-0.20201010031517-c3dcc1bddce4 github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7 + github.com/xeipuuv/gojsonschema v1.2.0 github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 go.opencensus.io v0.23.0 go.opentelemetry.io/otel v1.11.1 @@ -213,7 +217,6 @@ require ( github.com/go-ole/go-ole v1.2.5 // indirect github.com/go-openapi/jsonpointer v0.19.3 // indirect github.com/go-openapi/jsonreference v0.19.4 // indirect - github.com/go-openapi/spec v0.19.11 // indirect github.com/go-openapi/swag v0.19.11 // indirect github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect @@ -292,7 +295,6 @@ require ( github.com/mr-tron/base58 v1.2.0 // indirect github.com/multiformats/go-base36 v0.1.0 // indirect github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect - github.com/multiformats/go-multicodec v0.6.0 // indirect github.com/multiformats/go-multistream v0.3.3 // indirect github.com/nikkolasg/hexjson v0.0.0-20181101101858-78e39397e00c // indirect github.com/nkovacs/streamquote v1.0.0 // indirect @@ -324,6 +326,8 @@ require ( github.com/whyrusleeping/chunker v0.0.0-20181014151217-fe64bd25879f // indirect github.com/whyrusleeping/go-keyspace v0.0.0-20160322163242-5b898ac5add1 // indirect github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee // indirect + github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect + github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/zondax/hid v0.9.1 // indirect github.com/zondax/ledger-go v0.12.1 // indirect diff --git a/go.sum b/go.sum index 196d84a26..28790cad8 100644 --- a/go.sum +++ b/go.sum @@ -356,8 +356,8 @@ github.com/filecoin-project/go-state-types v0.1.0/go.mod h1:ezYnPf0bNkTsDibL/psS github.com/filecoin-project/go-state-types v0.1.6/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q= github.com/filecoin-project/go-state-types v0.1.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-9.0.20230127204632-9a067d394297 h1:EsCsQ/hpz0GhTH7cJ+4PI+3FTFCG+cfKu/mICpfUd2A= -github.com/filecoin-project/go-state-types v0.10.0-alpha-9.0.20230127204632-9a067d394297/go.mod h1:aLIas+W8BWAfpLWEPUOGMPBdhcVwoCG4pIQSQk26024= +github.com/filecoin-project/go-state-types v0.10.0-rc2.0.20230213183937-7f1ede218b87 h1:gqKaMwXp8mrvf1Zj+l8mXUPTC2HYf+TG7rcSivO0nDI= +github.com/filecoin-project/go-state-types v0.10.0-rc2.0.20230213183937-7f1ede218b87/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= @@ -403,8 +403,8 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2 github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og= github.com/frankban/quicktest v1.14.2/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= +github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -451,6 +451,7 @@ github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwoh github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg= github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg= +github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.7/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= github.com/go-openapi/spec v0.19.11 h1:ogU5q8dtp3MMPn59a9VRrPKVxvJHEs5P7yNMR5sNnis= github.com/go-openapi/spec v0.19.11/go.mod h1:vqK/dIdLGCosfvYsQV3WfC7N3TiZSnGY2RZKoFK7X28= @@ -464,6 +465,10 @@ github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LB github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs= +github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q= +github.com/gobuffalo/packr/v2 v2.6.0/go.mod h1:sgEE1xNZ6G0FNN5xn9pevVu4nywaxHvgup67xisti08= github.com/godbus/dbus v0.0.0-20190402143921-271e53dc4968/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -586,6 +591,8 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregdhill/go-openrpc v0.0.0-20220114144539-ae6f44720487 h1:NyaWOSkqFK1d9o+HLfnMIGzrHuUUPeBNIZyi5Zoe/lY= +github.com/gregdhill/go-openrpc v0.0.0-20220114144539-ae6f44720487/go.mod h1:a1eRkbhd3DYpRH2lnuUsVG+QMTI+v0hGnsis8C9hMrA= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.1.0/go.mod h1:f5nM7jw/oeRSadq3xCzHAvxcr8HZnzsqU6ILg/0NiiE= @@ -665,6 +672,7 @@ github.com/icza/backscanner v0.0.0-20210726202459-ac2ffc679f94 h1:9tcYMdi+7Rb1y0 github.com/icza/backscanner v0.0.0-20210726202459-ac2ffc679f94/go.mod h1:GYeBD1CF7AqnKZK+UCytLcY3G+UKo0ByXX/3xfdNyqQ= github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6 h1:8UsGZ2rr2ksmEru6lToqnXgA8Mz1DP11X4zSJ159C3k= github.com/icza/mighty v0.0.0-20180919140131-cfd07d671de6/go.mod h1:xQig96I1VNBDIWGCdTt54nHt6EeI639SmHycLYL7FkA= +github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxdb1-client v0.0.0-20200827194710-b269163b24ab h1:HqW4xhhynfjrtEiiSGcQUd6vrK23iMam1FO8rI7mwig= @@ -891,8 +899,8 @@ github.com/ipld/go-ipld-prime v0.11.0/go.mod h1:+WIAkokurHmZ/KwzDOMUuoeJgaRQktHt github.com/ipld/go-ipld-prime v0.14.0/go.mod h1:9ASQLwUFLptCov6lIYc70GRB4V7UTyLD0IJtrDJe6ZM= github.com/ipld/go-ipld-prime v0.16.0/go.mod h1:axSCuOCBPqrH+gvXr2w9uAOulJqBPhHPT2PjoiiU1qA= github.com/ipld/go-ipld-prime v0.17.0/go.mod h1:aYcKm5TIvGfY8P3QBKz/2gKcLxzJ1zDaD+o0bOowhgs= -github.com/ipld/go-ipld-prime v0.18.0 h1:xUk7NUBSWHEXdjiOu2sLXouFJOMs0yoYzeI5RAqhYQo= -github.com/ipld/go-ipld-prime v0.18.0/go.mod h1:735yXW548CKrLwVCYXzqx90p5deRJMVVxM9eJ4Qe+qE= +github.com/ipld/go-ipld-prime v0.20.0 h1:Ud3VwE9ClxpO2LkCYP7vWPc0Fo+dYdYzgxUJZ3uRG4g= +github.com/ipld/go-ipld-prime v0.20.0/go.mod h1:PzqZ/ZR981eKbgdr3y2DJYeD/8bgMawdGVlJDE8kK+M= github.com/ipld/go-ipld-prime-proto v0.0.0-20191113031812-e32bd156a1e5/go.mod h1:gcvzoEDBjwycpXt3LBE061wT9f46szXGHAmj9uoP6fU= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73 h1:TsyATB2ZRRQGTwafJdgEUQkmjOExRV0DNokcihZxbnQ= github.com/ipld/go-ipld-prime/storage/bsadapter v0.0.0-20211210234204-ce2a1c70cd73/go.mod h1:2PJ0JgxyB08t0b2WKrcuqI3di0V+5n6RS/LTUJhkoxY= @@ -923,6 +931,7 @@ github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901 h1:rp+c0RAYOWj8l6qbCUTSiRLG/iKnW3K3/QfPPuSsBt4= github.com/joeshaw/multierror v0.0.0-20140124173710-69b34d4ec901/go.mod h1:Z86h9688Y0wesXCyonoVr47MasHilkuLMqGhRZ4Hpak= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.1-0.20190114141812-62fb9bc030d1/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= @@ -951,6 +960,7 @@ github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7V github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kabukky/httpscerts v0.0.0-20150320125433-617593d7dcb3/go.mod h1:BYpt4ufZiIGv2nXn4gMxnfKV306n3mWXgNu/d2TqdTU= github.com/kami-zh/go-capturer v0.0.0-20171211120116-e492ea43421d/go.mod h1:P2viExyCEfeWGU259JnaQ34Inuec4R38JCyBx2edgD0= +github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= github.com/kilic/bls12-381 v0.0.0-20200607163746-32e1441c8a9f/go.mod h1:XXfR6YFCRSrkEXbNlIyDsgXVNJWVUV30m/ebkVy9n6s= @@ -976,6 +986,7 @@ github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t github.com/koalacxr/quantile v0.0.1 h1:wAW+SQ286Erny9wOjVww96t8ws+x5Zj6AKHDULUK+o0= github.com/koalacxr/quantile v0.0.1/go.mod h1:bGN/mCZLZ4lrSDHRQ6Lglj9chowGux8sGUIND+DQeD0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/koron/go-ssdp v0.0.0-20180514024734-4a0ed625a78b/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= @@ -986,10 +997,11 @@ github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFB github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= @@ -1511,8 +1523,8 @@ github.com/multiformats/go-multicodec v0.3.1-0.20210902112759-1539a079fd61/go.mo github.com/multiformats/go-multicodec v0.3.1-0.20211210143421-a526f306ed2c/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= github.com/multiformats/go-multicodec v0.4.1/go.mod h1:1Hj/eHRaVWSXiSNNfcEPcwZleTmdNP81xlxDLnWU9GQ= github.com/multiformats/go-multicodec v0.5.0/go.mod h1:DiY2HFaEp5EhEXb/iYzVAunmyX/aSFMxq2KMKfWEues= -github.com/multiformats/go-multicodec v0.6.0 h1:KhH2kSuCARyuJraYMFxrNO3DqIaYhOdS039kbhgVwpE= -github.com/multiformats/go-multicodec v0.6.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= +github.com/multiformats/go-multicodec v0.8.0 h1:evBmgkbSQux+Ds2IgfhkO38Dl2GDtRW8/Rp6YiSHX/Q= +github.com/multiformats/go-multicodec v0.8.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= github.com/multiformats/go-multihash v0.0.1/go.mod h1:w/5tugSrLEbWqlcgJabL3oHFKTwfvkofsjW2Qa1ct4U= github.com/multiformats/go-multihash v0.0.5/go.mod h1:lt/HCbqlQwlPBz7lv0sQCdtfcMtlJvakRUn/0Ual8po= github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= @@ -1693,9 +1705,10 @@ github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XFkP+Eg= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= @@ -1779,6 +1792,7 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3 github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1795,6 +1809,8 @@ github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpP github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE= +github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU= github.com/texttheater/golang-levenshtein v0.0.0-20180516184445-d188e65d659e/go.mod h1:XDKHRm5ThF8YJjx001LtgelzsoaEcvnA7lVWz9EeX3g= github.com/tidwall/gjson v1.6.0 h1:9VEQWz6LLMUsUl6PueE49ir4Ka6CzLymOAZDxpFsTDc= github.com/tidwall/gjson v1.6.0/go.mod h1:P256ACg0Mn+j1RXIDXoss50DeIABTYK1PULOJHhxOls= @@ -1833,8 +1849,8 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= github.com/warpfork/go-testmark v0.3.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= -github.com/warpfork/go-testmark v0.10.0 h1:E86YlUMYfwIacEsQGlnTvjk1IgYkyTGjPhF0RnwTCmw= github.com/warpfork/go-testmark v0.10.0/go.mod h1:jhEf8FVxd+F17juRubpmut64NEG6I2rgkUhlcqqXwE0= +github.com/warpfork/go-testmark v0.11.0 h1:J6LnV8KpceDvo7spaNU4+DauH2n1x+6RaO2rJrmpQ9U= github.com/warpfork/go-wish v0.0.0-20180510122957-5ad1f5abf436/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20190328234359-8b3e70f8e830/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= github.com/warpfork/go-wish v0.0.0-20200122115046-b9ea61034e4a h1:G++j5e0OC488te356JvdhaM8YS6nMsjLAYF7JxCv07w= @@ -1880,6 +1896,12 @@ github.com/whyrusleeping/multiaddr-filter v0.0.0-20160516205228-e903e4adabd7/go. github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee h1:lYbXeSvJi5zk5GLKVuid9TVjS9a0OmLIDKTfoZBL6Ow= github.com/whyrusleeping/timecache v0.0.0-20160911033111-cfcb2f1abfee/go.mod h1:m2aV4LZI4Aez7dP5PMyVKEHhUyEJ/RjmPEDOpDvudHg= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xorcare/golden v0.6.0/go.mod h1:7T39/ZMvaSEZlBPoYfVFmsBLmUl3uz9IuzWj/U6FtvQ= github.com/xorcare/golden v0.6.1-0.20191112154924-b87f686d7542 h1:oWgZJmC1DorFZDpfMfWg7xk29yEOZiXmo/wZl+utTI8= @@ -2004,6 +2026,7 @@ golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190618222545-ea8f1a30c443/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190927123631-a832865fa7ad/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -2191,12 +2214,14 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190523142557-0e01d883c5c5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190524122548-abf6ff778158/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190524152521-dbbf3f1254d4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190526052359-791d8a0f4d09/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190610200419-93c9922d18ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -2308,6 +2333,7 @@ golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= diff --git a/itests/contracts/Create2Factory.hex b/itests/contracts/Create2Factory.hex new file mode 100644 index 000000000..006874306 --- /dev/null +++ b/itests/contracts/Create2Factory.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506109fb806100206000396000f3fe608060405234801561001057600080fd5b506004361061004c5760003560e01c80632b85ba3814610051578063732768dd146100815780637c3e4053146100b2578063bb29998e146100e2575b600080fd5b61006b60048036038101906100669190610473565b610112565b60405161007891906104e1565b60405180910390f35b61009b60048036038101906100969190610473565b61013f565b6040516100a9929190610517565b60405180910390f35b6100cc60048036038101906100c79190610473565b61027e565b6040516100d991906104e1565b60405180910390f35b6100fc60048036038101906100f7919061056c565b6102c0565b60405161010991906104e1565b60405180910390f35b6000816000819055506000806101278461013f565b915091508161013557600080fd5b8092505050919050565b60008060008360405160240161015591906105a8565b6040516020818303038152906040527f7c3e4053000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505090506000803073ffffffffffffffffffffffffffffffffffffffff16836040516101fc9190610634565b600060405180830381855af49150503d8060008114610237576040519150601f19603f3d011682016040523d82523d6000602084013e61023c565b606091505b5091509150811561026e5760008180602001905181019061025d9190610689565b905082819550955050505050610279565b816000945094505050505b915091565b6000818260405161028e9061042b565b61029891906105a8565b8190604051809103906000f59050801580156102b8573d6000803e3d6000fd5b509050919050565b6000808290508073ffffffffffffffffffffffffffffffffffffffff166383197ef06040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561030e57600080fd5b505af1158015610322573d6000803e3d6000fd5b5050505060008173ffffffffffffffffffffffffffffffffffffffff166367e404ce6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610373573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061039791906106cb565b905060006104128373ffffffffffffffffffffffffffffffffffffffff1663bfa0b1336040518163ffffffff1660e01b8152600401602060405180830381865afa1580156103e9573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061040d919061070d565b61013f565b509050801561042057600080fd5b819350505050919050565b61028b8061073b83390190565b600080fd5b6000819050919050565b6104508161043d565b811461045b57600080fd5b50565b60008135905061046d81610447565b92915050565b60006020828403121561048957610488610438565b5b60006104978482850161045e565b91505092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006104cb826104a0565b9050919050565b6104db816104c0565b82525050565b60006020820190506104f660008301846104d2565b92915050565b60008115159050919050565b610511816104fc565b82525050565b600060408201905061052c6000830185610508565b61053960208301846104d2565b9392505050565b610549816104c0565b811461055457600080fd5b50565b60008135905061056681610540565b92915050565b60006020828403121561058257610581610438565b5b600061059084828501610557565b91505092915050565b6105a28161043d565b82525050565b60006020820190506105bd6000830184610599565b92915050565b600081519050919050565b600081905092915050565b60005b838110156105f75780820151818401526020810190506105dc565b60008484015250505050565b600061060e826105c3565b61061881856105ce565b93506106288185602086016105d9565b80840191505092915050565b60006106408284610603565b915081905092915050565b6000610656826104a0565b9050919050565b6106668161064b565b811461067157600080fd5b50565b6000815190506106838161065d565b92915050565b60006020828403121561069f5761069e610438565b5b60006106ad84828501610674565b91505092915050565b6000815190506106c581610540565b92915050565b6000602082840312156106e1576106e0610438565b5b60006106ef848285016106b6565b91505092915050565b60008151905061070781610447565b92915050565b60006020828403121561072357610722610438565b5b6000610731848285016106f8565b9150509291505056fe608060405234801561001057600080fd5b5060405161028b38038061028b833981810160405281019061003291906100ba565b326000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555080600181905550506100e7565b600080fd5b6000819050919050565b61009781610084565b81146100a257600080fd5b50565b6000815190506100b48161008e565b92915050565b6000602082840312156100d0576100cf61007f565b5b60006100de848285016100a5565b91505092915050565b610195806100f66000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806367e404ce1461004657806383197ef014610064578063bfa0b1331461006e575b600080fd5b61004e61008c565b60405161005b9190610110565b60405180910390f35b61006c6100b0565b005b6100766100c9565b6040516100839190610144565b60405180910390f35b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b3373ffffffffffffffffffffffffffffffffffffffff16ff5b60015481565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006100fa826100cf565b9050919050565b61010a816100ef565b82525050565b60006020820190506101256000830184610101565b92915050565b6000819050919050565b61013e8161012b565b82525050565b60006020820190506101596000830184610135565b9291505056fea26469706673582212208252a57fdfc00b722b7063f2d28dd8c5a36b469462c89da52bcf17ccdda2de6764736f6c63430008110033a26469706673582212200edf974d1735ee81f671df7e60300ab28168b8cb22cc3e59180ef743c4a526c964736f6c63430008110033 \ No newline at end of file diff --git a/itests/contracts/Create2Factory.sol b/itests/contracts/Create2Factory.sol new file mode 100644 index 000000000..ffec40822 --- /dev/null +++ b/itests/contracts/Create2Factory.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.17; +contract Create2Factory { + + bytes32 savedSalt; + + // Returns the address of the newly deployed contract + function deploy( + bytes32 _salt + ) public returns (address) { + // This syntax is a newer way to invoke create2 without assembly, you just need to pass salt + // https://docs.soliditylang.org/en/latest/control-structures.html#salted-contract-creations-create2 + savedSalt = _salt; + (bool success, address ret) = deployDelegateCall(_salt); + require(success); + return ret; + } + + function deployDelegateCall( + bytes32 _salt + ) public returns (bool, address) { + bytes memory data = abi.encodeWithSignature("_deploy(bytes32)", _salt); + (bool success, bytes memory returnedData) = address(this).delegatecall(data); + if(success){ + (address ret) = abi.decode(returnedData, (address)); + return (success, ret); + }else{ + return (success, address(0)); + } + } + + function _deploy(bytes32 _salt) public returns (address) { + // https://solidity-by-example.org/app/create1/ + // This syntax is a newer way to invoke create2 without assembly, you just need to pass salt + // https://docs.soliditylang.org/en/latest/control-structures.html#salted-contract-creations-create2 + return address(new SelfDestruct{salt: _salt}(_salt)); + } + + function test(address _address) public returns (address){ + + // run destroy() on _address + SelfDestruct selfDestruct = SelfDestruct(_address); + selfDestruct.destroy(); + + //verify data can still be accessed + address ret = selfDestruct.sender(); + + // attempt and fail to deploy contract using salt + (bool success, ) = deployDelegateCall(selfDestruct.salt()); + require(!success); + + return ret; + } +} + +contract SelfDestruct { + address public sender; + bytes32 public salt; + constructor(bytes32 _salt) { + sender = tx.origin; + salt=_salt; + } + function destroy() public { + selfdestruct(payable(msg.sender)); + } +} + diff --git a/itests/contracts/DeployValueTest.hex b/itests/contracts/DeployValueTest.hex new file mode 100644 index 000000000..535704abf --- /dev/null +++ b/itests/contracts/DeployValueTest.hex @@ -0,0 +1 @@ +60806040523460405161001190610073565b6040518091039082f090508015801561002e573d6000803e3d6000fd5b506000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555061007f565b60c78061031683390190565b6102888061008e6000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80630e3608df146100465780634313b53114610064578063b0d22c6214610082575b600080fd5b61004e6100a0565b60405161005b919061017d565b60405180910390f35b61006c610137565b60405161007991906101d9565b60405180910390f35b61008a61015b565b604051610097919061017d565b60405180910390f35b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166312065fe06040518163ffffffff1660e01b8152600401602060405180830381865afa15801561010e573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101329190610225565b905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006007905090565b6000819050919050565b61017781610164565b82525050565b6000602082019050610192600083018461016e565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101c382610198565b9050919050565b6101d3816101b8565b82525050565b60006020820190506101ee60008301846101ca565b92915050565b600080fd5b61020281610164565b811461020d57600080fd5b50565b60008151905061021f816101f9565b92915050565b60006020828403121561023b5761023a6101f4565b5b600061024984828501610210565b9150509291505056fea2646970667358221220c24abd10dbe58d92bfe62cb351771fcdc45d54241a8ce7085f2a75179c67cd8a64736f6c63430008110033608060405260b5806100126000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806312065fe014602d575b600080fd5b60336047565b604051603e91906066565b60405180910390f35b600047905090565b6000819050919050565b606081604f565b82525050565b6000602082019050607960008301846059565b9291505056fea26469706673582212207123972a300833ee01aebf99e4bdf8ecf9f01c0d3dd776048bd41803c6855c0e64736f6c63430008110033 \ No newline at end of file diff --git a/itests/contracts/DeployValueTest.sol b/itests/contracts/DeployValueTest.sol new file mode 100644 index 000000000..fe261b014 --- /dev/null +++ b/itests/contracts/DeployValueTest.sol @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.17; + + + +contract DeployValueTest { + address public newContract; + + constructor() payable { + newContract = address(new NewContract{value: msg.value}()); + } + + function getConst() public view returns (uint) { + return 7; + } + + function getNewContractBalance() public view returns (uint) { + return NewContract(newContract).getBalance(); + } +} + +contract NewContract { + constructor() payable { + } + + function getBalance() public view returns (uint) { + return address(this).balance; + } +} diff --git a/itests/contracts/EventMatrix.hex b/itests/contracts/EventMatrix.hex index 2b3ad91ad..be831e397 100644 --- a/itests/contracts/EventMatrix.hex +++ b/itests/contracts/EventMatrix.hex @@ -1 +1 @@ -608060405234801561001057600080fd5b506105eb806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063c755553811610071578063c755553814610198578063cbfc3b58146101c6578063cc6f8faf14610212578063cd5b6c3d14610254578063e2a614731461028c578063fb62b28b146102d8576100a9565b80630919b8be146100ae5780636199074d146100e657806366eef3461461012857806375091b1f14610132578063a63ae81a1461016a575b600080fd5b6100e4600480360360408110156100c457600080fd5b81019080803590602001909291908035906020019092919050505061031a565b005b610126600480360360608110156100fc57600080fd5b8101908080359060200190929190803590602001909291908035906020019092919050505061035d565b005b610130610391565b005b6101686004803603604081101561014857600080fd5b8101908080359060200190929190803590602001909291905050506103bf565b005b6101966004803603602081101561018057600080fd5b81019080803590602001909291905050506103fb565b005b6101c4600480360360208110156101ae57600080fd5b8101908080359060200190929190505050610435565b005b610210600480360360808110156101dc57600080fd5b8101908080359060200190929190803590602001909291908035906020019092919080359060200190929190505050610465565b005b6102526004803603606081101561022857600080fd5b810190808035906020019092919080359060200190929190803590602001909291905050506104ba565b005b61028a6004803603604081101561026a57600080fd5b8101908080359060200190929190803590602001909291905050506104f8565b005b6102d6600480360360808110156102a257600080fd5b810190808035906020019092919080359060200190929190803590602001909291908035906020019092919050505061052a565b005b610318600480360360608110156102ee57600080fd5b8101908080359060200190929190803590602001909291908035906020019092919050505061056a565b005b7f5469c6b769315f5668523937f05ca07d4cc87849432bc5f5907f1d90fa73b9f98282604051808381526020018281526020019250505060405180910390a15050565b8082847fb89dabcdb7ff41f1794c0da92f65ece6c19b6b0caeac5407b2a721efe27c080460405160405180910390a4505050565b7fc3f6f1c76bd4e74ee5782052b0b4f8bd5c50b86c3c5a2f52638e03066e50a91b60405160405180910390a1565b817f6709824ebe5f6e620ca3f4b02a3428e8ce2dc97c550816eaeeb3a342b214bd85826040518082815260200191505060405180910390a25050565b7fc804e53d6048af1b3e6a352e246d5f3864fea9d635ace499e023a58c383b3a88816040518082815260200191505060405180910390a150565b807f44a227a31429ab5eb00daf6611c6422f10571619f2267e0e149e9ebe6d2a5d0560405160405180910390a250565b7f28d45631a87b2a52a9625f8520fa37ff8c4d926cdf17042e241985da5cb7b850848484846040518085815260200184815260200183815260200182815260200194505050505060405180910390a150505050565b81837fcd5fe5fbc1d27b90036997224cea7aa565e3779622867265081f636b3a5ccb08836040518082815260200191505060405180910390a3505050565b80827f232f09cef3babc26e58d1cc1346c0a8bc626ffe600c9605b5d747783eda484a760405160405180910390a35050565b8183857f812e73dbcf7e267f27ecb1383bfc902a6650b41b6e7d03ac265108c369673d95846040518082815260200191505060405180910390a450505050565b7fd4d143faaf60340ad98e1f2c96fc26f5695834c21b5200edad339ee7e9a372cc83838360405180848152602001838152602001828152602001935050505060405180910390a150505056fea265627a7a72315820954561fde80ab925299e0a9f3356b01f64fb1976dd335ac2ebd9367441e29f0564736f6c63430005110032 +608060405234801561001057600080fd5b506106af806100206000396000f3fe608060405234801561001057600080fd5b50600436106100a95760003560e01c8063c755553811610071578063c755553814610128578063cbfc3b5814610144578063cc6f8faf14610160578063cd5b6c3d1461017c578063e2a6147314610198578063fb62b28b146101b4576100a9565b80630919b8be146100ae5780636199074d146100ca57806366eef346146100e657806375091b1f146100f0578063a63ae81a1461010c575b600080fd5b6100c860048036038101906100c39190610483565b6101d0565b005b6100e460048036038101906100df91906104c3565b61020d565b005b6100ee610241565b005b61010a60048036038101906101059190610483565b61026f565b005b61012660048036038101906101219190610516565b6102ab565b005b610142600480360381019061013d9190610516565b6102e5565b005b61015e60048036038101906101599190610543565b610315565b005b61017a600480360381019061017591906104c3565b610358565b005b61019660048036038101906101919190610483565b610396565b005b6101b260048036038101906101ad9190610543565b6103c8565b005b6101ce60048036038101906101c991906104c3565b610408565b005b7f5469c6b769315f5668523937f05ca07d4cc87849432bc5f5907f1d90fa73b9f982826040516102019291906105b9565b60405180910390a15050565b8082847fb89dabcdb7ff41f1794c0da92f65ece6c19b6b0caeac5407b2a721efe27c080460405160405180910390a4505050565b7fc3f6f1c76bd4e74ee5782052b0b4f8bd5c50b86c3c5a2f52638e03066e50a91b60405160405180910390a1565b817f6709824ebe5f6e620ca3f4b02a3428e8ce2dc97c550816eaeeb3a342b214bd858260405161029f91906105e2565b60405180910390a25050565b7fc804e53d6048af1b3e6a352e246d5f3864fea9d635ace499e023a58c383b3a88816040516102da91906105e2565b60405180910390a150565b807f44a227a31429ab5eb00daf6611c6422f10571619f2267e0e149e9ebe6d2a5d0560405160405180910390a250565b7f28d45631a87b2a52a9625f8520fa37ff8c4d926cdf17042e241985da5cb7b8508484848460405161034a94939291906105fd565b60405180910390a150505050565b81837fcd5fe5fbc1d27b90036997224cea7aa565e3779622867265081f636b3a5ccb088360405161038991906105e2565b60405180910390a3505050565b80827f232f09cef3babc26e58d1cc1346c0a8bc626ffe600c9605b5d747783eda484a760405160405180910390a35050565b8183857f812e73dbcf7e267f27ecb1383bfc902a6650b41b6e7d03ac265108c369673d95846040516103fa91906105e2565b60405180910390a450505050565b7fd4d143faaf60340ad98e1f2c96fc26f5695834c21b5200edad339ee7e9a372cc83838360405161043b93929190610642565b60405180910390a1505050565b600080fd5b6000819050919050565b6104608161044d565b811461046b57600080fd5b50565b60008135905061047d81610457565b92915050565b6000806040838503121561049a57610499610448565b5b60006104a88582860161046e565b92505060206104b98582860161046e565b9150509250929050565b6000806000606084860312156104dc576104db610448565b5b60006104ea8682870161046e565b93505060206104fb8682870161046e565b925050604061050c8682870161046e565b9150509250925092565b60006020828403121561052c5761052b610448565b5b600061053a8482850161046e565b91505092915050565b6000806000806080858703121561055d5761055c610448565b5b600061056b8782880161046e565b945050602061057c8782880161046e565b935050604061058d8782880161046e565b925050606061059e8782880161046e565b91505092959194509250565b6105b38161044d565b82525050565b60006040820190506105ce60008301856105aa565b6105db60208301846105aa565b9392505050565b60006020820190506105f760008301846105aa565b92915050565b600060808201905061061260008301876105aa565b61061f60208301866105aa565b61062c60408301856105aa565b61063960608301846105aa565b95945050505050565b600060608201905061065760008301866105aa565b61066460208301856105aa565b61067160408301846105aa565b94935050505056fea26469706673582212201b2f4de851da592b926eb2cd07ccfbbd02270fde6dee2459ba942e5dcf5685d364736f6c63430008110033 \ No newline at end of file diff --git a/itests/contracts/EventMatrix.sol b/itests/contracts/EventMatrix.sol index bd008e27b..f1d63c69e 100644 --- a/itests/contracts/EventMatrix.sol +++ b/itests/contracts/EventMatrix.sol @@ -1,4 +1,5 @@ -pragma solidity ^0.5.0; +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0; contract EventMatrix { event EventZeroData(); diff --git a/itests/contracts/ExternalRecursiveCallSimple.hex b/itests/contracts/ExternalRecursiveCallSimple.hex new file mode 100644 index 000000000..03d79fe2d --- /dev/null +++ b/itests/contracts/ExternalRecursiveCallSimple.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506101ee806100206000396000f3fe60806040526004361061001e5760003560e01c8063c38e07dd14610023575b600080fd5b61003d600480360381019061003891906100fe565b61003f565b005b60008111156100c0573073ffffffffffffffffffffffffffffffffffffffff1663c38e07dd600183610071919061015a565b6040518263ffffffff1660e01b815260040161008d919061019d565b600060405180830381600087803b1580156100a757600080fd5b505af11580156100bb573d6000803e3d6000fd5b505050505b50565b600080fd5b6000819050919050565b6100db816100c8565b81146100e657600080fd5b50565b6000813590506100f8816100d2565b92915050565b600060208284031215610114576101136100c3565b5b6000610122848285016100e9565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610165826100c8565b9150610170836100c8565b92508282039050818111156101885761018761012b565b5b92915050565b610197816100c8565b82525050565b60006020820190506101b2600083018461018e565b9291505056fea264697066735822122033d012e17f5d7a62bb724021b5c4e0d109aeb28d1cd5b5c0a0b1b801c0b5032164736f6c63430008110033 \ No newline at end of file diff --git a/itests/contracts/ExternalRecursiveCallSimple.sol b/itests/contracts/ExternalRecursiveCallSimple.sol new file mode 100644 index 000000000..97a27811b --- /dev/null +++ b/itests/contracts/ExternalRecursiveCallSimple.sol @@ -0,0 +1,13 @@ + +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.17; + +contract StackRecCallExp { + function exec1(uint256 r) public payable { + if(r > 0) { + StackRecCallExp(address(this)).exec1(r-1); + } + + return; + } +} diff --git a/itests/contracts/GetDifficulty.hex b/itests/contracts/GetDifficulty.hex new file mode 100644 index 000000000..6d584b233 --- /dev/null +++ b/itests/contracts/GetDifficulty.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5060b58061001f6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063b6baffe314602d575b600080fd5b60336047565b604051603e91906066565b60405180910390f35b600044905090565b6000819050919050565b606081604f565b82525050565b6000602082019050607960008301846059565b9291505056fea2646970667358221220c113f1abaabaed6a0324d363896b0d15a8bca7b9a540948a5be5b636a12a534f64736f6c63430008110033 \ No newline at end of file diff --git a/itests/contracts/GetDifficulty.sol b/itests/contracts/GetDifficulty.sol new file mode 100644 index 000000000..155e7cfd1 --- /dev/null +++ b/itests/contracts/GetDifficulty.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.8.17; + +contract GetDifficulty { + function getDifficulty () public view returns (uint256) { + return block.difficulty; + } +} + diff --git a/itests/contracts/TransparentUpgradeableProxy.hex b/itests/contracts/TransparentUpgradeableProxy.hex new file mode 100644 index 000000000..d3430beee --- /dev/null +++ b/itests/contracts/TransparentUpgradeableProxy.hex @@ -0,0 +1 @@ +608060405234801561001057600080fd5b5061256c806100206000396000f3fe60806040523480156200001157600080fd5b50600436106200003a5760003560e01c806320965255146200003f578063f8a8fd6d1462000061575b600080fd5b620000496200006d565b604051620000589190620002a6565b60405180910390f35b6200006b62000234565b005b6000806040516200007e9062000253565b604051809103906000f0801580156200009b573d6000803e3d6000fd5b509050600081604051620000af9062000261565b620000bb919062000308565b604051809103906000f080158015620000d8573d6000803e3d6000fd5b5090506000604051620000eb906200026f565b604051809103906000f08015801562000108573d6000803e3d6000fd5b5090508173ffffffffffffffffffffffffffffffffffffffff16633659cfe6826040518263ffffffff1660e01b815260040162000146919062000308565b600060405180830381600087803b1580156200016157600080fd5b505af115801562000176573d6000803e3d6000fd5b5050505060006040516200018a906200027d565b604051809103906000f080158015620001a7573d6000803e3d6000fd5b5090508073ffffffffffffffffffffffffffffffffffffffff16633ccc0522846040518263ffffffff1660e01b8152600401620001e5919062000308565b6020604051808303816000875af115801562000205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200022b91906200035b565b94505050505090565b6200023e6200006d565b6000146200025157620002506200038d565b5b565b6103f780620003bd83390190565b61174480620007b483390190565b6103ef8062001ef883390190565b61025080620022e783390190565b6000819050919050565b620002a0816200028b565b82525050565b6000602082019050620002bd600083018462000295565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620002f082620002c3565b9050919050565b6200030281620002e3565b82525050565b60006020820190506200031f6000830184620002f7565b92915050565b600080fd5b62000335816200028b565b81146200034157600080fd5b50565b60008151905062000355816200032a565b92915050565b60006020828403121562000374576200037362000325565b5b6000620003848482850162000344565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fdfe608060405234801561001057600080fd5b506103d7806100206000396000f3fe608060405234801561001057600080fd5b50600436106100455760003560e01c8063209652551461004e578063552410771461006c5780638129fc1c1461008857610046565b5b600180819055005b610056610092565b6040516100639190610218565b60405180910390f35b61008660048036038101906100819190610264565b61009c565b005b6100906100a6565b005b6000600154905090565b8060018190555050565b60008060019054906101000a900460ff161590508080156100d75750600160008054906101000a900460ff1660ff16105b8061010457506100e6306101dc565b1580156101035750600160008054906101000a900460ff1660ff16145b5b610143576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161013a90610314565b60405180910390fd5b60016000806101000a81548160ff021916908360ff1602179055508015610180576001600060016101000a81548160ff0219169083151502179055505b80156101d95760008060016101000a81548160ff0219169083151502179055507f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249860016040516101d09190610386565b60405180910390a15b50565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b610212816101ff565b82525050565b600060208201905061022d6000830184610209565b92915050565b600080fd5b610241816101ff565b811461024c57600080fd5b50565b60008135905061025e81610238565b92915050565b60006020828403121561027a57610279610233565b5b60006102888482850161024f565b91505092915050565b600082825260208201905092915050565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b60006102fe602e83610291565b9150610309826102a2565b604082019050919050565b6000602082019050818103600083015261032d816102f1565b9050919050565b6000819050919050565b600060ff82169050919050565b6000819050919050565b600061037061036b61036684610334565b61034b565b61033e565b9050919050565b61038081610355565b82525050565b600060208201905061039b6000830184610377565b9291505056fea2646970667358221220934a8ba055f2e0df7da7dde7999f38a4972809a218a2a064683da10ad547f1fd64736f6c63430008110033608060405260405162001744380380620017448339818101604052810190620000299190620005c7565b80604051806020016040528060008152506200004e828260006200006860201b60201c565b50506200006133620000ab60201b60201c565b50620008fd565b62000079836200010960201b60201c565b600082511180620000875750805b15620000a657620000a483836200016060201b620003361760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f620000dc6200019660201b60201c565b82604051620000ed9291906200060a565b60405180910390a16200010681620001fa60201b60201c565b50565b6200011a81620002ea60201b60201c565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b60606200018e83836040518060600160405280602781526020016200171d60279139620003c060201b60201c565b905092915050565b6000620001d17fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610360001b6200045260201b620003631760201c565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036200026c576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016200026390620006be565b60405180910390fd5b80620002a67fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610360001b6200045260201b620003631760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b62000300816200045c60201b6200036d1760201c565b62000342576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620003399062000756565b60405180910390fd5b806200037c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b6200045260201b620003631760201c565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60606000808573ffffffffffffffffffffffffffffffffffffffff1685604051620003ec9190620007f1565b600060405180830381855af49150503d806000811462000429576040519150601f19603f3d011682016040523d82523d6000602084013e6200042e565b606091505b509150915062000447868383876200047f60201b60201c565b925050509392505050565b6000819050919050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b60608315620004ef576000835103620004e657620004a3856200045c60201b60201c565b620004e5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620004dc906200085a565b60405180910390fd5b5b82905062000502565b6200050183836200050a60201b60201c565b5b949350505050565b6000825111156200051e5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401620005549190620008d9565b60405180910390fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200058f8262000562565b9050919050565b620005a18162000582565b8114620005ad57600080fd5b50565b600081519050620005c18162000596565b92915050565b600060208284031215620005e057620005df6200055d565b5b6000620005f084828501620005b0565b91505092915050565b620006048162000582565b82525050565b6000604082019050620006216000830185620005f9565b620006306020830184620005f9565b9392505050565b600082825260208201905092915050565b7f455243313936373a206e65772061646d696e20697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000620006a660268362000637565b9150620006b38262000648565b604082019050919050565b60006020820190508181036000830152620006d98162000697565b9050919050565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b60006200073e602d8362000637565b91506200074b82620006e0565b604082019050919050565b6000602082019050818103600083015262000771816200072f565b9050919050565b600081519050919050565b600081905092915050565b60005b83811015620007ae57808201518184015260208101905062000791565b60008484015250505050565b6000620007c78262000778565b620007d3818562000783565b9350620007e58185602086016200078e565b80840191505092915050565b6000620007ff8284620007ba565b915081905092915050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b600062000842601d8362000637565b91506200084f826200080a565b602082019050919050565b60006020820190508181036000830152620008758162000833565b9050919050565b600081519050919050565b6000601f19601f8301169050919050565b6000620008a5826200087c565b620008b1818562000637565b9350620008c38185602086016200078e565b620008ce8162000887565b840191505092915050565b60006020820190508181036000830152620008f5818462000898565b905092915050565b610e10806200090d6000396000f3fe60806040526004361061004e5760003560e01c80633659cfe6146100675780634f1ef286146100835780635c60da1b1461009f5780638f283970146100bd578063f851a440146100d95761005d565b3661005d5761005b6100f7565b005b6100656100f7565b005b610081600480360381019061007c9190610916565b610111565b005b61009d600480360381019061009891906109a8565b61017f565b005b6100a761021c565b6040516100b49190610a17565b60405180910390f35b6100d760048036038101906100d29190610916565b61027b565b005b6100e16102d7565b6040516100ee9190610a17565b60405180910390f35b6100ff610390565b61010f61010a61040f565b61041e565b565b610119610444565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16036101735761015361049b565b61016e816040518060200160405280600081525060006104aa565b61017c565b61017b6100f7565b5b50565b610187610444565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff160361020e576102098383838080601f016020809104026020016040519081016040528093929190818152602001838380828437600081840152601f19601f8201169050808301925050505050505060016104aa565b610217565b6102166100f7565b5b505050565b6000610226610444565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff160361026f5761026061049b565b61026861040f565b9050610278565b6102776100f7565b5b90565b610283610444565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16036102cb576102bd61049b565b6102c6816104d6565b6102d4565b6102d36100f7565b5b50565b60006102e1610444565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff160361032a5761031b61049b565b610323610444565b9050610333565b6103326100f7565b5b90565b606061035b8383604051806060016040528060278152602001610db460279139610522565b905092915050565b6000819050919050565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b610398610444565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1603610405576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016103fc90610adb565b60405180910390fd5b61040d6105a8565b565b60006104196105aa565b905090565b3660008037600080366000845af43d6000803e806000811461043f573d6000f35b3d6000fd5b60006104727fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610360001b610363565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b600034146104a857600080fd5b565b6104b383610601565b6000825111806104c05750805b156104d1576104cf8383610336565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6104ff610444565b8260405161050e929190610afb565b60405180910390a161051f81610650565b50565b60606000808573ffffffffffffffffffffffffffffffffffffffff168560405161054c9190610b95565b600060405180830381855af49150503d8060008114610587576040519150601f19603f3d011682016040523d82523d6000602084013e61058c565b606091505b509150915061059d86838387610730565b925050509392505050565b565b60006105d87f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b610363565b60000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b61060a816107a5565b8073ffffffffffffffffffffffffffffffffffffffff167fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b60405160405180910390a250565b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036106bf576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016106b690610c1e565b60405180910390fd5b806106ec7fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d610360001b610363565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6060831561079257600083510361078a5761074a8561036d565b610789576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161078090610c8a565b60405180910390fd5b5b82905061079d565b61079c838361085e565b5b949350505050565b6107ae8161036d565b6107ed576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107e490610d1c565b60405180910390fd5b8061081a7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b610363565b60000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6000825111156108715781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016108a59190610d91565b60405180910390fd5b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006108e3826108b8565b9050919050565b6108f3816108d8565b81146108fe57600080fd5b50565b600081359050610910816108ea565b92915050565b60006020828403121561092c5761092b6108ae565b5b600061093a84828501610901565b91505092915050565b600080fd5b600080fd5b600080fd5b60008083601f84011261096857610967610943565b5b8235905067ffffffffffffffff81111561098557610984610948565b5b6020830191508360018202830111156109a1576109a061094d565b5b9250929050565b6000806000604084860312156109c1576109c06108ae565b5b60006109cf86828701610901565b935050602084013567ffffffffffffffff8111156109f0576109ef6108b3565b5b6109fc86828701610952565b92509250509250925092565b610a11816108d8565b82525050565b6000602082019050610a2c6000830184610a08565b92915050565b600082825260208201905092915050565b7f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60008201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f7879207461726760208201527f6574000000000000000000000000000000000000000000000000000000000000604082015250565b6000610ac5604283610a32565b9150610ad082610a43565b606082019050919050565b60006020820190508181036000830152610af481610ab8565b9050919050565b6000604082019050610b106000830185610a08565b610b1d6020830184610a08565b9392505050565b600081519050919050565b600081905092915050565b60005b83811015610b58578082015181840152602081019050610b3d565b60008484015250505050565b6000610b6f82610b24565b610b798185610b2f565b9350610b89818560208601610b3a565b80840191505092915050565b6000610ba18284610b64565b915081905092915050565b7f455243313936373a206e65772061646d696e20697320746865207a65726f206160008201527f6464726573730000000000000000000000000000000000000000000000000000602082015250565b6000610c08602683610a32565b9150610c1382610bac565b604082019050919050565b60006020820190508181036000830152610c3781610bfb565b9050919050565b7f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000600082015250565b6000610c74601d83610a32565b9150610c7f82610c3e565b602082019050919050565b60006020820190508181036000830152610ca381610c67565b9050919050565b7f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60008201527f6f74206120636f6e747261637400000000000000000000000000000000000000602082015250565b6000610d06602d83610a32565b9150610d1182610caa565b604082019050919050565b60006020820190508181036000830152610d3581610cf9565b9050919050565b600081519050919050565b6000601f19601f8301169050919050565b6000610d6382610d3c565b610d6d8185610a32565b9350610d7d818560208601610b3a565b610d8681610d47565b840191505092915050565b60006020820190508181036000830152610dab8184610d58565b90509291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e09e1af8aa595ace2cac727dfa78b8a1c201fdfb2f2b3364541017dec279304b64736f6c63430008110033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564608060405234801561001057600080fd5b506103cf806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c8063209652551461004657806355241077146100645780638129fc1c14610080575b600080fd5b61004e61008a565b60405161005b9190610210565b60405180910390f35b61007e6004803603810190610079919061025c565b610094565b005b61008861009e565b005b6000600154905090565b8060018190555050565b60008060019054906101000a900460ff161590508080156100cf5750600160008054906101000a900460ff1660ff16105b806100fc57506100de306101d4565b1580156100fb5750600160008054906101000a900460ff1660ff16145b5b61013b576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101329061030c565b60405180910390fd5b60016000806101000a81548160ff021916908360ff1602179055508015610178576001600060016101000a81548160ff0219169083151502179055505b80156101d15760008060016101000a81548160ff0219169083151502179055507f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb384740249860016040516101c8919061037e565b60405180910390a15b50565b6000808273ffffffffffffffffffffffffffffffffffffffff163b119050919050565b6000819050919050565b61020a816101f7565b82525050565b60006020820190506102256000830184610201565b92915050565b600080fd5b610239816101f7565b811461024457600080fd5b50565b60008135905061025681610230565b92915050565b6000602082840312156102725761027161022b565b5b600061028084828501610247565b91505092915050565b600082825260208201905092915050565b7f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160008201527f647920696e697469616c697a6564000000000000000000000000000000000000602082015250565b60006102f6602e83610289565b91506103018261029a565b604082019050919050565b60006020820190508181036000830152610325816102e9565b9050919050565b6000819050919050565b600060ff82169050919050565b6000819050919050565b600061036861036361035e8461032c565b610343565b610336565b9050919050565b6103788161034d565b82525050565b6000602082019050610393600083018461036f565b9291505056fea2646970667358221220f2a2169e9ff5b35281e8323276adc729174eda42cfb39d55a7943efc6c70485164736f6c63430008110033608060405234801561001057600080fd5b50610230806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80633ccc052214610030575b600080fd5b61004a60048036038101906100459190610140565b610060565b6040516100579190610186565b60405180910390f35b6000808290508073ffffffffffffffffffffffffffffffffffffffff1663209652556040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100d591906101cd565b915050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061010d826100e2565b9050919050565b61011d81610102565b811461012857600080fd5b50565b60008135905061013a81610114565b92915050565b600060208284031215610156576101556100dd565b5b60006101648482850161012b565b91505092915050565b6000819050919050565b6101808161016d565b82525050565b600060208201905061019b6000830184610177565b92915050565b6101aa8161016d565b81146101b557600080fd5b50565b6000815190506101c7816101a1565b92915050565b6000602082840312156101e3576101e26100dd565b5b60006101f1848285016101b8565b9150509291505056fea2646970667358221220b98dab0efb759c0eea90d951be7f01d309e1c04588076d429d10680bddf1351864736f6c63430008110033a264697066735822122024666606126645f287960fe9548cc80d17258ad514d74180c7af0e1ee954bd4364736f6c63430008110033 \ No newline at end of file diff --git a/itests/contracts/TransparentUpgradeableProxy.sol b/itests/contracts/TransparentUpgradeableProxy.sol new file mode 100644 index 000000000..5bb3d0e5d --- /dev/null +++ b/itests/contracts/TransparentUpgradeableProxy.sol @@ -0,0 +1,590 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +abstract contract Initializable { + uint8 private _initialized; + + bool private _initializing; + + event Initialized(uint8 version); + + modifier initializer() { + bool isTopLevelCall = !_initializing; + require( + (isTopLevelCall && _initialized < 1) || + (!Address.isContract(address(this)) && _initialized == 1), + "Initializable: contract is already initialized" + ); + _initialized = 1; + if (isTopLevelCall) { + _initializing = true; + } + _; + if (isTopLevelCall) { + _initializing = false; + emit Initialized(1); + } + } + + modifier reinitializer(uint8 version) { + require( + !_initializing && _initialized < version, + "Initializable: contract is already initialized" + ); + _initialized = version; + _initializing = true; + _; + _initializing = false; + emit Initialized(version); + } + + modifier onlyInitializing() { + require(_initializing, "Initializable: contract is not initializing"); + _; + } + + function _disableInitializers() internal virtual { + require(!_initializing, "Initializable: contract is initializing"); + if (_initialized != type(uint8).max) { + _initialized = type(uint8).max; + emit Initialized(type(uint8).max); + } + } + + function _getInitializedVersion() internal view returns (uint8) { + return _initialized; + } + + function _isInitializing() internal view returns (bool) { + return _initializing; + } +} + +contract Implementation4 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } + + function getValue() public view returns (uint256) { + return _value; + } + + fallback() external { + _value = 1; + } +} + +contract Implementation2 is Initializable { + uint256 internal _value; + + function initialize() public initializer {} + + function setValue(uint256 _number) public { + _value = _number; + } + + function getValue() public view returns (uint256) { + return _value; + } +} + +abstract contract Proxy { + function _delegate(address implementation) internal virtual { + assembly { + calldatacopy(0, 0, calldatasize()) + + let result := delegatecall( + gas(), + implementation, + 0, + calldatasize(), + 0, + 0 + ) + + returndatacopy(0, 0, returndatasize()) + + switch result + case 0 { + revert(0, returndatasize()) + } + default { + return(0, returndatasize()) + } + } + } + + function _implementation() internal view virtual returns (address); + + function _fallback() internal virtual { + _beforeFallback(); + _delegate(_implementation()); + } + + fallback() external payable virtual { + _fallback(); + } + + receive() external payable virtual { + _fallback(); + } + + function _beforeFallback() internal virtual {} +} + +interface IBeacon { + function implementation() external view returns (address); +} + +interface IERC1822Proxiable { + function proxiableUUID() external view returns (bytes32); +} + +library Address { + function isContract(address account) internal view returns (bool) { + return account.code.length > 0; + } + + function sendValue(address payable recipient, uint256 amount) internal { + require( + address(this).balance >= amount, + "Address: insufficient balance" + ); + + (bool success, ) = recipient.call{value: amount}(""); + require( + success, + "Address: unable to send value, recipient may have reverted" + ); + } + + function functionCall( + address target, + bytes memory data + ) internal returns (bytes memory) { + return + functionCallWithValue( + target, + data, + 0, + "Address: low-level call failed" + ); + } + + function functionCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + return functionCallWithValue(target, data, 0, errorMessage); + } + + function functionCallWithValue( + address target, + bytes memory data, + uint256 value + ) internal returns (bytes memory) { + return + functionCallWithValue( + target, + data, + value, + "Address: low-level call with value failed" + ); + } + + function functionCallWithValue( + address target, + bytes memory data, + uint256 value, + string memory errorMessage + ) internal returns (bytes memory) { + require( + address(this).balance >= value, + "Address: insufficient balance for call" + ); + (bool success, bytes memory returndata) = target.call{value: value}( + data + ); + return + verifyCallResultFromTarget( + target, + success, + returndata, + errorMessage + ); + } + + function functionStaticCall( + address target, + bytes memory data + ) internal view returns (bytes memory) { + return + functionStaticCall( + target, + data, + "Address: low-level static call failed" + ); + } + + function functionStaticCall( + address target, + bytes memory data, + string memory errorMessage + ) internal view returns (bytes memory) { + (bool success, bytes memory returndata) = target.staticcall(data); + return + verifyCallResultFromTarget( + target, + success, + returndata, + errorMessage + ); + } + + function functionDelegateCall( + address target, + bytes memory data + ) internal returns (bytes memory) { + return + functionDelegateCall( + target, + data, + "Address: low-level delegate call failed" + ); + } + + function functionDelegateCall( + address target, + bytes memory data, + string memory errorMessage + ) internal returns (bytes memory) { + (bool success, bytes memory returndata) = target.delegatecall(data); + return + verifyCallResultFromTarget( + target, + success, + returndata, + errorMessage + ); + } + + function verifyCallResultFromTarget( + address target, + bool success, + bytes memory returndata, + string memory errorMessage + ) internal view returns (bytes memory) { + if (success) { + if (returndata.length == 0) { + require(isContract(target), "Address: call to non-contract"); + } + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + function verifyCallResult( + bool success, + bytes memory returndata, + string memory errorMessage + ) internal pure returns (bytes memory) { + if (success) { + return returndata; + } else { + _revert(returndata, errorMessage); + } + } + + function _revert( + bytes memory returndata, + string memory errorMessage + ) private pure { + if (returndata.length > 0) { + assembly { + let returndata_size := mload(returndata) + revert(add(32, returndata), returndata_size) + } + } else { + revert(errorMessage); + } + } +} + +library StorageSlot { + struct AddressSlot { + address value; + } + + struct BooleanSlot { + bool value; + } + + struct Bytes32Slot { + bytes32 value; + } + + struct Uint256Slot { + uint256 value; + } + + function getAddressSlot( + bytes32 slot + ) internal pure returns (AddressSlot storage r) { + assembly { + r.slot := slot + } + } + + function getBooleanSlot( + bytes32 slot + ) internal pure returns (BooleanSlot storage r) { + assembly { + r.slot := slot + } + } + + function getBytes32Slot( + bytes32 slot + ) internal pure returns (Bytes32Slot storage r) { + assembly { + r.slot := slot + } + } + + function getUint256Slot( + bytes32 slot + ) internal pure returns (Uint256Slot storage r) { + assembly { + r.slot := slot + } + } +} + +abstract contract ERC1967Upgrade { + bytes32 private constant _ROLLBACK_SLOT = + 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; + + bytes32 internal constant _IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + + event Upgraded(address indexed implementation); + + function _getImplementation() internal view returns (address) { + return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; + } + + function _setImplementation(address newImplementation) private { + require( + Address.isContract(newImplementation), + "ERC1967: new implementation is not a contract" + ); + StorageSlot + .getAddressSlot(_IMPLEMENTATION_SLOT) + .value = newImplementation; + } + + function _upgradeTo(address newImplementation) internal { + _setImplementation(newImplementation); + emit Upgraded(newImplementation); + } + + function _upgradeToAndCall( + address newImplementation, + bytes memory data, + bool forceCall + ) internal { + _upgradeTo(newImplementation); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall(newImplementation, data); + } + } + + function _upgradeToAndCallUUPS( + address newImplementation, + bytes memory data, + bool forceCall + ) internal { + if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) { + _setImplementation(newImplementation); + } else { + try IERC1822Proxiable(newImplementation).proxiableUUID() returns ( + bytes32 slot + ) { + require( + slot == _IMPLEMENTATION_SLOT, + "ERC1967Upgrade: unsupported proxiableUUID" + ); + } catch { + revert("ERC1967Upgrade: new implementation is not UUPS"); + } + _upgradeToAndCall(newImplementation, data, forceCall); + } + } + + bytes32 internal constant _ADMIN_SLOT = + 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; + + event AdminChanged(address previousAdmin, address newAdmin); + + function _getAdmin() internal view returns (address) { + return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; + } + + function _setAdmin(address newAdmin) private { + require( + newAdmin != address(0), + "ERC1967: new admin is the zero address" + ); + StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; + } + + function _changeAdmin(address newAdmin) internal { + emit AdminChanged(_getAdmin(), newAdmin); + _setAdmin(newAdmin); + } + + bytes32 internal constant _BEACON_SLOT = + 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; + + event BeaconUpgraded(address indexed beacon); + + function _getBeacon() internal view returns (address) { + return StorageSlot.getAddressSlot(_BEACON_SLOT).value; + } + + function _setBeacon(address newBeacon) private { + require( + Address.isContract(newBeacon), + "ERC1967: new beacon is not a contract" + ); + require( + Address.isContract(IBeacon(newBeacon).implementation()), + "ERC1967: beacon implementation is not a contract" + ); + StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; + } + + function _upgradeBeaconToAndCall( + address newBeacon, + bytes memory data, + bool forceCall + ) internal { + _setBeacon(newBeacon); + emit BeaconUpgraded(newBeacon); + if (data.length > 0 || forceCall) { + Address.functionDelegateCall( + IBeacon(newBeacon).implementation(), + data + ); + } + } +} + +contract ERC1967Proxy is Proxy, ERC1967Upgrade { + constructor(address _logic, bytes memory _data) payable { + _upgradeToAndCall(_logic, _data, false); + } + + function _implementation() + internal + view + virtual + override + returns (address impl) + { + return ERC1967Upgrade._getImplementation(); + } +} + +contract TransparentUpgradeableProxy is ERC1967Proxy { + constructor(address _logic) payable ERC1967Proxy(_logic, "") { + _changeAdmin(msg.sender); + } + + modifier ifAdmin() { + if (msg.sender == _getAdmin()) { + _; + } else { + _fallback(); + } + } + + function admin() external payable ifAdmin returns (address admin_) { + _requireZeroValue(); + admin_ = _getAdmin(); + } + + function implementation() + external + payable + ifAdmin + returns (address implementation_) + { + _requireZeroValue(); + implementation_ = _implementation(); + } + + function changeAdmin(address newAdmin) external payable virtual ifAdmin { + _requireZeroValue(); + _changeAdmin(newAdmin); + } + + function upgradeTo(address newImplementation) external payable ifAdmin { + _requireZeroValue(); + _upgradeToAndCall(newImplementation, bytes(""), false); + } + + function upgradeToAndCall( + address newImplementation, + bytes calldata data + ) external payable ifAdmin { + _upgradeToAndCall(newImplementation, data, true); + } + + function _admin() internal view virtual returns (address) { + return _getAdmin(); + } + + function _beforeFallback() internal virtual override { + require( + msg.sender != _getAdmin(), + "TransparentUpgradeableProxy: admin cannot fallback to proxy target" + ); + super._beforeFallback(); + } + + function _requireZeroValue() private { + require(msg.value == 0); + } +} + +contract TestHelper { + function getValue(address proxyAddress) public returns (uint256) { + Implementation2 proxyInstance2 = Implementation2(proxyAddress); + return proxyInstance2.getValue(); + } +} + +contract TransparentUpgradeableProxyTestRunner { + function test() public { + assert(0 == getValue()); + } + + function getValue() public returns (uint256) { + Implementation4 instance4 = new Implementation4(); + TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy( + address(instance4) + ); + Implementation2 instance2 = new Implementation2(); + proxy.upgradeTo(address(instance2)); + //use helper because proxy admin can't call getValue() + TestHelper h = new TestHelper(); + return h.getValue(address(proxy)); + } +} diff --git a/itests/contracts/compile.sh b/itests/contracts/compile.sh old mode 100755 new mode 100644 index 4395fdb53..31bff4b68 --- a/itests/contracts/compile.sh +++ b/itests/contracts/compile.sh @@ -5,15 +5,19 @@ set -o pipefail # 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 . -name \*.sol -print0 | +find . -maxdepth 1 -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 +for filename in Constructor TestApp ValueSender Create2Factory DeployValueTest; do echo $filename solc --bin $filename.sol | tail -n5|head -n1 | tr -d "\n" > $filename.hex done +for filename in TransparentUpgradeableProxy ; do + echo $filename + solc --bin $filename.sol | tail -n1| tr -d "\n" > $filename.hex +done diff --git a/itests/decode_params_test.go b/itests/decode_params_test.go new file mode 100644 index 000000000..6a4a8c681 --- /dev/null +++ b/itests/decode_params_test.go @@ -0,0 +1,124 @@ +// stm: #integration +package itests + +import ( + "bytes" + "encoding/json" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/go-state-types/builtin/v10/eam" + "github.com/filecoin-project/go-state-types/cbor" + "github.com/filecoin-project/go-state-types/manifest" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors" + "github.com/filecoin-project/lotus/cli" +) + +type marshalable interface { + cbor.Marshaler + cbor.Unmarshaler +} + +type testCase struct { + ActorKey string + MethodNum abi.MethodNum + retVal marshalable +} + +// Used './lotus state replay --show-trace ' to get params/return to decode. +func TestDecodeParams(t *testing.T) { + testCborBytes := abi.CborBytes([]byte{1, 2, 3}) + + testCases := []testCase{ + { + ActorKey: manifest.EvmKey, + MethodNum: builtin.MethodsEVM.InvokeContract, + retVal: &testCborBytes, + }, + { + ActorKey: manifest.EamKey, + MethodNum: builtin.MethodsEAM.CreateExternal, + retVal: &testCborBytes, + }, + } + + for _, _tc := range testCases { + tc := _tc + t.Run(tc.ActorKey+" "+tc.MethodNum.String(), func(t *testing.T) { + av, err := actorstypes.VersionForNetwork(build.TestNetworkVersion) + require.NoError(t, err) + actorCodeCid, found := actors.GetActorCodeID(av, tc.ActorKey) + require.True(t, found) + + buf := bytes.NewBuffer(nil) + if err := tc.retVal.MarshalCBOR(buf); err != nil { + t.Fatal(err) + } + + paramString, err := cli.JsonParams(actorCodeCid, tc.MethodNum, buf.Bytes()) + require.NoError(t, err) + + jsonParams, err := json.MarshalIndent(tc.retVal, "", " ") + require.NoError(t, err) + require.Equal(t, string(jsonParams), paramString) + }) + } +} + +func TestDecodeReturn(t *testing.T) { + testCborBytes := abi.CborBytes([]byte{1, 2, 3}) + + robustAddr, err := address.NewIDAddress(12345) + require.NoError(t, err) + + //ethAddr, err := ethtypes.ParseEthAddress("d4c5fb16488Aa48081296299d54b0c648C9333dA") + //require.NoError(t, err) + + testReturn := eam.CreateExternalReturn{ + ActorID: 12345, + RobustAddress: &robustAddr, + EthAddress: [20]byte{}, + } + + testCases := []testCase{ + { + ActorKey: manifest.EvmKey, + MethodNum: builtin.MethodsEVM.InvokeContract, + retVal: &testCborBytes, + }, + { + ActorKey: manifest.EamKey, + MethodNum: builtin.MethodsEAM.CreateExternal, + retVal: &testReturn, + }, + } + + for _, _tc := range testCases { + tc := _tc + t.Run(tc.ActorKey+" "+tc.MethodNum.String(), func(t *testing.T) { + av, err := actorstypes.VersionForNetwork(build.TestNetworkVersion) + require.NoError(t, err) + actorCodeCid, found := actors.GetActorCodeID(av, tc.ActorKey) + require.True(t, found) + + buf := bytes.NewBuffer(nil) + if err := tc.retVal.MarshalCBOR(buf); err != nil { + t.Fatal(err) + } + + returnString, err := cli.JsonReturn(actorCodeCid, tc.MethodNum, buf.Bytes()) + require.NoError(t, err) + + jsonReturn, err := json.MarshalIndent(tc.retVal, "", " ") + require.NoError(t, err) + require.Equal(t, string(jsonReturn), returnString) + }) + } +} diff --git a/itests/eth_account_abstraction_test.go b/itests/eth_account_abstraction_test.go index e6d9723bf..8d92d0a04 100644 --- a/itests/eth_account_abstraction_test.go +++ b/itests/eth_account_abstraction_test.go @@ -24,7 +24,7 @@ import ( "github.com/filecoin-project/lotus/itests/kit" ) -// TestEthAccountAbstraction goes over the account abstraction workflow: +// TestEthAccountAbstraction goes over the placeholder creation and promotion workflow: // - an placeholder is created when it receives a message // - the placeholder turns into an EOA when it sends a message func TestEthAccountAbstraction(t *testing.T) { @@ -67,7 +67,8 @@ func TestEthAccountAbstraction(t *testing.T) { msgFromPlaceholder := &types.Message{ From: placeholderAddress, // self-send because an "eth tx payload" can't be to a filecoin address? - To: placeholderAddress, + To: placeholderAddress, + Method: builtin2.MethodsEVM.InvokeContract, } msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK) require.NoError(t, err) @@ -100,9 +101,10 @@ func TestEthAccountAbstraction(t *testing.T) { // Send another message, it should succeed without any code CID changes msgFromPlaceholder = &types.Message{ - From: placeholderAddress, - To: placeholderAddress, - Nonce: 1, + From: placeholderAddress, + To: placeholderAddress, + Method: builtin2.MethodsEVM.InvokeContract, + Nonce: 1, } msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK) @@ -152,9 +154,10 @@ func TestEthAccountAbstractionFailure(t *testing.T) { // create a placeholder actor at the target address msgCreatePlaceholder := &types.Message{ - From: client.DefaultKey.Address, - To: placeholderAddress, - Value: abi.TokenAmount(types.MustParseFIL("100")), + From: client.DefaultKey.Address, + To: placeholderAddress, + Value: abi.TokenAmount(types.MustParseFIL("100")), + Method: builtin2.MethodsEVM.InvokeContract, } smCreatePlaceholder, err := client.MpoolPushMessage(ctx, msgCreatePlaceholder, nil) require.NoError(t, err) @@ -172,9 +175,10 @@ func TestEthAccountAbstractionFailure(t *testing.T) { // send a message from the placeholder address msgFromPlaceholder := &types.Message{ - From: placeholderAddress, - To: placeholderAddress, - Value: abi.TokenAmount(types.MustParseFIL("20")), + From: placeholderAddress, + To: placeholderAddress, + Value: abi.TokenAmount(types.MustParseFIL("20")), + Method: builtin2.MethodsEVM.InvokeContract, } msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK) require.NoError(t, err) @@ -209,10 +213,11 @@ func TestEthAccountAbstractionFailure(t *testing.T) { // Send a valid message now, it should succeed without any code CID changes msgFromPlaceholder = &types.Message{ - From: placeholderAddress, - To: placeholderAddress, - Nonce: 1, - Value: abi.NewTokenAmount(1), + From: placeholderAddress, + To: placeholderAddress, + Nonce: 1, + Value: abi.NewTokenAmount(1), + Method: builtin2.MethodsEVM.InvokeContract, } msgFromPlaceholder, err = client.GasEstimateMessageGas(ctx, msgFromPlaceholder, nil, types.EmptyTSK) diff --git a/itests/eth_bytecode_test.go b/itests/eth_bytecode_test.go new file mode 100644 index 000000000..4d03a0f6d --- /dev/null +++ b/itests/eth_bytecode_test.go @@ -0,0 +1,74 @@ +package itests + +import ( + "context" + "encoding/hex" + "os" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/itests/kit" +) + +// TestGetCode ensures that GetCode returns the correct results for: +// 1. Placeholders. +// 2. Non-existent actors. +// 3. Normal EVM actors. +// 4. Self-destructed EVM actors. +func TestGetCode(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) + defer cancel() + + // Accounts should have empty code. + { + // A random eth address should have no code. + _, ethAddr, filAddr := client.EVM().NewAccount() + bytecode, err := client.EVM().EthGetCode(ctx, ethAddr, "latest") + require.NoError(t, err) + require.Empty(t, bytecode) + + // send some funds to the account. + kit.SendFunds(ctx, t, client, filAddr, types.FromFil(10)) + + // The code should still be empty, target is now a placeholder. + bytecode, err = client.EVM().EthGetCode(ctx, ethAddr, "latest") + require.NoError(t, err) + require.Empty(t, bytecode) + } + + // Check contract code. + { + // install a contract + contractHex, err := os.ReadFile("./contracts/SelfDestruct.hex") + require.NoError(t, err) + contract, err := hex.DecodeString(string(contractHex)) + require.NoError(t, err) + createReturn := client.EVM().DeployContract(ctx, client.DefaultKey.Address, contract) + contractAddr := createReturn.EthAddress + contractFilAddr := *createReturn.RobustAddress + + // The newly deployed contract should not be empty. + bytecode, err := client.EVM().EthGetCode(ctx, contractAddr, "latest") + require.NoError(t, err) + require.NotEmpty(t, bytecode) + + // Destroy it. + _, _, err = client.EVM().InvokeContractByFuncName(ctx, client.DefaultKey.Address, contractFilAddr, "destroy()", nil) + require.NoError(t, err) + + // The code should be empty again. + bytecode, err = client.EVM().EthGetCode(ctx, contractAddr, "latest") + require.NoError(t, err) + require.Empty(t, bytecode) + } + +} diff --git a/itests/eth_config_test.go b/itests/eth_config_test.go new file mode 100644 index 000000000..8b74d011a --- /dev/null +++ b/itests/eth_config_test.go @@ -0,0 +1,62 @@ +// stm: #integration +package itests + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/lotus/chain/types/ethtypes" + "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/node/impl/full" +) + +func TestEthFilterAPIDisabledViaConfig(t *testing.T) { + ctx := context.Background() + + kit.QuietMiningLogs() + + // pass kit.DisableEthRPC() so RealTimeFilterAPI will not be enabled + client, _, _ := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.DisableEthRPC()) + + _, err := client.EthNewPendingTransactionFilter(ctx) + require.NotNil(t, err) + require.Equal(t, err.Error(), full.ErrModuleDisabled.Error()) + + _, err = client.EthGetLogs(ctx, ðtypes.EthFilterSpec{}) + require.NotNil(t, err) + require.Equal(t, err.Error(), full.ErrModuleDisabled.Error()) + + _, err = client.EthGetFilterChanges(ctx, ethtypes.EthFilterID{}) + require.NotNil(t, err) + require.Equal(t, err.Error(), full.ErrModuleDisabled.Error()) + + _, err = client.EthGetFilterLogs(ctx, ethtypes.EthFilterID{}) + require.NotNil(t, err) + require.Equal(t, err.Error(), full.ErrModuleDisabled.Error()) + + _, err = client.EthNewFilter(ctx, ðtypes.EthFilterSpec{}) + require.NotNil(t, err) + require.Equal(t, err.Error(), full.ErrModuleDisabled.Error()) + + _, err = client.EthNewBlockFilter(ctx) + require.NotNil(t, err) + require.Equal(t, err.Error(), full.ErrModuleDisabled.Error()) + + _, err = client.EthNewPendingTransactionFilter(ctx) + require.NotNil(t, err) + require.Equal(t, err.Error(), full.ErrModuleDisabled.Error()) + + _, err = client.EthUninstallFilter(ctx, ethtypes.EthFilterID{}) + require.NotNil(t, err) + require.Equal(t, err.Error(), full.ErrModuleDisabled.Error()) + + _, err = client.EthSubscribe(ctx, []byte("{}")) + require.NotNil(t, err) + require.Equal(t, err.Error(), full.ErrModuleDisabled.Error()) + + _, err = client.EthUnsubscribe(ctx, ethtypes.EthSubscriptionID{}) + require.NotNil(t, err) + require.Equal(t, err.Error(), full.ErrModuleDisabled.Error()) +} diff --git a/itests/eth_conformance_test.go b/itests/eth_conformance_test.go new file mode 100644 index 000000000..8a367d6b1 --- /dev/null +++ b/itests/eth_conformance_test.go @@ -0,0 +1,514 @@ +package itests + +import ( + "bytes" + "context" + "encoding/binary" + "encoding/hex" + "encoding/json" + "os" + "strings" + "testing" + "time" + + "github.com/go-openapi/spec" + "github.com/gregdhill/go-openrpc/parse" + orpctypes "github.com/gregdhill/go-openrpc/types" + manet "github.com/multiformats/go-multiaddr/net" + "github.com/stretchr/testify/require" + "github.com/xeipuuv/gojsonschema" + + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-jsonrpc" + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/types/ethtypes" + "github.com/filecoin-project/lotus/chain/wallet/key" + "github.com/filecoin-project/lotus/itests/kit" +) + +// TODO generate this using reflection. It's the same as the EthAPI except every return value is a json.RawMessage +type ethAPIRaw struct { + EthAccounts func(context.Context) (json.RawMessage, error) + EthBlockNumber func(context.Context) (json.RawMessage, error) + EthCall func(context.Context, ethtypes.EthCall, string) (json.RawMessage, error) + EthChainId func(context.Context) (json.RawMessage, error) + EthEstimateGas func(context.Context, ethtypes.EthCall) (json.RawMessage, error) + EthFeeHistory func(context.Context, ethtypes.EthUint64, string, []float64) (json.RawMessage, error) + EthGasPrice func(context.Context) (json.RawMessage, error) + EthGetBalance func(context.Context, ethtypes.EthAddress, string) (json.RawMessage, error) + EthGetBlockByHash func(context.Context, ethtypes.EthHash, bool) (json.RawMessage, error) + EthGetBlockByNumber func(context.Context, string, bool) (json.RawMessage, error) + EthGetBlockTransactionCountByHash func(context.Context, ethtypes.EthHash) (json.RawMessage, error) + EthGetBlockTransactionCountByNumber func(context.Context, ethtypes.EthUint64) (json.RawMessage, error) + EthGetCode func(context.Context, ethtypes.EthAddress, string) (json.RawMessage, error) + EthGetFilterChanges func(context.Context, ethtypes.EthFilterID) (json.RawMessage, error) + EthGetFilterLogs func(context.Context, ethtypes.EthFilterID) (json.RawMessage, error) + EthGetLogs func(context.Context, *ethtypes.EthFilterSpec) (json.RawMessage, error) + EthGetStorageAt func(context.Context, ethtypes.EthAddress, ethtypes.EthBytes, string) (json.RawMessage, error) + EthGetTransactionByBlockHashAndIndex func(context.Context, ethtypes.EthHash, ethtypes.EthUint64) (json.RawMessage, error) + EthGetTransactionByBlockNumberAndIndex func(context.Context, ethtypes.EthUint64, ethtypes.EthUint64) (json.RawMessage, error) + EthGetTransactionByHash func(context.Context, *ethtypes.EthHash) (json.RawMessage, error) + EthGetTransactionCount func(context.Context, ethtypes.EthAddress, string) (json.RawMessage, error) + EthGetTransactionReceipt func(context.Context, ethtypes.EthHash) (json.RawMessage, error) + EthMaxPriorityFeePerGas func(context.Context) (json.RawMessage, error) + EthNewBlockFilter func(context.Context) (json.RawMessage, error) + EthNewFilter func(context.Context, *ethtypes.EthFilterSpec) (json.RawMessage, error) + EthNewPendingTransactionFilter func(context.Context) (json.RawMessage, error) + EthSendRawTransaction func(context.Context, ethtypes.EthBytes) (json.RawMessage, error) + EthSubscribe func(context.Context, string, *ethtypes.EthSubscriptionParams) (json.RawMessage, error) + EthUninstallFilter func(context.Context, ethtypes.EthFilterID) (json.RawMessage, error) + EthUnsubscribe func(context.Context, ethtypes.EthSubscriptionID) (json.RawMessage, error) +} + +func TestEthOpenRPCConformance(t *testing.T) { + kit.QuietAllLogsExcept("events", "messagepool") + + // specs/eth_openrpc.json is built from https://github.com/ethereum/execution-apis + specJSON, err := os.ReadFile("specs/eth_openrpc.json") + require.NoError(t, err) + + specParsed := orpctypes.NewOpenRPCSpec1() + err = json.Unmarshal(specJSON, specParsed) + require.NoError(t, err) + parse.GetTypes(specParsed, specParsed.Objects) + + schemas := make(map[string]spec.Schema) + for _, method := range specParsed.Methods { + if method.Result != nil { + schemas[method.Name] = method.Result.Schema + } + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.WithEthRPC()) + ens.InterconnectAll().BeginMining(10 * time.Millisecond) + + contractHex, err := os.ReadFile("contracts/EventMatrix.hex") + require.NoError(t, err) + + // strip any trailing newlines from the file + contractHex = bytes.TrimRight(contractHex, "\n") + + contractBin, err := hex.DecodeString(string(contractHex)) + require.NoError(t, err) + + senderKey, senderEthAddr, senderFilAddr := client.EVM().NewAccount() + _, receiverEthAddr, _ := client.EVM().NewAccount() + kit.SendFunds(ctx, t, client, senderFilAddr, types.FromFil(1000)) + + deployerAddr, err := client.EVM().WalletDefaultAddress(ctx) + require.NoError(t, err) + + pendingTransactionFilterID, err := client.EthNewPendingTransactionFilter(ctx) + require.NoError(t, err) + + blockFilterID, err := client.EthNewBlockFilter(ctx) + require.NoError(t, err) + + filterAllLogs := kit.NewEthFilterBuilder().FromBlockEpoch(0).Filter() + + logFilterID, err := client.EthNewFilter(ctx, filterAllLogs) + require.NoError(t, err) + + uninstallableFilterID, err := client.EthNewFilter(ctx, filterAllLogs) + require.NoError(t, err) + + rawSignedEthTx := createRawSignedEthTx(ctx, t, client, senderEthAddr, receiverEthAddr, senderKey, contractBin) + + result := client.EVM().DeployContract(ctx, deployerAddr, contractBin) + contractAddr, err := address.NewIDAddress(result.ActorID) + require.NoError(t, err) + + contractEthAddr := ethtypes.EthAddress(result.EthAddress) + + messageWithEvents, blockHashWithMessage, blockNumberWithMessage := waitForMessageWithEvents(ctx, t, client, deployerAddr, contractAddr) + + // create a json-rpc client that returns raw json responses + var ethapi ethAPIRaw + + netAddr, err := manet.ToNetAddr(client.ListenAddr) + require.NoError(t, err) + rpcAddr := "ws://" + netAddr.String() + "/rpc/v1" + + closer, err := jsonrpc.NewClient(ctx, rpcAddr, "Filecoin", ðapi, nil) + require.NoError(t, err) + defer closer() + + testCases := []struct { + method string + variant string // suffix applied to the test name to distinguish different variants of a method call + call func(*ethAPIRaw) (json.RawMessage, error) + skipReason string + }{ + // Alphabetical order + + { + method: "eth_accounts", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthAccounts(context.Background()) + }, + }, + + { + method: "eth_blockNumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthBlockNumber(context.Background()) + }, + }, + + { + method: "eth_call", + variant: "latest", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthCall(context.Background(), ethtypes.EthCall{ + From: &senderEthAddr, + Data: contractBin, + }, "latest") + }, + }, + + { + method: "eth_chainId", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthChainId(context.Background()) + }, + }, + + { + method: "eth_estimateGas", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthEstimateGas(context.Background(), ethtypes.EthCall{ + From: &senderEthAddr, + Data: contractBin, + }) + }, + }, + + { + method: "eth_feeHistory", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthFeeHistory(context.Background(), ethtypes.EthUint64(2), "latest", nil) + }, + }, + + { + method: "eth_gasPrice", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGasPrice(context.Background()) + }, + }, + + { + method: "eth_getBalance", + variant: "blocknumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBalance(context.Background(), contractEthAddr, "0x0") + }, + }, + + { + method: "eth_getBlockByHash", + variant: "txhashes", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockByHash(context.Background(), blockHashWithMessage, false) + }, + }, + + { + method: "eth_getBlockByHash", + variant: "txfull", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockByHash(context.Background(), blockHashWithMessage, true) + }, + }, + + { + method: "eth_getBlockByNumber", + variant: "earliest", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockByNumber(context.Background(), "earliest", true) + }, + skipReason: "earliest block is not supported", + }, + + { + method: "eth_getBlockByNumber", + variant: "pending", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockByNumber(context.Background(), "pending", true) + }, + }, + + { + method: "eth_getBlockByNumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockByNumber(context.Background(), blockNumberWithMessage.Hex(), true) + }, + }, + + { + method: "eth_getBlockTransactionCountByHash", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockTransactionCountByHash(context.Background(), blockHashWithMessage) + }, + }, + + { + method: "eth_getBlockTransactionCountByNumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetBlockTransactionCountByNumber(context.Background(), blockNumberWithMessage) + }, + }, + + { + method: "eth_getCode", + variant: "blocknumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetCode(context.Background(), contractEthAddr, blockNumberWithMessage.Hex()) + }, + }, + + { + method: "eth_getFilterChanges", + variant: "pendingtransaction", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return a.EthGetFilterChanges(ctx, pendingTransactionFilterID) + }, + }, + + { + method: "eth_getFilterChanges", + variant: "block", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return a.EthGetFilterChanges(ctx, blockFilterID) + }, + }, + + { + method: "eth_getFilterChanges", + variant: "logs", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return a.EthGetFilterChanges(ctx, logFilterID) + }, + }, + + { + method: "eth_getFilterLogs", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return a.EthGetFilterLogs(ctx, logFilterID) + }, + }, + + { + method: "eth_getLogs", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetLogs(context.Background(), filterAllLogs) + }, + }, + + { + method: "eth_getStorageAt", + variant: "blocknumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetStorageAt(context.Background(), contractEthAddr, ethtypes.EthBytes{0}, "0x0") + }, + }, + + { + method: "eth_getTransactionByBlockHashAndIndex", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionByBlockHashAndIndex(context.Background(), blockHashWithMessage, ethtypes.EthUint64(0)) + }, + skipReason: "unimplemented", + }, + + { + method: "eth_getTransactionByBlockNumberAndIndex", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionByBlockNumberAndIndex(context.Background(), blockNumberWithMessage, ethtypes.EthUint64(0)) + }, + skipReason: "unimplemented", + }, + + { + method: "eth_getTransactionByHash", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionByHash(context.Background(), &messageWithEvents) + }, + }, + + { + method: "eth_getTransactionCount", + variant: "blocknumber", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionCount(context.Background(), senderEthAddr, blockNumberWithMessage.Hex()) + }, + }, + + { + method: "eth_getTransactionReceipt", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthGetTransactionReceipt(context.Background(), messageWithEvents) + }, + }, + + { + method: "eth_maxPriorityFeePerGas", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthMaxPriorityFeePerGas(context.Background()) + }, + }, + + { + method: "eth_newBlockFilter", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthNewBlockFilter(context.Background()) + }, + }, + + { + method: "eth_newFilter", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthNewFilter(context.Background(), filterAllLogs) + }, + }, + + { + method: "eth_newPendingTransactionFilter", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthNewPendingTransactionFilter(context.Background()) + }, + }, + + { + method: "eth_sendRawTransaction", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return ethapi.EthSendRawTransaction(context.Background(), rawSignedEthTx) + }, + }, + { + method: "eth_uninstallFilter", + call: func(a *ethAPIRaw) (json.RawMessage, error) { + return a.EthUninstallFilter(ctx, uninstallableFilterID) + }, + }, + } + + for _, tc := range testCases { + tc := tc + name := tc.method + if tc.variant != "" { + name += "_" + tc.variant + } + t.Run(name, func(t *testing.T) { + if tc.skipReason != "" { + t.Skipf(tc.skipReason) + } + + schema, ok := schemas[tc.method] + require.True(t, ok, "method not found in openrpc spec") + + resp, err := tc.call(ðapi) + require.NoError(t, err) + + respJson, err := json.Marshal(resp) + require.NoError(t, err) + + loader := gojsonschema.NewGoLoader(schema) + resploader := gojsonschema.NewBytesLoader(respJson) + result, err := gojsonschema.Validate(loader, resploader) + require.NoError(t, err) + + if !result.Valid() { + if len(result.Errors()) == 1 && strings.Contains(result.Errors()[0].String(), "Must validate one and only one schema (oneOf)") { + // Ignore this error, since it seems the openrpc spec can't handle it + // New transaction and block filters have the same schema: an array of 32 byte hashes + return + } + + niceRespJson, err := json.MarshalIndent(resp, "", " ") + if err == nil { + t.Logf("response was %s", niceRespJson) + } + + schemaJson, err := json.MarshalIndent(schema, "", " ") + if err == nil { + t.Logf("schema was %s", schemaJson) + } + + // check against https://www.jsonschemavalidator.net/ + + for _, desc := range result.Errors() { + t.Logf("- %s\n", desc) + } + + t.Errorf("response did not validate") + } + }) + } +} + +func createRawSignedEthTx(ctx context.Context, t *testing.T, client *kit.TestFullNode, senderEthAddr ethtypes.EthAddress, receiverEthAddr ethtypes.EthAddress, senderKey *key.Key, contractBin []byte) []byte { + gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + From: &senderEthAddr, + Data: contractBin, + }) + require.NoError(t, err) + + maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + require.NoError(t, err) + + tx := ethtypes.EthTxArgs{ + ChainID: build.Eip155ChainId, + Value: big.NewInt(100), + Nonce: 0, + To: &receiverEthAddr, + MaxFeePerGas: types.NanoFil, + MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), + GasLimit: int(gaslimit), + V: big.Zero(), + R: big.Zero(), + S: big.Zero(), + } + + client.EVM().SignTransaction(&tx, senderKey.PrivateKey) + signed, err := tx.ToRlpSignedMsg() + require.NoError(t, err) + return signed +} + +func waitForMessageWithEvents(ctx context.Context, t *testing.T, client *kit.TestFullNode, sender address.Address, target address.Address) (ethtypes.EthHash, ethtypes.EthHash, ethtypes.EthUint64) { + vals := []uint64{44, 27, 19, 12} + inputData := []byte{} + for _, v := range vals { + buf := make([]byte, 32) + binary.BigEndian.PutUint64(buf[24:], v) + inputData = append(inputData, buf...) + } + + // send a message that exercises event logs + ret, err := client.EVM().InvokeSolidity(ctx, sender, target, kit.EventMatrixContract.Fn["logEventThreeIndexedWithData"], inputData) + require.NoError(t, err) + require.True(t, ret.Receipt.ExitCode.IsSuccess(), "contract execution failed") + + msgHash, err := client.EthGetTransactionHashByCid(ctx, ret.Message) + require.NoError(t, err) + require.NotNil(t, msgHash) + + ts, err := client.ChainGetTipSet(ctx, ret.TipSet) + require.NoError(t, err) + + blockNumber := ethtypes.EthUint64(ts.Height()) + + tsCid, err := ts.Key().Cid() + require.NoError(t, err) + + blockHash, err := ethtypes.EthHashFromCid(tsCid) + require.NoError(t, err) + return *msgHash, blockHash, blockNumber +} diff --git a/itests/eth_deploy_test.go b/itests/eth_deploy_test.go index 98038de7b..bbdadb1d5 100644 --- a/itests/eth_deploy_test.go +++ b/itests/eth_deploy_test.go @@ -11,12 +11,16 @@ import ( "time" "github.com/stretchr/testify/require" + "golang.org/x/crypto/sha3" "github.com/filecoin-project/go-state-types/big" "github.com/filecoin-project/go-state-types/manifest" + gstStore "github.com/filecoin-project/go-state-types/store" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/blockstore" "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors/builtin/evm" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/itests/kit" @@ -219,4 +223,32 @@ func TestDeployment(t *testing.T) { require.NoError(t, err) client.AssertActorType(ctx, contractAddr, "evm") + + // Check bytecode and bytecode hash match. + contractAct, err := client.StateGetActor(ctx, contractAddr, types.EmptyTSK) + require.NoError(t, err) + + bs := blockstore.NewAPIBlockstore(client) + ctxStore := gstStore.WrapBlockStore(ctx, bs) + + evmSt, err := evm.Load(ctxStore, contractAct) + require.NoError(t, err) + + byteCodeCid, err := evmSt.GetBytecodeCID() + require.NoError(t, err) + + byteCode, err := bs.Get(ctx, byteCodeCid) + require.NoError(t, err) + + byteCodeHashChain, err := evmSt.GetBytecodeHash() + require.NoError(t, err) + + hasher := sha3.NewLegacyKeccak256() + hasher.Write(byteCode.RawData()) + byteCodeHash := hasher.Sum(nil) + require.Equal(t, byteCodeHashChain[:], byteCodeHash) + + byteCodeSt, err := evmSt.GetBytecode() + require.NoError(t, err) + require.Equal(t, byteCode.RawData(), byteCodeSt) } diff --git a/itests/eth_fee_history_test.go b/itests/eth_fee_history_test.go new file mode 100644 index 000000000..72302f298 --- /dev/null +++ b/itests/eth_fee_history_test.go @@ -0,0 +1,100 @@ +package itests + +import ( + "context" + "encoding/json" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-jsonrpc" + + "github.com/filecoin-project/lotus/chain/types/ethtypes" + "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/lib/result" +) + +func TestEthFeeHistory(t *testing.T) { + require := require.New(t) + + kit.QuietAllLogsExcept() + + blockTime := 100 * time.Millisecond + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + // Wait for the network to create 20 blocks + client.WaitTillChain(ctx, kit.HeightAtLeast(20)) + + history, err := client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams]( + json.Marshal([]interface{}{5, "0x10"}), + ).Assert(require.NoError)) + require.NoError(err) + require.Equal(6, len(history.BaseFeePerGas)) + require.Equal(5, len(history.GasUsedRatio)) + require.Equal(ethtypes.EthUint64(16-5+1), history.OldestBlock) + require.Nil(history.Reward) + + history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams]( + json.Marshal([]interface{}{"5", "0x10"}), + ).Assert(require.NoError)) + require.NoError(err) + require.Equal(6, len(history.BaseFeePerGas)) + require.Equal(5, len(history.GasUsedRatio)) + require.Equal(ethtypes.EthUint64(16-5+1), history.OldestBlock) + require.Nil(history.Reward) + + history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams]( + json.Marshal([]interface{}{"0x10", "0x12"}), + ).Assert(require.NoError)) + require.NoError(err) + require.Equal(17, len(history.BaseFeePerGas)) + require.Equal(16, len(history.GasUsedRatio)) + require.Equal(ethtypes.EthUint64(18-16+1), history.OldestBlock) + require.Nil(history.Reward) + + history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams]( + json.Marshal([]interface{}{5, "0x10"}), + ).Assert(require.NoError)) + require.NoError(err) + require.Equal(6, len(history.BaseFeePerGas)) + require.Equal(5, len(history.GasUsedRatio)) + require.Equal(ethtypes.EthUint64(16-5+1), history.OldestBlock) + require.Nil(history.Reward) + + history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams]( + json.Marshal([]interface{}{5, "10"}), + ).Assert(require.NoError)) + require.NoError(err) + require.Equal(6, len(history.BaseFeePerGas)) + require.Equal(5, len(history.GasUsedRatio)) + require.Equal(ethtypes.EthUint64(10-5+1), history.OldestBlock) + require.Nil(history.Reward) + + history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams]( + json.Marshal([]interface{}{5, "10", &[]float64{25, 50, 75}}), + ).Assert(require.NoError)) + require.NoError(err) + require.Equal(6, len(history.BaseFeePerGas)) + require.Equal(5, len(history.GasUsedRatio)) + require.Equal(ethtypes.EthUint64(10-5+1), history.OldestBlock) + require.NotNil(history.Reward) + require.Equal(5, len(*history.Reward)) + for _, arr := range *history.Reward { + require.Equal(3, len(arr)) + } + + history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams]( + json.Marshal([]interface{}{1025, "10", &[]float64{25, 50, 75}}), + ).Assert(require.NoError)) + require.Error(err) + + history, err = client.EthFeeHistory(ctx, result.Wrap[jsonrpc.RawParams]( + json.Marshal([]interface{}{5, "10", &[]float64{}}), + ).Assert(require.NoError)) + require.NoError(err) +} diff --git a/itests/eth_filter_test.go b/itests/eth_filter_test.go index c66a56154..1104bec13 100644 --- a/itests/eth_filter_test.go +++ b/itests/eth_filter_test.go @@ -1,4 +1,3 @@ -// stm: #integration package itests import ( @@ -8,6 +7,7 @@ import ( "encoding/hex" "encoding/json" "fmt" + "math/bits" "os" "sort" "strconv" @@ -17,8 +17,6 @@ import ( "github.com/ipfs/go-cid" "github.com/stretchr/testify/require" - cbg "github.com/whyrusleeping/cbor-gen" - "golang.org/x/crypto/sha3" "golang.org/x/xerrors" "github.com/filecoin-project/go-address" @@ -34,53 +32,6 @@ import ( res "github.com/filecoin-project/lotus/lib/result" ) -// SolidityContractDef holds information about one of the test contracts -type SolidityContractDef struct { - Filename string // filename of the hex of the contract, e.g. contracts/EventMatrix.hex - Fn map[string][]byte // mapping of function names to 32-bit selector - Ev map[string][]byte // mapping of event names to 256-bit signature hashes -} - -var EventMatrixContract = SolidityContractDef{ - Filename: "contracts/EventMatrix.hex", - Fn: map[string][]byte{ - "logEventZeroData": ethFunctionHash("logEventZeroData()"), - "logEventOneData": ethFunctionHash("logEventOneData(uint256)"), - "logEventTwoData": ethFunctionHash("logEventTwoData(uint256,uint256)"), - "logEventThreeData": ethFunctionHash("logEventThreeData(uint256,uint256,uint256)"), - "logEventFourData": ethFunctionHash("logEventFourData(uint256,uint256,uint256,uint256)"), - "logEventOneIndexed": ethFunctionHash("logEventOneIndexed(uint256)"), - "logEventTwoIndexed": ethFunctionHash("logEventTwoIndexed(uint256,uint256)"), - "logEventThreeIndexed": ethFunctionHash("logEventThreeIndexed(uint256,uint256,uint256)"), - "logEventOneIndexedWithData": ethFunctionHash("logEventOneIndexedWithData(uint256,uint256)"), - "logEventTwoIndexedWithData": ethFunctionHash("logEventTwoIndexedWithData(uint256,uint256,uint256)"), - "logEventThreeIndexedWithData": ethFunctionHash("logEventThreeIndexedWithData(uint256,uint256,uint256,uint256)"), - }, - Ev: map[string][]byte{ - "EventZeroData": ethTopicHash("EventZeroData()"), - "EventOneData": ethTopicHash("EventOneData(uint256)"), - "EventTwoData": ethTopicHash("EventTwoData(uint256,uint256)"), - "EventThreeData": ethTopicHash("EventThreeData(uint256,uint256,uint256)"), - "EventFourData": ethTopicHash("EventFourData(uint256,uint256,uint256,uint256)"), - "EventOneIndexed": ethTopicHash("EventOneIndexed(uint256)"), - "EventTwoIndexed": ethTopicHash("EventTwoIndexed(uint256,uint256)"), - "EventThreeIndexed": ethTopicHash("EventThreeIndexed(uint256,uint256,uint256)"), - "EventOneIndexedWithData": ethTopicHash("EventOneIndexedWithData(uint256,uint256)"), - "EventTwoIndexedWithData": ethTopicHash("EventTwoIndexedWithData(uint256,uint256,uint256)"), - "EventThreeIndexedWithData": ethTopicHash("EventThreeIndexedWithData(uint256,uint256,uint256,uint256)"), - }, -} - -var EventsContract = SolidityContractDef{ - Filename: "contracts/events.bin", - Fn: map[string][]byte{ - "log_zero_data": {0x00, 0x00, 0x00, 0x00}, - "log_zero_nodata": {0x00, 0x00, 0x00, 0x01}, - "log_four_data": {0x00, 0x00, 0x00, 0x02}, - }, - Ev: map[string][]byte{}, -} - func TestEthNewPendingTransactionFilter(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -185,6 +136,113 @@ func TestEthNewPendingTransactionFilter(t *testing.T) { } } +func TestEthNewPendingTransactionSub(t *testing.T) { + require := require.New(t) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + kit.QuietAllLogsExcept("events", "messagepool") + + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC(), kit.WithEthRPC()) + ens.InterconnectAll().BeginMining(10 * time.Millisecond) + + // create a new address where to send funds. + addr, err := client.WalletNew(ctx, types.KTBLS) + require.NoError(err) + + // get the existing balance from the default wallet to then split it. + bal, err := client.WalletBalance(ctx, client.DefaultKey.Address) + require.NoError(err) + + // install filter + subId, err := client.EthSubscribe(ctx, res.Wrap[jsonrpc.RawParams](json.Marshal(ethtypes.EthSubscribeParams{EventType: "newPendingTransactions"})).Assert(require.NoError)) + require.NoError(err) + + 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 = 100 + + // we'll send half our balance (saving the other half for gas), + // in `iterations` increments. + toSend := big.Div(bal, big.NewInt(2)) + each := big.Div(toSend, big.NewInt(iterations)) + + waitAllCh := make(chan struct{}) + go func() { + headChangeCh, err := client.ChainNotify(ctx) + require.NoError(err) + <-headChangeCh // skip hccurrent + + defer func() { + close(waitAllCh) + }() + + count := 0 + for { + select { + case <-ctx.Done(): + return + case headChanges := <-headChangeCh: + for _, change := range headChanges { + if change.Type == store.HCApply { + msgs, err := client.ChainGetMessagesInTipset(ctx, change.Val.Key()) + require.NoError(err) + count += len(msgs) + if count == iterations { + return + } + } + } + } + } + }() + + var sms []*types.SignedMessage + for i := 0; i < iterations; i++ { + msg := &types.Message{ + From: client.DefaultKey.Address, + To: addr, + Value: each, + } + + sm, err := client.MpoolPushMessage(ctx, msg, nil) + require.NoError(err) + require.EqualValues(i, sm.Message.Nonce) + + sms = append(sms, sm) + } + + select { + case <-waitAllCh: + case <-ctx.Done(): + t.Errorf("timeout waiting to pack messages") + } + + expected := make(map[string]bool) + for _, sm := range sms { + hash, err := ethtypes.EthHashFromCid(sm.Cid()) + require.NoError(err) + expected[hash.String()] = false + } + + // expect to have seen iteration number of mpool messages + require.Equal(len(subResponses), len(expected), "expected number of filter results to equal number of messages") + + for _, txid := range subResponses { + expected[txid.Result.(string)] = true + } + + for _, found := range expected { + require.True(found) + } +} + func TestEthNewBlockFilter(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() @@ -334,33 +392,33 @@ func TestEthNewFilterDefaultSpec(t *testing.T) { expected := []ExpectedEthLog{ { Address: ethContractAddr, - Topics: []ethtypes.EthBytes{ - paddedEthBytes([]byte{0x11, 0x11}), - paddedEthBytes([]byte{0x22, 0x22}), - paddedEthBytes([]byte{0x33, 0x33}), - paddedEthBytes([]byte{0x44, 0x44}), + Topics: []ethtypes.EthHash{ + paddedEthHash([]byte{0x11, 0x11}), + paddedEthHash([]byte{0x22, 0x22}), + paddedEthHash([]byte{0x33, 0x33}), + paddedEthHash([]byte{0x44, 0x44}), }, - Data: paddedEthBytes([]byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}), + Data: []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, }, { Address: ethContractAddr, - Topics: []ethtypes.EthBytes{ - paddedEthBytes([]byte{0x11, 0x11}), - paddedEthBytes([]byte{0x22, 0x22}), - paddedEthBytes([]byte{0x33, 0x33}), - paddedEthBytes([]byte{0x44, 0x44}), + Topics: []ethtypes.EthHash{ + paddedEthHash([]byte{0x11, 0x11}), + paddedEthHash([]byte{0x22, 0x22}), + paddedEthHash([]byte{0x33, 0x33}), + paddedEthHash([]byte{0x44, 0x44}), }, - Data: paddedEthBytes([]byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}), + Data: []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, }, { Address: ethContractAddr, - Topics: []ethtypes.EthBytes{ - paddedEthBytes([]byte{0x11, 0x11}), - paddedEthBytes([]byte{0x22, 0x22}), - paddedEthBytes([]byte{0x33, 0x33}), - paddedEthBytes([]byte{0x44, 0x44}), + Topics: []ethtypes.EthHash{ + paddedEthHash([]byte{0x11, 0x11}), + paddedEthHash([]byte{0x22, 0x22}), + paddedEthHash([]byte{0x33, 0x33}), + paddedEthHash([]byte{0x44, 0x44}), }, - Data: paddedEthBytes([]byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}), + Data: []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, }, } @@ -385,7 +443,7 @@ func TestEthGetLogsBasic(t *testing.T) { ethContractAddr, received := invokeLogFourData(t, client, invocations) // Build filter spec - spec := newEthFilterBuilder(). + spec := kit.NewEthFilterBuilder(). FromBlockEpoch(0). Topic1OneOf(paddedEthHash([]byte{0x11, 0x11})). Filter() @@ -393,13 +451,13 @@ func TestEthGetLogsBasic(t *testing.T) { expected := []ExpectedEthLog{ { Address: ethContractAddr, - Topics: []ethtypes.EthBytes{ - paddedEthBytes([]byte{0x11, 0x11}), - paddedEthBytes([]byte{0x22, 0x22}), - paddedEthBytes([]byte{0x33, 0x33}), - paddedEthBytes([]byte{0x44, 0x44}), + Topics: []ethtypes.EthHash{ + paddedEthHash([]byte{0x11, 0x11}), + paddedEthHash([]byte{0x22, 0x22}), + paddedEthHash([]byte{0x33, 0x33}), + paddedEthHash([]byte{0x44, 0x44}), }, - Data: paddedEthBytes([]byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}), + Data: []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, }, } @@ -458,13 +516,13 @@ func TestEthSubscribeLogsNoTopicSpec(t *testing.T) { for i := range expected { expected[i] = ExpectedEthLog{ Address: ethContractAddr, - Topics: []ethtypes.EthBytes{ - paddedEthBytes([]byte{0x11, 0x11}), - paddedEthBytes([]byte{0x22, 0x22}), - paddedEthBytes([]byte{0x33, 0x33}), - paddedEthBytes([]byte{0x44, 0x44}), + Topics: []ethtypes.EthHash{ + paddedEthHash([]byte{0x11, 0x11}), + paddedEthHash([]byte{0x22, 0x22}), + paddedEthHash([]byte{0x33, 0x33}), + paddedEthHash([]byte{0x44, 0x44}), }, - Data: paddedEthBytes([]byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}), + Data: []byte{0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}, } } @@ -473,6 +531,51 @@ func TestEthSubscribeLogsNoTopicSpec(t *testing.T) { AssertEthLogs(t, elogs, expected, messages) } +func TestTxReceiptBloom(t *testing.T) { + blockTime := 50 * time.Millisecond + client, _, ens := kit.EnsembleMinimal( + t, + kit.MockProofs(), + kit.ThroughRPC()) + ens.InterconnectAll().BeginMining(blockTime) + + ctx, cancel := context.WithTimeout(context.Background(), time.Minute) + defer cancel() + + fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, "contracts/EventMatrix.hex") + + _, ml, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "logEventZeroData()", nil) + require.NoError(t, err) + + th, err := client.EthGetTransactionHashByCid(ctx, ml.Message) + require.NoError(t, err) + require.NotNil(t, th) + + receipt, err := client.EthGetTransactionReceipt(ctx, *th) + require.NoError(t, err) + require.NotNil(t, receipt) + require.Len(t, receipt.Logs, 1) + + // computed by calling EventMatrix/logEventZeroData in remix + // note this only contains topic bits + matchMask := "0x00000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + + maskBytes, err := hex.DecodeString(matchMask[2:]) + require.NoError(t, err) + + bitsSet := 0 + for i, maskByte := range maskBytes { + bitsSet += bits.OnesCount8(receipt.LogsBloom[i]) + + if maskByte > 0 { + require.True(t, maskByte&receipt.LogsBloom[i] > 0) + } + } + + // 3 bits from the topic, 3 bits from the address + require.Equal(t, 6, bitsSet) +} + func TestEthGetLogs(t *testing.T) { require := require.New(t) kit.QuietAllLogsExcept("events", "messagepool") @@ -575,7 +678,10 @@ func TestEthSubscribeLogs(t *testing.T) { // subscribe to topics in filter subParam, err := json.Marshal(ethtypes.EthSubscribeParams{ EventType: "logs", - Params: ðtypes.EthSubscriptionParams{Topics: tc.spec.Topics}, + Params: ðtypes.EthSubscriptionParams{ + Topics: tc.spec.Topics, + Address: tc.spec.Address, + }, }) require.NoError(err) @@ -609,19 +715,13 @@ func TestEthSubscribeLogs(t *testing.T) { var elogs []*ethtypes.EthLog for resp := range responseCh { - rlist, ok := resp.Result.([]interface{}) - require.True(ok, "expected subscription result to be []interface{}, but was %T", resp.Result) + rmap, ok := resp.Result.(map[string]interface{}) + require.True(ok, "expected subscription result entry to be map[string]interface{}, but was %T", resp.Result) - for _, rentry := range rlist { - rmap, ok := rentry.(map[string]interface{}) - require.True(ok, "expected subscription result entry to be map[string]interface{}, but was %T", resp.Result) - - elog, err := ParseEthLog(rmap) - require.NoError(err) - - elogs = append(elogs, elog) - } + elog, err := ParseEthLog(rmap) + require.NoError(err) + elogs = append(elogs, elog) } AssertEthLogs(t, elogs, tc.expected, messages) }) @@ -695,18 +795,18 @@ func TestEthGetLogsWithBlockRanges(t *testing.T) { // Select events for partitioning for _, m := range messages { - if bytes.Equal(m.invocation.Selector, EventMatrixContract.Fn["logEventTwoIndexedWithData"]) { - addr := getContractEthAddress(ctx, t, client, m.invocation.Target) + if bytes.Equal(m.invocation.Selector, kit.EventMatrixContract.Fn["logEventTwoIndexedWithData"]) { + addr := getEthAddress(ctx, t, client, m.invocation.Target) args := unpackUint64Values(m.invocation.Data) require.Equal(3, len(args), "logEventTwoIndexedWithData should have 3 arguments") distinctHeights[m.ts.Height()] = true expectedByHeight[m.ts.Height()] = append(expectedByHeight[m.ts.Height()], ExpectedEthLog{ Address: addr, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(args[0]), - paddedUint64(args[1]), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(args[0]), + uint64EthHash(args[1]), }, Data: paddedUint64(args[2]), }) @@ -769,7 +869,7 @@ func TestEthGetLogsWithBlockRanges(t *testing.T) { require.True(len(partition3.expected) > 0, "partition should have events") // these are the topics we selected for partitioning earlier - topics := []ethtypes.EthHash{paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"])} + topics := []ethtypes.EthHash{kit.EventMatrixContract.Ev["EventTwoIndexedWithData"]} union := func(lists ...[]ExpectedEthLog) []ExpectedEthLog { ret := []ExpectedEthLog{} @@ -786,91 +886,91 @@ func TestEthGetLogsWithBlockRanges(t *testing.T) { }{ { name: "find all events from genesis", - spec: newEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(0).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected, partition2.expected, partition3.expected), }, { name: "find all from start of partition1", - spec: newEthFilterBuilder().FromBlockEpoch(partition1.start).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition1.start).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected, partition2.expected, partition3.expected), }, { name: "find all from start of partition2", - spec: newEthFilterBuilder().FromBlockEpoch(partition2.start).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition2.start).Topic1OneOf(topics...).Filter(), expected: union(partition2.expected, partition3.expected), }, { name: "find all from start of partition3", - spec: newEthFilterBuilder().FromBlockEpoch(partition3.start).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition3.start).Topic1OneOf(topics...).Filter(), expected: union(partition3.expected), }, { name: "find none after end of partition3", - spec: newEthFilterBuilder().FromBlockEpoch(partition3.end + 1).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition3.end + 1).Topic1OneOf(topics...).Filter(), expected: nil, }, { name: "find all events from genesis to end of partition1", - spec: newEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected), }, { name: "find all events from genesis to end of partition2", - spec: newEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition2.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition2.end).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected, partition2.expected), }, { name: "find all events from genesis to end of partition3", - spec: newEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition3.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition3.end).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected, partition2.expected, partition3.expected), }, { name: "find none from genesis to start of partition1", - spec: newEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition1.start - 1).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(0).ToBlockEpoch(partition1.start - 1).Topic1OneOf(topics...).Filter(), expected: nil, }, { name: "find all events in partition1", - spec: newEthFilterBuilder().FromBlockEpoch(partition1.start).ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition1.start).ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected), }, { name: "find all events in partition2", - spec: newEthFilterBuilder().FromBlockEpoch(partition2.start).ToBlockEpoch(partition2.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition2.start).ToBlockEpoch(partition2.end).Topic1OneOf(topics...).Filter(), expected: union(partition2.expected), }, { name: "find all events in partition3", - spec: newEthFilterBuilder().FromBlockEpoch(partition3.start).ToBlockEpoch(partition3.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition3.start).ToBlockEpoch(partition3.end).Topic1OneOf(topics...).Filter(), expected: union(partition3.expected), }, { name: "find all events from earliest to end of partition1", - spec: newEthFilterBuilder().FromBlock("earliest").ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock("earliest").ToBlockEpoch(partition1.end).Topic1OneOf(topics...).Filter(), expected: union(partition1.expected), }, { name: "find all events from start of partition3 to latest", - spec: newEthFilterBuilder().FromBlockEpoch(partition3.start).ToBlock("latest").Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlockEpoch(partition3.start).ToBlock("latest").Topic1OneOf(topics...).Filter(), expected: union(partition3.expected), }, { name: "find all events from earliest to latest", - spec: newEthFilterBuilder().FromBlock("earliest").ToBlock("latest").Topic1OneOf(topics...).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock("earliest").ToBlock("latest").Topic1OneOf(topics...).Filter(), expected: union(partition1.expected, partition2.expected, partition3.expected), }, } @@ -900,20 +1000,20 @@ func TestEthNewFilterMergesHistoricWithRealtime(t *testing.T) { ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - sender, contract := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename) + sender, contract := client.EVM().DeployContractFromFilename(ctx, kit.EventMatrixContract.Filename) // generate some events before the creation of the filter preInvocations := []Invocation{ { Sender: sender, Target: contract, - Selector: EventMatrixContract.Fn["logEventOneData"], + Selector: kit.EventMatrixContract.Fn["logEventOneData"], Data: packUint64Values(1), }, { Sender: sender, Target: contract, - Selector: EventMatrixContract.Fn["logEventOneIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexed"], Data: packUint64Values(2), }, } @@ -921,7 +1021,7 @@ func TestEthNewFilterMergesHistoricWithRealtime(t *testing.T) { messages := invokeAndWaitUntilAllOnChain(t, client, preInvocations) // now install filter - spec := newEthFilterBuilder().FromBlock("earliest").Filter() + spec := kit.NewEthFilterBuilder().FromBlock("earliest").Filter() filterID, err := client.EthNewFilter(ctx, spec) require.NoError(err) @@ -931,13 +1031,13 @@ func TestEthNewFilterMergesHistoricWithRealtime(t *testing.T) { { Sender: sender, Target: contract, - Selector: EventMatrixContract.Fn["logEventOneData"], + Selector: kit.EventMatrixContract.Fn["logEventOneData"], Data: packUint64Values(3), }, { Sender: sender, Target: contract, - Selector: EventMatrixContract.Fn["logEventOneIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexed"], Data: packUint64Values(4), }, } @@ -951,36 +1051,36 @@ func TestEthNewFilterMergesHistoricWithRealtime(t *testing.T) { res, err := client.EthGetFilterChanges(ctx, filterID) require.NoError(err) - ethContractAddr := getContractEthAddress(ctx, t, client, contract) + ethContractAddr := getEthAddress(ctx, t, client, contract) // expect to see 2 messages from before the filter was installed and 2 after expected := []ExpectedEthLog{ { Address: ethContractAddr, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneData"], + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneData"], }, Data: paddedUint64(1), }, { Address: ethContractAddr, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], - paddedUint64(2), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexed"], + uint64EthHash(2), }, }, { Address: ethContractAddr, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneData"], + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneData"], }, Data: paddedUint64(3), }, { Address: ethContractAddr, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], - paddedUint64(4), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexed"], + uint64EthHash(4), }, }, } @@ -1002,7 +1102,7 @@ type msgInTipset struct { reverted bool } -func getContractEthAddress(ctx context.Context, t *testing.T, client *kit.TestFullNode, addr address.Address) ethtypes.EthAddress { +func getEthAddress(ctx context.Context, t *testing.T, client *kit.TestFullNode, addr address.Address) ethtypes.EthAddress { head, err := client.ChainHead(ctx) require.NoError(t, err) @@ -1140,28 +1240,28 @@ func invokeLogFourData(t *testing.T, client *kit.TestFullNode, iterations int) ( ctx, cancel := context.WithTimeout(context.Background(), time.Minute) defer cancel() - fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, EventsContract.Filename) + fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, kit.EventsContract.Filename) invocations := make([]Invocation, iterations) for i := range invocations { invocations[i] = Invocation{ Sender: fromAddr, Target: idAddr, - Selector: EventsContract.Fn["log_four_data"], + Selector: kit.EventsContract.Fn["log_four_data"], Data: nil, } } messages := invokeAndWaitUntilAllOnChain(t, client, invocations) - ethAddr := getContractEthAddress(ctx, t, client, idAddr) + ethAddr := getEthAddress(ctx, t, client, idAddr) return ethAddr, messages } func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *kit.TestFullNode) (ethtypes.EthAddress, ethtypes.EthAddress, []Invocation) { - sender1, contract1 := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename) - sender2, contract2 := client.EVM().DeployContractFromFilename(ctx, EventMatrixContract.Filename) + sender1, contract1 := client.EVM().DeployContractFromFilename(ctx, kit.EventMatrixContract.Filename) + sender2, contract2 := client.EVM().DeployContractFromFilename(ctx, kit.EventMatrixContract.Filename) invocations := []Invocation{ // log EventZeroData() @@ -1169,7 +1269,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventZeroData"], + Selector: kit.EventMatrixContract.Fn["logEventZeroData"], Data: nil, }, @@ -1179,7 +1279,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventOneData"], + Selector: kit.EventMatrixContract.Fn["logEventOneData"], Data: packUint64Values(23), }, @@ -1189,7 +1289,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventOneIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexed"], Data: packUint64Values(44), }, @@ -1200,7 +1300,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender2, Target: contract2, - Selector: EventMatrixContract.Fn["logEventTwoIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventTwoIndexed"], Data: packUint64Values(44, 19), }, @@ -1210,7 +1310,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventOneData"], + Selector: kit.EventMatrixContract.Fn["logEventOneData"], Data: packUint64Values(44), }, @@ -1220,7 +1320,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventTwoData"], + Selector: kit.EventMatrixContract.Fn["logEventTwoData"], Data: packUint64Values(555, 666), }, @@ -1229,7 +1329,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender2, Target: contract2, - Selector: EventMatrixContract.Fn["logEventZeroData"], + Selector: kit.EventMatrixContract.Fn["logEventZeroData"], Data: nil, }, @@ -1239,7 +1339,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventThreeData"], + Selector: kit.EventMatrixContract.Fn["logEventThreeData"], Data: packUint64Values(1, 2, 3), }, @@ -1251,7 +1351,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract2, - Selector: EventMatrixContract.Fn["logEventThreeIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventThreeIndexed"], Data: packUint64Values(44, 27, 19), }, @@ -1262,7 +1362,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventOneIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexedWithData"], Data: packUint64Values(44, 19), }, @@ -1273,7 +1373,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventOneIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexedWithData"], Data: packUint64Values(46, 12), }, @@ -1285,7 +1385,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventTwoIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventTwoIndexedWithData"], Data: packUint64Values(44, 27, 19), }, @@ -1298,7 +1398,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventThreeIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventThreeIndexedWithData"], Data: packUint64Values(44, 27, 19, 12), }, @@ -1309,7 +1409,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender2, Target: contract2, - Selector: EventMatrixContract.Fn["logEventOneIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventOneIndexedWithData"], Data: packUint64Values(50, 9), }, @@ -1321,7 +1421,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventTwoIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventTwoIndexedWithData"], Data: packUint64Values(46, 27, 19), }, @@ -1333,7 +1433,7 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventTwoIndexedWithData"], + Selector: kit.EventMatrixContract.Fn["logEventTwoIndexedWithData"], Data: packUint64Values(46, 14, 19), }, // log EventTwoIndexed(44,19) from contract1 @@ -1343,13 +1443,13 @@ func prepareEventMatrixInvocations(ctx context.Context, t *testing.T, client *ki { Sender: sender1, Target: contract1, - Selector: EventMatrixContract.Fn["logEventTwoIndexed"], + Selector: kit.EventMatrixContract.Fn["logEventTwoIndexed"], Data: packUint64Values(40, 20), }, } - ethAddr1 := getContractEthAddress(ctx, t, client, contract1) - ethAddr2 := getContractEthAddress(ctx, t, client, contract2) + ethAddr1 := getEthAddress(ctx, t, client, contract1) + ethAddr2 := getEthAddress(ctx, t, client, contract2) return ethAddr1, ethAddr2, invocations } @@ -1371,20 +1471,20 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock return []filterTestCase{ { name: "find all EventZeroData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventZeroData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(kit.EventMatrixContract.Ev["EventZeroData"]).Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventZeroData"], + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventZeroData"], }, Data: nil, }, { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventZeroData"], + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventZeroData"], }, Data: nil, }, @@ -1392,20 +1492,20 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventOneData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(kit.EventMatrixContract.Ev["EventOneData"]).Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneData"], + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneData"], }, Data: packUint64Values(23), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneData"], + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneData"], }, Data: packUint64Values(44), }, @@ -1413,13 +1513,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventTwoData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(kit.EventMatrixContract.Ev["EventTwoData"]).Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoData"], + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoData"], }, Data: packUint64Values(555, 666), }, @@ -1427,13 +1527,13 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventThreeData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(kit.EventMatrixContract.Ev["EventThreeData"]).Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeData"], + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventThreeData"], }, Data: packUint64Values(1, 2, 3), }, @@ -1441,14 +1541,14 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventOneIndexed events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexed"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(kit.EventMatrixContract.Ev["EventOneIndexed"]).Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], - paddedUint64(44), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexed"], + uint64EthHash(44), }, Data: nil, }, @@ -1456,24 +1556,24 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventTwoIndexed events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexed"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(kit.EventMatrixContract.Ev["EventTwoIndexed"]).Filter(), expected: []ExpectedEthLog{ { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], - paddedUint64(44), - paddedUint64(19), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexed"], + uint64EthHash(44), + uint64EthHash(19), }, Data: nil, }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], - paddedUint64(40), - paddedUint64(20), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexed"], + uint64EthHash(40), + uint64EthHash(20), }, Data: nil, }, @@ -1481,16 +1581,16 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventThreeIndexed events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeIndexed"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(kit.EventMatrixContract.Ev["EventThreeIndexed"]).Filter(), expected: []ExpectedEthLog{ { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventThreeIndexed"], + uint64EthHash(44), + uint64EthHash(27), + uint64EthHash(19), }, Data: nil, }, @@ -1498,30 +1598,30 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventOneIndexedWithData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexedWithData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(kit.EventMatrixContract.Ev["EventOneIndexedWithData"]).Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(44), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(44), }, Data: paddedUint64(19), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(46), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(46), }, Data: paddedUint64(12), }, { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(50), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(50), }, Data: paddedUint64(9), }, @@ -1529,33 +1629,33 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventTwoIndexedWithData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(kit.EventMatrixContract.Ev["EventTwoIndexedWithData"]).Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(44), - paddedUint64(27), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(44), + uint64EthHash(27), }, Data: paddedUint64(19), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(27), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(46), + uint64EthHash(27), }, Data: paddedUint64(19), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(14), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(46), + uint64EthHash(14), }, Data: paddedUint64(19), }, @@ -1563,16 +1663,16 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all EventThreeIndexedWithData events", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventThreeIndexedWithData"])).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic1OneOf(kit.EventMatrixContract.Ev["EventThreeIndexedWithData"]).Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexedWithData"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventThreeIndexedWithData"], + uint64EthHash(44), + uint64EthHash(27), + uint64EthHash(19), }, Data: paddedUint64(12), }, @@ -1581,60 +1681,60 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { name: "find all events with topic2 of 44", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(44))).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(uint64EthHash(44)).Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], - paddedUint64(44), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexed"], + uint64EthHash(44), }, Data: nil, }, { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], - paddedUint64(44), - paddedUint64(19), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexed"], + uint64EthHash(44), + uint64EthHash(19), }, Data: nil, }, { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventThreeIndexed"], + uint64EthHash(44), + uint64EthHash(27), + uint64EthHash(19), }, Data: nil, }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(44), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(44), }, Data: paddedUint64(19), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(44), - paddedUint64(27), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(44), + uint64EthHash(27), }, Data: paddedUint64(19), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexedWithData"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventThreeIndexedWithData"], + uint64EthHash(44), + uint64EthHash(27), + uint64EthHash(19), }, Data: paddedUint64(12), }, @@ -1642,32 +1742,32 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all events with topic2 of 46", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(46))).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(uint64EthHash(46)).Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(46), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(46), }, Data: paddedUint64(12), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(27), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(46), + uint64EthHash(27), }, Data: paddedUint64(19), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(14), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(46), + uint64EthHash(14), }, Data: paddedUint64(19), }, @@ -1675,14 +1775,14 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all events with topic2 of 50", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(50))).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(uint64EthHash(50)).Filter(), expected: []ExpectedEthLog{ { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(50), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(50), }, Data: paddedUint64(9), }, @@ -1690,40 +1790,40 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock }, { name: "find all events with topic2 of 46 or 50", - spec: newEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(paddedEthHash(paddedUint64(46)), paddedEthHash(paddedUint64(50))).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).Topic2OneOf(uint64EthHash(46), uint64EthHash(50)).Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(46), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(46), }, Data: paddedUint64(12), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(27), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(46), + uint64EthHash(27), }, Data: paddedUint64(19), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(14), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(46), + uint64EthHash(14), }, Data: paddedUint64(19), }, { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(50), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(50), }, Data: paddedUint64(9), }, @@ -1732,28 +1832,28 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { name: "find all events with topic1 of EventTwoIndexedWithData and topic3 of 27", - spec: newEthFilterBuilder(). + spec: kit.NewEthFilterBuilder(). FromBlockEpoch(0). - Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"])). - Topic3OneOf(paddedEthHash(paddedUint64(27))). + Topic1OneOf(kit.EventMatrixContract.Ev["EventTwoIndexedWithData"]). + Topic3OneOf(uint64EthHash(27)). Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(44), - paddedUint64(27), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(44), + uint64EthHash(27), }, Data: paddedUint64(19), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(46), - paddedUint64(27), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(46), + uint64EthHash(27), }, Data: paddedUint64(19), }, @@ -1762,27 +1862,27 @@ func getTopicFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlock { name: "find all events with topic1 of EventTwoIndexedWithData or EventOneIndexed and topic2 of 44", - spec: newEthFilterBuilder(). + spec: kit.NewEthFilterBuilder(). FromBlockEpoch(0). - Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventTwoIndexedWithData"]), paddedEthHash(EventMatrixContract.Ev["EventOneIndexed"])). - Topic2OneOf(paddedEthHash(paddedUint64(44))). + Topic1OneOf((kit.EventMatrixContract.Ev["EventTwoIndexedWithData"]), kit.EventMatrixContract.Ev["EventOneIndexed"]). + Topic2OneOf(uint64EthHash(44)). Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexedWithData"], - paddedUint64(44), - paddedUint64(27), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexedWithData"], + uint64EthHash(44), + uint64EthHash(27), }, Data: paddedUint64(19), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexed"], - paddedUint64(44), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexed"], + uint64EthHash(44), }, Data: nil, }, @@ -1796,40 +1896,40 @@ func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlo return []filterTestCase{ { name: "find all events from contract2", - spec: newEthFilterBuilder().FromBlock(fromBlock).AddressOneOf(contract2).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).AddressOneOf(contract2).Filter(), expected: []ExpectedEthLog{ { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventZeroData"], + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventZeroData"], }, Data: nil, }, { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventThreeIndexed"], + uint64EthHash(44), + uint64EthHash(27), + uint64EthHash(19), }, Data: nil, }, { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], - paddedUint64(44), - paddedUint64(19), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexed"], + uint64EthHash(44), + uint64EthHash(19), }, Data: nil, }, { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(50), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(50), }, Data: paddedUint64(9), }, @@ -1838,25 +1938,25 @@ func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlo { name: "find all events with topic2 of 44 from contract2", - spec: newEthFilterBuilder().FromBlock(fromBlock).AddressOneOf(contract2).Topic2OneOf(paddedEthHash(paddedUint64(44))).Filter(), + spec: kit.NewEthFilterBuilder().FromBlock(fromBlock).AddressOneOf(contract2).Topic2OneOf(paddedEthHash(paddedUint64(44))).Filter(), expected: []ExpectedEthLog{ { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventThreeIndexed"], - paddedUint64(44), - paddedUint64(27), - paddedUint64(19), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventThreeIndexed"], + uint64EthHash(44), + uint64EthHash(27), + uint64EthHash(19), }, Data: nil, }, { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventTwoIndexed"], - paddedUint64(44), - paddedUint64(19), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventTwoIndexed"], + uint64EthHash(44), + uint64EthHash(19), }, Data: nil, }, @@ -1865,34 +1965,34 @@ func getAddressFilterTestCases(contract1, contract2 ethtypes.EthAddress, fromBlo { name: "find all EventOneIndexedWithData events from contract1 or contract2", - spec: newEthFilterBuilder(). + spec: kit.NewEthFilterBuilder(). FromBlockEpoch(0). AddressOneOf(contract1, contract2). - Topic1OneOf(paddedEthHash(EventMatrixContract.Ev["EventOneIndexedWithData"])). + Topic1OneOf(kit.EventMatrixContract.Ev["EventOneIndexedWithData"]). Filter(), expected: []ExpectedEthLog{ { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(44), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(44), }, Data: paddedUint64(19), }, { Address: contract1, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(46), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(46), }, Data: paddedUint64(12), }, { Address: contract2, - Topics: []ethtypes.EthBytes{ - EventMatrixContract.Ev["EventOneIndexedWithData"], - paddedUint64(50), + Topics: []ethtypes.EthHash{ + kit.EventMatrixContract.Ev["EventOneIndexedWithData"], + uint64EthHash(50), }, Data: paddedUint64(9), }, @@ -1913,21 +2013,22 @@ type ExpectedEthLog struct { Address ethtypes.EthAddress `json:"address"` // List of topics associated with the event log. - Topics []ethtypes.EthBytes `json:"topics"` + Topics []ethtypes.EthHash `json:"topics"` // Data is the value of the event log, excluding topics Data ethtypes.EthBytes `json:"data"` } func AssertEthLogs(t *testing.T, actual []*ethtypes.EthLog, expected []ExpectedEthLog, messages map[ethtypes.EthHash]msgInTipset) { + t.Helper() require := require.New(t) t.Logf("got %d ethlogs, wanted %d", len(actual), len(expected)) - formatTopics := func(topics []ethtypes.EthBytes) string { + formatTopics := func(topics []ethtypes.EthHash) string { ss := make([]string, len(topics)) for i := range topics { - ss[i] = fmt.Sprintf("%d:%x", i, topics[i]) + ss[i] = fmt.Sprintf("%d:%s", i, topics[i]) } return strings.Join(ss, ",") } @@ -1964,7 +2065,7 @@ func AssertEthLogs(t *testing.T, actual []*ethtypes.EthLog, expected []ExpectedE } for j := range elog.Topics { - if !bytes.Equal(elog.Topics[j], want.Topics[j]) { + if elog.Topics[j] != want.Topics[j] { continue LoopExpected } } @@ -1989,7 +2090,7 @@ func AssertEthLogs(t *testing.T, actual []*ethtypes.EthLog, expected []ExpectedE buf.WriteString(fmt.Sprintf("event %d\n", i)) buf.WriteString(fmt.Sprintf(" emitter: %v\n", ev.Emitter)) for _, en := range ev.Entries { - buf.WriteString(fmt.Sprintf(" %s=%x\n", en.Key, decodeLogBytes(en.Value))) + buf.WriteString(fmt.Sprintf(" %s=%x\n", en.Key, en.Value)) } } @@ -2012,23 +2113,16 @@ func AssertEthLogs(t *testing.T, actual []*ethtypes.EthLog, expected []ExpectedE func parseEthLogsFromSubscriptionResponses(subResponses []ethtypes.EthSubscriptionResponse) ([]*ethtypes.EthLog, error) { elogs := make([]*ethtypes.EthLog, 0, len(subResponses)) for i := range subResponses { - rlist, ok := subResponses[i].Result.([]interface{}) + rmap, ok := subResponses[i].Result.(map[string]interface{}) if !ok { - return nil, xerrors.Errorf("expected subscription result to be []interface{}, but was %T", subResponses[i].Result) + return nil, xerrors.Errorf("expected subscription result entry to be map[string]interface{}, but was %T", subResponses[i].Result) } - for _, r := range rlist { - rmap, ok := r.(map[string]interface{}) - if !ok { - return nil, xerrors.Errorf("expected subscription result entry to be map[string]interface{}, but was %T", r) - } - - elog, err := ParseEthLog(rmap) - if err != nil { - return nil, err - } - elogs = append(elogs, elog) + elog, err := ParseEthLog(rmap) + if err != nil { + return nil, err } + elogs = append(elogs, elog) } return elogs, nil @@ -2145,7 +2239,7 @@ func ParseEthLog(in map[string]interface{}) (*ethtypes.EthLog, error) { if err != nil { return nil, xerrors.Errorf("%s: %w", k, err) } - el.Topics = append(el.Topics, topic) + el.Topics = append(el.Topics, paddedEthHash(topic)) continue } @@ -2158,7 +2252,7 @@ func ParseEthLog(in map[string]interface{}) (*ethtypes.EthLog, error) { if err != nil { return nil, xerrors.Errorf("%s: %w", k, err) } - el.Topics = append(el.Topics, topic) + el.Topics = append(el.Topics, paddedEthHash(topic)) } } } @@ -2166,22 +2260,18 @@ func ParseEthLog(in map[string]interface{}) (*ethtypes.EthLog, error) { return el, err } -func paddedEthBytes(orig []byte) ethtypes.EthBytes { - needed := 32 - len(orig) - if needed <= 0 { - return orig - } - ret := make([]byte, 32) - copy(ret[needed:], orig) - return ret -} - func paddedUint64(v uint64) ethtypes.EthBytes { buf := make([]byte, 32) binary.BigEndian.PutUint64(buf[24:], v) return buf } +func uint64EthHash(v uint64) ethtypes.EthHash { + var buf ethtypes.EthHash + binary.BigEndian.PutUint64(buf[24:], v) + return buf +} + func paddedEthHash(orig []byte) ethtypes.EthHash { if len(orig) > 32 { panic("exceeds EthHash length") @@ -2192,18 +2282,6 @@ func paddedEthHash(orig []byte) ethtypes.EthHash { return ret } -func ethTopicHash(sig string) []byte { - hasher := sha3.NewLegacyKeccak256() - hasher.Write([]byte(sig)) - return hasher.Sum(nil) -} - -func ethFunctionHash(sig string) []byte { - hasher := sha3.NewLegacyKeccak256() - hasher.Write([]byte(sig)) - return hasher.Sum(nil)[:4] -} - func packUint64Values(vals ...uint64) []byte { ret := []byte{} for _, v := range vals { @@ -2225,86 +2303,3 @@ func unpackUint64Values(data []byte) []uint64 { } return vals } - -func newEthFilterBuilder() *ethFilterBuilder { return ðFilterBuilder{} } - -type ethFilterBuilder struct { - filter ethtypes.EthFilterSpec -} - -func (e *ethFilterBuilder) Filter() *ethtypes.EthFilterSpec { return &e.filter } - -func (e *ethFilterBuilder) FromBlock(v string) *ethFilterBuilder { - e.filter.FromBlock = &v - return e -} - -func (e *ethFilterBuilder) FromBlockEpoch(v abi.ChainEpoch) *ethFilterBuilder { - s := ethtypes.EthUint64(v).Hex() - e.filter.FromBlock = &s - return e -} - -func (e *ethFilterBuilder) ToBlock(v string) *ethFilterBuilder { - e.filter.ToBlock = &v - return e -} - -func (e *ethFilterBuilder) ToBlockEpoch(v abi.ChainEpoch) *ethFilterBuilder { - s := ethtypes.EthUint64(v).Hex() - e.filter.ToBlock = &s - return e -} - -func (e *ethFilterBuilder) BlockHash(h ethtypes.EthHash) *ethFilterBuilder { - e.filter.BlockHash = &h - return e -} - -func (e *ethFilterBuilder) AddressOneOf(as ...ethtypes.EthAddress) *ethFilterBuilder { - e.filter.Address = as - return e -} - -func (e *ethFilterBuilder) Topic1OneOf(hs ...ethtypes.EthHash) *ethFilterBuilder { - if len(e.filter.Topics) == 0 { - e.filter.Topics = make(ethtypes.EthTopicSpec, 1) - } - e.filter.Topics[0] = hs - return e -} - -func (e *ethFilterBuilder) Topic2OneOf(hs ...ethtypes.EthHash) *ethFilterBuilder { - for len(e.filter.Topics) < 2 { - e.filter.Topics = append(e.filter.Topics, nil) - } - e.filter.Topics[1] = hs - return e -} - -func (e *ethFilterBuilder) Topic3OneOf(hs ...ethtypes.EthHash) *ethFilterBuilder { - for len(e.filter.Topics) < 3 { - e.filter.Topics = append(e.filter.Topics, nil) - } - e.filter.Topics[2] = hs - return e -} - -func (e *ethFilterBuilder) Topic4OneOf(hs ...ethtypes.EthHash) *ethFilterBuilder { - for len(e.filter.Topics) < 4 { - e.filter.Topics = append(e.filter.Topics, nil) - } - e.filter.Topics[3] = hs - return e -} - -func decodeLogBytes(orig []byte) []byte { - if len(orig) == 0 { - return orig - } - decoded, err := cbg.ReadByteArray(bytes.NewReader(orig), uint64(len(orig))) - if err != nil { - return orig - } - return decoded -} diff --git a/itests/eth_transactions_test.go b/itests/eth_transactions_test.go index 6ba63608a..9afeb7bd5 100644 --- a/itests/eth_transactions_test.go +++ b/itests/eth_transactions_test.go @@ -87,7 +87,19 @@ func TestValueTransferValidSignature(t *testing.T) { ethTx, err := client.EthGetTransactionByHash(ctx, &hash) require.Nil(t, err) require.EqualValues(t, ethAddr, ethTx.From) - + require.EqualValues(t, ethAddr2, *ethTx.To) + require.EqualValues(t, tx.ChainID, ethTx.ChainID) + require.EqualValues(t, tx.Nonce, ethTx.Nonce) + require.EqualValues(t, hash, ethTx.Hash) + require.EqualValues(t, tx.Value, ethTx.Value) + require.EqualValues(t, 2, ethTx.Type) + require.EqualValues(t, ethtypes.EthBytes{}, ethTx.Input) + require.EqualValues(t, tx.GasLimit, ethTx.Gas) + require.EqualValues(t, tx.MaxFeePerGas, ethTx.MaxFeePerGas) + require.EqualValues(t, tx.MaxPriorityFeePerGas, ethTx.MaxPriorityFeePerGas) + require.EqualValues(t, tx.V, ethTx.V) + require.EqualValues(t, tx.R, ethTx.R) + require.EqualValues(t, tx.S, ethTx.S) } func TestLegacyTransaction(t *testing.T) { @@ -105,11 +117,9 @@ func TestLegacyTransaction(t *testing.T) { require.NoError(t, err) _, err = client.EVM().EthSendRawTransaction(ctx, txBytes) require.ErrorContains(t, err, "legacy transaction is not supported") - } func TestContractDeploymentValidSignature(t *testing.T) { - blockTime := 100 * time.Millisecond client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs(), kit.ThroughRPC()) @@ -258,7 +268,6 @@ func TestContractInvocation(t *testing.T) { // Success. require.EqualValues(t, ethtypes.EthUint64(0x1), receipt.Status) - } func deployContractTx(ctx context.Context, client *kit.TestFullNode, ethAddr ethtypes.EthAddress, contract []byte) (*ethtypes.EthTxArgs, error) { diff --git a/itests/fevm_test.go b/itests/fevm_test.go index de11fd618..3018bf63d 100644 --- a/itests/fevm_test.go +++ b/itests/fevm_test.go @@ -3,10 +3,12 @@ package itests import ( "bytes" "context" + "crypto/rand" "encoding/binary" "encoding/hex" "fmt" "testing" + "time" "github.com/stretchr/testify/require" @@ -16,6 +18,8 @@ import ( "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/go-state-types/manifest" + "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/types/ethtypes" "github.com/filecoin-project/lotus/itests/kit" @@ -56,7 +60,7 @@ func buildInputFromuint64(number uint64) []byte { // 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) + expectedIterationsBeforeFailing := int(220) fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename) t.Log("recursion count - ", count) inputData := buildInputFromuint64(count) @@ -122,7 +126,7 @@ func TestFEVMRecursiveFail(t *testing.T) { 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) + require.Equal(t, exitcode.ExitCode(37), wait.Receipt.ExitCode) }) } } @@ -149,24 +153,26 @@ func TestFEVMRecursive2(t *testing.T) { 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) { +// TestFEVMRecursiveDelegatecallCount tests the maximum delegatecall recursion depth. It currently +// succeeds succeeds up to 237 times. +func TestFEVMRecursiveDelegatecallCount(t *testing.T) { ctx, cancel, client := kit.SetupFEVMTest(t) defer cancel() + highestSuccessCount := uint64(225) + filename := "contracts/RecursiveDelegeatecall.hex" + recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(1)) + recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(2)) + recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(10)) + recursiveDelegatecallSuccess(ctx, t, client, filename, uint64(100)) + recursiveDelegatecallSuccess(ctx, t, client, filename, highestSuccessCount) - //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)) + recursiveDelegatecallFail(ctx, t, client, filename, highestSuccessCount+1) + recursiveDelegatecallFail(ctx, t, client, filename, uint64(1000)) + recursiveDelegatecallFail(ctx, t, client, filename, uint64(10000000)) - for i := uint64(239); i <= 800; i += 40 { - recursiveDelegatecallFail(ctx, t, client, filename, i) - } } // TestFEVMBasic does a basic fevm contract installation and invocation @@ -469,10 +475,9 @@ func TestFEVMSendGasLimit(t *testing.T) { } // 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? + //TODO change the gas limit of this invocation and confirm that the number of errors is + // different ctx, cancel, client := kit.SetupFEVMTest(t) defer cancel() @@ -485,17 +490,16 @@ func TestFEVMDelegateCallRecursiveFail(t *testing.T) { inputDataValue := inputDataFromArray([]byte{7}) inputData := append(inputDataContract, inputDataValue...) - //verify that the returned value of the call to setvars is 7 + //verify that we run out of gas then revert. _, wait, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, actorAddr, "setVarsSelf(address,uint256)", inputData) require.Error(t, err) - require.Equal(t, exitcode.SysErrorIllegalArgument, wait.Receipt.ExitCode) + require.Equal(t, exitcode.ExitCode(33), 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) { @@ -555,7 +559,7 @@ func TestFEVMRecursiveFuncCall(t *testing.T) { t.Run("n=20", testN(20, exitcode.Ok)) t.Run("n=200", testN(200, exitcode.Ok)) t.Run("n=507", testN(507, exitcode.Ok)) - t.Run("n=508", testN(508, exitcode.ExitCode(23))) // 23 means stack overflow + t.Run("n=508", testN(508, exitcode.ExitCode(37))) // 37 means stack overflow } // TestFEVMRecursiveActorCall deploys a contract and makes a recursive actor calls @@ -584,7 +588,7 @@ func TestFEVMRecursiveActorCall(t *testing.T) { t.Run("n=200,r=1", testN(200, 1, exitcode.Ok)) t.Run("n=251,r=1", testN(251, 1, exitcode.Ok)) - t.Run("n=252,r=1-fails", testN(252, 1, exitcode.ExitCode(23))) // 23 means stack overflow + t.Run("n=252,r=1-fails", testN(252, 1, exitcode.ExitCode(37))) // 37 means stack overflow t.Run("n=0,r=10", testN(0, 10, exitcode.Ok)) t.Run("n=1,r=10", testN(1, 10, exitcode.Ok)) @@ -592,7 +596,7 @@ func TestFEVMRecursiveActorCall(t *testing.T) { t.Run("n=200,r=10", testN(200, 10, exitcode.Ok)) t.Run("n=251,r=10", testN(251, 10, exitcode.Ok)) - t.Run("n=252,r=10-fails", testN(252, 10, exitcode.ExitCode(23))) + t.Run("n=252,r=10-fails", testN(252, 10, exitcode.ExitCode(37))) t.Run("n=0,r=32", testN(0, 32, exitcode.Ok)) t.Run("n=1,r=32", testN(1, 32, exitcode.Ok)) @@ -600,9 +604,267 @@ func TestFEVMRecursiveActorCall(t *testing.T) { t.Run("n=200,r=32", testN(200, 32, exitcode.Ok)) t.Run("n=251,r=32", testN(251, 32, exitcode.Ok)) - t.Run("n=0,r=254", testN(0, 254, exitcode.Ok)) - t.Run("n=251,r=170", testN(251, 170, exitcode.Ok)) + t.Run("n=0,r=252", testN(0, 252, exitcode.Ok)) + t.Run("n=251,r=166", testN(251, 166, exitcode.Ok)) - t.Run("n=0,r=255-fails", testN(0, 255, exitcode.ExitCode(33))) // 33 means transaction reverted - t.Run("n=251,r=171-fails", testN(251, 171, exitcode.ExitCode(33))) + t.Run("n=0,r=253-fails", testN(0, 253, exitcode.ExitCode(33))) // 33 means transaction reverted + t.Run("n=251,r=167-fails", testN(251, 167, exitcode.ExitCode(33))) +} + +// TestFEVMRecursiveActorCallEstimate +func TestFEVMRecursiveActorCallEstimate(t *testing.T) { + ctx, cancel, client := kit.SetupFEVMTest(t) + defer cancel() + + //install contract Actor + filenameActor := "contracts/ExternalRecursiveCallSimple.hex" + _, actorAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor) + + contractAddr, err := ethtypes.EthAddressFromFilecoinAddress(actorAddr) + require.NoError(t, err) + + // create a new Ethereum account + key, ethAddr, ethFilAddr := client.EVM().NewAccount() + kit.SendFunds(ctx, t, client, ethFilAddr, types.FromFil(1000)) + + makeParams := func(r int) []byte { + funcSignature := "exec1(uint256)" + entryPoint := kit.CalcFuncSignature(funcSignature) + + inputData := make([]byte, 32) + binary.BigEndian.PutUint64(inputData[24:], uint64(r)) + + params := append(entryPoint, inputData...) + + return params + } + + testN := func(r int) func(t *testing.T) { + return func(t *testing.T) { + t.Logf("running with %d recursive calls", r) + + params := makeParams(r) + gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + From: ðAddr, + To: &contractAddr, + Data: params, + }) + require.NoError(t, err) + require.LessOrEqual(t, int64(gaslimit), build.BlockGasLimit) + + t.Logf("EthEstimateGas GasLimit=%d", gaslimit) + + maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + require.NoError(t, err) + + nonce, err := client.MpoolGetNonce(ctx, ethFilAddr) + require.NoError(t, err) + + tx := ðtypes.EthTxArgs{ + ChainID: build.Eip155ChainId, + To: &contractAddr, + Value: big.Zero(), + Nonce: int(nonce), + MaxFeePerGas: types.NanoFil, + MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), + GasLimit: int(gaslimit), + Input: params, + V: big.Zero(), + R: big.Zero(), + S: big.Zero(), + } + + client.EVM().SignTransaction(tx, key.PrivateKey) + hash := client.EVM().SubmitTransaction(ctx, tx) + + smsg, err := tx.ToSignedMessage() + require.NoError(t, err) + + _, err = client.StateWaitMsg(ctx, smsg.Cid(), 0, 0, false) + require.NoError(t, err) + + receipt, err := client.EthGetTransactionReceipt(ctx, hash) + require.NoError(t, err) + require.NotNil(t, receipt) + + t.Logf("Receipt GasUsed=%d", receipt.GasUsed) + t.Logf("Ratio %0.2f", float64(receipt.GasUsed)/float64(gaslimit)) + t.Logf("Overestimate %0.2f", ((float64(gaslimit)/float64(receipt.GasUsed))-1)*100) + + require.EqualValues(t, ethtypes.EthUint64(1), receipt.Status) + } + } + + t.Run("n=1", testN(1)) + t.Run("n=2", testN(2)) + t.Run("n=3", testN(3)) + t.Run("n=4", testN(4)) + t.Run("n=5", testN(5)) + t.Run("n=10", testN(10)) + t.Run("n=20", testN(20)) + t.Run("n=30", testN(30)) + t.Run("n=40", testN(40)) + t.Run("n=50", testN(50)) + t.Run("n=100", testN(100)) +} + +// TestFEVM deploys a contract while sending value to it +func TestFEVMDeployWithValue(t *testing.T) { + ctx, cancel, client := kit.SetupFEVMTest(t) + defer cancel() + + //testValue is the amount sent when the contract is created + //at the end we check that the new contract has a balance of testValue + testValue := big.NewInt(20) + + // deploy DeployValueTest which creates NewContract + // testValue is sent to DeployValueTest and that amount is + // also sent to NewContract + filenameActor := "contracts/DeployValueTest.hex" + fromAddr, idAddr := client.EVM().DeployContractFromFilenameWithValue(ctx, filenameActor, testValue) + + //call getNewContractBalance to find the value of NewContract + ret, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "getNewContractBalance()", []byte{}) + require.NoError(t, err) + + contractBalance, err := decodeOutputToUint64(ret) + require.NoError(t, err) + + //require balance of NewContract is testValue + require.Equal(t, testValue.Uint64(), contractBalance) +} + +func TestFEVMDestroyCreate2(t *testing.T) { + ctx, cancel, client := kit.SetupFEVMTest(t) + defer cancel() + + //deploy create2 factory contract + filename := "contracts/Create2Factory.hex" + fromAddr, idAddr := client.EVM().DeployContractFromFilename(ctx, filename) + + //construct salt for create2 + salt := make([]byte, 32) + _, err := rand.Read(salt) + require.NoError(t, err) + + //deploy contract using create2 factory + selfDestructAddress, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "deploy(bytes32)", salt) + require.NoError(t, err) + + //convert to filecoin actor address so we can call InvokeContractByFuncName + ea, err := ethtypes.CastEthAddress(selfDestructAddress[12:]) + require.NoError(t, err) + selfDestructAddressActor, err := ea.ToFilecoinAddress() + require.NoError(t, err) + + //read sender property from contract + ret, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, selfDestructAddressActor, "sender()", []byte{}) + require.NoError(t, err) + + //assert contract has correct data + ethFromAddr := inputDataFromFrom(ctx, t, client, fromAddr) + require.Equal(t, ethFromAddr, ret) + + //run test() which 1.calls sefldestruct 2. verifies sender() is the correct value 3. attempts and fails to deploy via create2 + testSenderAddress, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "test(address)", selfDestructAddress) + require.NoError(t, err) + require.Equal(t, testSenderAddress, ethFromAddr) + + //read sender() but get response of 0x0 because of self destruct + senderAfterDestroy, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, selfDestructAddressActor, "sender()", []byte{}) + require.NoError(t, err) + require.Equal(t, []byte{}, senderAfterDestroy) + + // deploy new contract at same address usign same salt + newAddressSelfDestruct, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, idAddr, "deploy(bytes32)", salt) + require.NoError(t, err) + require.Equal(t, newAddressSelfDestruct, selfDestructAddress) + + //verify sender() property is correct + senderSecondCall, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, selfDestructAddressActor, "sender()", []byte{}) + require.NoError(t, err) + + //assert contract has correct data + require.Equal(t, ethFromAddr, senderSecondCall) + +} + +func TestFEVMBareTransferTriggersSmartContractLogic(t *testing.T) { + ctx, cancel, client := kit.SetupFEVMTest(t) + defer cancel() + + // This contract emits an event on receiving value. + filename := "contracts/ValueSender.hex" + _, contractAddr := client.EVM().DeployContractFromFilename(ctx, filename) + + accctKey, accntEth, accntFil := client.EVM().NewAccount() + kit.SendFunds(ctx, t, client, accntFil, types.FromFil(10)) + + contractEth, err := ethtypes.EthAddressFromFilecoinAddress(contractAddr) + require.NoError(t, err) + + gaslimit, err := client.EthEstimateGas(ctx, ethtypes.EthCall{ + From: &accntEth, + To: &contractEth, + Value: ethtypes.EthBigInt(big.NewInt(100)), + }) + require.NoError(t, err) + + maxPriorityFeePerGas, err := client.EthMaxPriorityFeePerGas(ctx) + require.NoError(t, err) + + tx := ethtypes.EthTxArgs{ + ChainID: build.Eip155ChainId, + Value: big.NewInt(100), + Nonce: 0, + To: &contractEth, + MaxFeePerGas: types.NanoFil, + MaxPriorityFeePerGas: big.Int(maxPriorityFeePerGas), + GasLimit: int(gaslimit), + V: big.Zero(), + R: big.Zero(), + S: big.Zero(), + } + + client.EVM().SignTransaction(&tx, accctKey.PrivateKey) + + hash := client.EVM().SubmitTransaction(ctx, &tx) + + var receipt *api.EthTxReceipt + for i := 0; i < 1000; i++ { + receipt, err = client.EthGetTransactionReceipt(ctx, hash) + require.NoError(t, err) + if receipt != nil { + break + } + time.Sleep(500 * time.Millisecond) + } + + // The receive() function emits one log, that's how we know we hit it. + require.Len(t, receipt.Logs, 1) +} + +func TestFEVMProxyUpgradeable(t *testing.T) { + ctx, cancel, client := kit.SetupFEVMTest(t) + defer cancel() + + //install transparently upgradeable proxy + proxyFilename := "contracts/TransparentUpgradeableProxy.hex" + fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, proxyFilename) + + _, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "test()", []byte{}) + require.NoError(t, err) +} + +func TestFEVMGetBlockDifficulty(t *testing.T) { + ctx, cancel, client := kit.SetupFEVMTest(t) + defer cancel() + + //install contract + filenameActor := "contracts/GetDifficulty.hex" + fromAddr, contractAddr := client.EVM().DeployContractFromFilename(ctx, filenameActor) + + ret, _, err := client.EVM().InvokeContractByFuncName(ctx, fromAddr, contractAddr, "getDifficulty()", []byte{}) + require.NoError(t, err) + require.Equal(t, len(ret), 32) } diff --git a/itests/gas_estimation_test.go b/itests/gas_estimation_test.go index 42cad8611..24013c885 100644 --- a/itests/gas_estimation_test.go +++ b/itests/gas_estimation_test.go @@ -2,16 +2,23 @@ package itests import ( "context" + "math" "testing" "time" "github.com/stretchr/testify/require" + "github.com/filecoin-project/go-address" + "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" + "github.com/filecoin-project/go-state-types/exitcode" "github.com/filecoin-project/lotus/api" + "github.com/filecoin-project/lotus/build" + "github.com/filecoin-project/lotus/chain/actors/builtin" "github.com/filecoin-project/lotus/chain/actors/builtin/account" "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/vm" "github.com/filecoin-project/lotus/itests/kit" ) @@ -37,8 +44,9 @@ func TestEstimateGasNoFunds(t *testing.T) { sm, err := client.MpoolPushMessage(ctx, msg, nil) require.NoError(t, err) - _, err = client.StateWaitMsg(ctx, sm.Cid(), 3, api.LookbackNoLimit, true) + ret, err := client.StateWaitMsg(ctx, sm.Cid(), 3, api.LookbackNoLimit, true) require.NoError(t, err) + require.True(t, ret.Receipt.ExitCode.IsSuccess()) // Make sure we can estimate gas even if we have no funds. msg2 := &types.Message{ @@ -52,3 +60,110 @@ func TestEstimateGasNoFunds(t *testing.T) { require.NoError(t, err) require.NotZero(t, limit) } + +// Make sure that we correctly calculate the inclusion cost. Especially, make sure the FVM and Lotus +// agree and that: +// 1. The FVM will never charge _less_ than the inclusion cost. +// 2. The FVM will never fine a storage provider for including a message that costs exactly the +// inclusion cost. +func TestEstimateInclusion(t *testing.T) { + ctx := context.Background() + + kit.QuietMiningLogs() + + // We need this to be "correct" in this test so that lotus can get the correct gas value + // (which, unfortunately, looks at the height and not the current network version). + oldPrices := vm.Prices + vm.Prices = map[abi.ChainEpoch]vm.Pricelist{ + 0: oldPrices[build.UpgradeHyggeHeight], + } + t.Cleanup(func() { vm.Prices = oldPrices }) + + client, _, ens := kit.EnsembleMinimal(t, kit.MockProofs()) + ens.InterconnectAll().BeginMining(10 * time.Millisecond) + + // First, try sending a message that should have no fees beyond the inclusion cost. I.e., it + // does absolutely nothing: + msg := &types.Message{ + From: client.DefaultKey.Address, + To: client.DefaultKey.Address, + Value: big.Zero(), + GasLimit: 0, + GasFeeCap: abi.NewTokenAmount(10000), + GasPremium: big.Zero(), + } + + burntBefore, err := client.WalletBalance(ctx, builtin.BurntFundsActorAddr) + require.NoError(t, err) + balanceBefore, err := client.WalletBalance(ctx, client.DefaultKey.Address) + require.NoError(t, err) + + // Sign the message and compute the correct inclusion cost. + var smsg *types.SignedMessage + for i := 0; ; i++ { + var err error + smsg, err = client.WalletSignMessage(ctx, client.DefaultKey.Address, msg) + require.NoError(t, err) + estimatedGas := vm.PricelistByEpoch(math.MaxInt).OnChainMessage(smsg.ChainLength()).Total() + if estimatedGas == msg.GasLimit { + break + } + // Try 10 times to get the right gas value. + require.Less(t, i, 10, "unable to estimate gas: %s != %s", estimatedGas, msg.GasLimit) + msg.GasLimit = estimatedGas + } + + cid, err := client.MpoolPush(ctx, smsg) + require.NoError(t, err) + ret, err := client.StateWaitMsg(ctx, cid, 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.True(t, ret.Receipt.ExitCode.IsSuccess()) + require.Equal(t, msg.GasLimit, ret.Receipt.GasUsed) + + // Then try sending a message of the same size that tries to create an actor. This should + // get successfully included, but fail with out of gas: + + // Mutate the last byte to get a new address of the same length. + toBytes := msg.To.Bytes() + toBytes[len(toBytes)-1] += 1 //nolint:golint + newAddr, err := address.NewFromBytes(toBytes) + require.NoError(t, err) + + msg.Nonce = 1 + msg.To = newAddr + smsg, err = client.WalletSignMessage(ctx, client.DefaultKey.Address, msg) + require.NoError(t, err) + + cid, err = client.MpoolPush(ctx, smsg) + require.NoError(t, err) + ret, err = client.StateWaitMsg(ctx, cid, 3, api.LookbackNoLimit, true) + require.NoError(t, err) + require.Equal(t, ret.Receipt.ExitCode, exitcode.SysErrOutOfGas) + require.Equal(t, msg.GasLimit, ret.Receipt.GasUsed) + + // Now make sure that the client is the only contributor to the burnt funds actor (the + // miners should not have been fined for either message). + + burntAfter, err := client.WalletBalance(ctx, builtin.BurntFundsActorAddr) + require.NoError(t, err) + balanceAfter, err := client.WalletBalance(ctx, client.DefaultKey.Address) + require.NoError(t, err) + + burnt := big.Sub(burntAfter, burntBefore) + spent := big.Sub(balanceBefore, balanceAfter) + + require.Equal(t, burnt, spent) + + // Finally, try to submit a message with too little gas. This should fail. + + msg.Nonce = 2 + msg.To = msg.From + msg.GasLimit -= 1 //nolint:golint + + smsg, err = client.WalletSignMessage(ctx, client.DefaultKey.Address, msg) + require.NoError(t, err) + + _, err = client.MpoolPush(ctx, smsg) + require.ErrorContains(t, err, "will not be included in a block") + require.ErrorContains(t, err, "cannot be less than the cost of storing a message") +} diff --git a/itests/kit/ensemble_opts_nv.go b/itests/kit/ensemble_opts_nv.go index c44dca92f..fa0d592bf 100644 --- a/itests/kit/ensemble_opts_nv.go +++ b/itests/kit/ensemble_opts_nv.go @@ -49,12 +49,12 @@ func LatestActorsAt(upgradeHeight abi.ChainEpoch) EnsembleOpt { }) /* inline-gen start */ return UpgradeSchedule(stmgr.Upgrade{ - Network: network.Version18, + Network: network.Version19, Height: -1, }, stmgr.Upgrade{ - Network: network.Version19, + Network: network.Version20, Height: upgradeHeight, - Migration: filcns.UpgradeActorsV11, + Migration: filcns.UpgradeActorsV12, }) /* inline-gen end */ } diff --git a/itests/kit/evm.go b/itests/kit/evm.go index 6c27da557..db8cf2cbb 100644 --- a/itests/kit/evm.go +++ b/itests/kit/evm.go @@ -5,6 +5,7 @@ import ( "context" "encoding/binary" "encoding/hex" + "errors" "fmt" "os" "testing" @@ -18,7 +19,6 @@ import ( "golang.org/x/crypto/sha3" "github.com/filecoin-project/go-address" - amt4 "github.com/filecoin-project/go-amt-ipld/v4" "github.com/filecoin-project/go-state-types/abi" "github.com/filecoin-project/go-state-types/big" builtintypes "github.com/filecoin-project/go-state-types/builtin" @@ -42,26 +42,18 @@ func (f *TestFullNode) EVM() *EVM { return &EVM{f} } -func (e *EVM) DeployContract(ctx context.Context, sender address.Address, bytecode []byte) eam.CreateReturn { +func (e *EVM) DeployContractWithValue(ctx context.Context, sender address.Address, bytecode []byte, value big.Int) eam.CreateReturn { require := require.New(e.t) - nonce, err := e.MpoolGetNonce(ctx, sender) - if err != nil { - nonce = 0 // assume a zero nonce on error (e.g. sender doesn't exist). - } - - var salt [32]byte - binary.BigEndian.PutUint64(salt[:], nonce) - method := builtintypes.MethodsEAM.CreateExternal initcode := abi.CborBytes(bytecode) - params, err := actors.SerializeParams(&initcode) - require.NoError(err) + params, errActors := actors.SerializeParams(&initcode) + require.NoError(errActors) msg := &types.Message{ To: builtintypes.EthereumAddressManagerActorAddr, From: sender, - Value: big.Zero(), + Value: value, Method: method, Params: params, } @@ -71,7 +63,7 @@ func (e *EVM) DeployContract(ctx context.Context, sender address.Address, byteco require.NoError(err) e.t.Log("waiting for message to execute") - wait, err := e.StateWaitMsg(ctx, smsg.Cid(), 0, 0, false) + wait, err := e.StateWaitMsg(ctx, smsg.Cid(), 3, 0, false) require.NoError(err) require.True(wait.Receipt.ExitCode.IsSuccess(), "contract installation failed") @@ -83,8 +75,11 @@ func (e *EVM) DeployContract(ctx context.Context, sender address.Address, byteco return result } +func (e *EVM) DeployContract(ctx context.Context, sender address.Address, bytecode []byte) eam.CreateReturn { + return e.DeployContractWithValue(ctx, sender, bytecode, big.Zero()) +} -func (e *EVM) DeployContractFromFilename(ctx context.Context, binFilename string) (address.Address, address.Address) { +func (e *EVM) DeployContractFromFilenameWithValue(ctx context.Context, binFilename string, value big.Int) (address.Address, address.Address) { contractHex, err := os.ReadFile(binFilename) require.NoError(e.t, err) @@ -97,12 +92,15 @@ func (e *EVM) DeployContractFromFilename(ctx context.Context, binFilename string fromAddr, err := e.WalletDefaultAddress(ctx) require.NoError(e.t, err) - result := e.DeployContract(ctx, fromAddr, contract) + result := e.DeployContractWithValue(ctx, fromAddr, contract, value) idAddr, err := address.NewIDAddress(result.ActorID) require.NoError(e.t, err) return fromAddr, idAddr } +func (e *EVM) DeployContractFromFilename(ctx context.Context, binFilename string) (address.Address, address.Address) { + return e.DeployContractFromFilenameWithValue(ctx, binFilename, big.Zero()) +} func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target address.Address, selector []byte, inputData []byte) (*api.MsgLookup, error) { params := append(selector, inputData...) @@ -129,11 +127,15 @@ func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target } e.t.Log("waiting for message to execute") - wait, err := e.StateWaitMsg(ctx, smsg.Cid(), 0, 0, false) + wait, err := e.StateWaitMsg(ctx, smsg.Cid(), 3, 0, false) if err != nil { return nil, err } - + if !wait.Receipt.ExitCode.IsSuccess() { + result, err := e.StateReplay(ctx, types.EmptyTSK, wait.Message) + require.NoError(e.t, err) + e.t.Log(result.Error) + } return wait, nil } @@ -141,21 +143,10 @@ func (e *EVM) InvokeSolidity(ctx context.Context, sender address.Address, target func (e *EVM) LoadEvents(ctx context.Context, eventsRoot cid.Cid) []types.Event { require := require.New(e.t) - s := &apiIpldStore{ctx, e} - amt, err := amt4.LoadAMT(ctx, s, eventsRoot, amt4.UseTreeBitWidth(types.EventAMTBitwidth)) + events, err := e.ChainGetEvents(ctx, eventsRoot) require.NoError(err) - ret := make([]types.Event, 0, amt.Len()) - err = amt.ForEach(ctx, func(u uint64, deferred *cbg.Deferred) error { - var evt types.Event - if err := evt.UnmarshalCBOR(bytes.NewReader(deferred.Raw)); err != nil { - return err - } - ret = append(ret, evt) - return nil - }) - require.NoError(err) - return ret + return events } func (e *EVM) NewAccount() (*key.Key, ethtypes.EthAddress, address.Address) { @@ -251,7 +242,9 @@ func (e *EVM) InvokeContractByFuncName(ctx context.Context, fromAddr address.Add return nil, wait, err } if !wait.Receipt.ExitCode.IsSuccess() { - return nil, wait, fmt.Errorf("contract execution failed - %v", wait.Receipt.ExitCode) + result, err := e.StateReplay(ctx, types.EmptyTSK, wait.Message) + require.NoError(e.t, err) + return nil, wait, errors.New(result.Error) } result, err := cbg.ReadByteArray(bytes.NewBuffer(wait.Receipt.Return), uint64(len(wait.Receipt.Return))) if err != nil { @@ -326,7 +319,7 @@ func removeLeadingZeros(data []byte) []byte { } func SetupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *TestFullNode) { - //make all logs extra quiet for fevm tests + // make all logs extra quiet for fevm tests lvl, err := logging.LevelFromString("error") if err != nil { panic(err) @@ -349,7 +342,7 @@ func SetupFEVMTest(t *testing.T) (context.Context, context.CancelFunc, *TestFull return ctx, cancel, client } -func (e *EVM) TransferValueOrFail(ctx context.Context, fromAddr address.Address, toAddr address.Address, sendAmount big.Int) { +func (e *EVM) TransferValueOrFail(ctx context.Context, fromAddr address.Address, toAddr address.Address, sendAmount big.Int) *api.MsgLookup { sendMsg := &types.Message{ From: fromAddr, To: toAddr, @@ -360,4 +353,79 @@ func (e *EVM) TransferValueOrFail(ctx context.Context, fromAddr address.Address, 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) + return mLookup +} + +func NewEthFilterBuilder() *EthFilterBuilder { + return &EthFilterBuilder{} +} + +type EthFilterBuilder struct { + filter ethtypes.EthFilterSpec +} + +func (e *EthFilterBuilder) Filter() *ethtypes.EthFilterSpec { return &e.filter } + +func (e *EthFilterBuilder) FromBlock(v string) *EthFilterBuilder { + e.filter.FromBlock = &v + return e +} + +func (e *EthFilterBuilder) FromBlockEpoch(v abi.ChainEpoch) *EthFilterBuilder { + s := ethtypes.EthUint64(v).Hex() + e.filter.FromBlock = &s + return e +} + +func (e *EthFilterBuilder) ToBlock(v string) *EthFilterBuilder { + e.filter.ToBlock = &v + return e +} + +func (e *EthFilterBuilder) ToBlockEpoch(v abi.ChainEpoch) *EthFilterBuilder { + s := ethtypes.EthUint64(v).Hex() + e.filter.ToBlock = &s + return e +} + +func (e *EthFilterBuilder) BlockHash(h ethtypes.EthHash) *EthFilterBuilder { + e.filter.BlockHash = &h + return e +} + +func (e *EthFilterBuilder) AddressOneOf(as ...ethtypes.EthAddress) *EthFilterBuilder { + e.filter.Address = as + return e +} + +func (e *EthFilterBuilder) Topic1OneOf(hs ...ethtypes.EthHash) *EthFilterBuilder { + if len(e.filter.Topics) == 0 { + e.filter.Topics = make(ethtypes.EthTopicSpec, 1) + } + e.filter.Topics[0] = hs + return e +} + +func (e *EthFilterBuilder) Topic2OneOf(hs ...ethtypes.EthHash) *EthFilterBuilder { + for len(e.filter.Topics) < 2 { + e.filter.Topics = append(e.filter.Topics, nil) + } + e.filter.Topics[1] = hs + return e +} + +func (e *EthFilterBuilder) Topic3OneOf(hs ...ethtypes.EthHash) *EthFilterBuilder { + for len(e.filter.Topics) < 3 { + e.filter.Topics = append(e.filter.Topics, nil) + } + e.filter.Topics[2] = hs + return e +} + +func (e *EthFilterBuilder) Topic4OneOf(hs ...ethtypes.EthHash) *EthFilterBuilder { + for len(e.filter.Topics) < 4 { + e.filter.Topics = append(e.filter.Topics, nil) + } + e.filter.Topics[3] = hs + return e } diff --git a/itests/kit/solidity.go b/itests/kit/solidity.go new file mode 100644 index 000000000..6999bd91b --- /dev/null +++ b/itests/kit/solidity.go @@ -0,0 +1,68 @@ +package kit + +import ( + "golang.org/x/crypto/sha3" + + "github.com/filecoin-project/lotus/chain/types/ethtypes" +) + +func EthTopicHash(sig string) ethtypes.EthHash { + hasher := sha3.NewLegacyKeccak256() + hasher.Write([]byte(sig)) + var hash ethtypes.EthHash + copy(hash[:], hasher.Sum(nil)) + return hash +} + +func EthFunctionHash(sig string) []byte { + hasher := sha3.NewLegacyKeccak256() + hasher.Write([]byte(sig)) + return hasher.Sum(nil)[:4] +} + +// SolidityContractDef holds information about one of the test contracts +type SolidityContractDef struct { + Filename string // filename of the hex of the contract, e.g. contracts/EventMatrix.hex + Fn map[string][]byte // mapping of function names to 32-bit selector + Ev map[string]ethtypes.EthHash // mapping of event names to 256-bit signature hashes +} + +var EventMatrixContract = SolidityContractDef{ + Filename: "contracts/EventMatrix.hex", + Fn: map[string][]byte{ + "logEventZeroData": EthFunctionHash("logEventZeroData()"), + "logEventOneData": EthFunctionHash("logEventOneData(uint256)"), + "logEventTwoData": EthFunctionHash("logEventTwoData(uint256,uint256)"), + "logEventThreeData": EthFunctionHash("logEventThreeData(uint256,uint256,uint256)"), + "logEventFourData": EthFunctionHash("logEventFourData(uint256,uint256,uint256,uint256)"), + "logEventOneIndexed": EthFunctionHash("logEventOneIndexed(uint256)"), + "logEventTwoIndexed": EthFunctionHash("logEventTwoIndexed(uint256,uint256)"), + "logEventThreeIndexed": EthFunctionHash("logEventThreeIndexed(uint256,uint256,uint256)"), + "logEventOneIndexedWithData": EthFunctionHash("logEventOneIndexedWithData(uint256,uint256)"), + "logEventTwoIndexedWithData": EthFunctionHash("logEventTwoIndexedWithData(uint256,uint256,uint256)"), + "logEventThreeIndexedWithData": EthFunctionHash("logEventThreeIndexedWithData(uint256,uint256,uint256,uint256)"), + }, + Ev: map[string]ethtypes.EthHash{ + "EventZeroData": EthTopicHash("EventZeroData()"), + "EventOneData": EthTopicHash("EventOneData(uint256)"), + "EventTwoData": EthTopicHash("EventTwoData(uint256,uint256)"), + "EventThreeData": EthTopicHash("EventThreeData(uint256,uint256,uint256)"), + "EventFourData": EthTopicHash("EventFourData(uint256,uint256,uint256,uint256)"), + "EventOneIndexed": EthTopicHash("EventOneIndexed(uint256)"), + "EventTwoIndexed": EthTopicHash("EventTwoIndexed(uint256,uint256)"), + "EventThreeIndexed": EthTopicHash("EventThreeIndexed(uint256,uint256,uint256)"), + "EventOneIndexedWithData": EthTopicHash("EventOneIndexedWithData(uint256,uint256)"), + "EventTwoIndexedWithData": EthTopicHash("EventTwoIndexedWithData(uint256,uint256,uint256)"), + "EventThreeIndexedWithData": EthTopicHash("EventThreeIndexedWithData(uint256,uint256,uint256,uint256)"), + }, +} + +var EventsContract = SolidityContractDef{ + Filename: "contracts/events.bin", + Fn: map[string][]byte{ + "log_zero_data": {0x00, 0x00, 0x00, 0x00}, + "log_zero_nodata": {0x00, 0x00, 0x00, 0x01}, + "log_four_data": {0x00, 0x00, 0x00, 0x02}, + }, + Ev: map[string]ethtypes.EthHash{}, +} diff --git a/itests/migration_nv20_test.go b/itests/migration_nv20_test.go new file mode 100644 index 000000000..c1c91d37c --- /dev/null +++ b/itests/migration_nv20_test.go @@ -0,0 +1,97 @@ +package itests + +import ( + "context" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-state-types/abi" + actorstypes "github.com/filecoin-project/go-state-types/actors" + "github.com/filecoin-project/go-state-types/builtin" + "github.com/filecoin-project/go-state-types/manifest" + "github.com/filecoin-project/go-state-types/network" + gstStore "github.com/filecoin-project/go-state-types/store" + + "github.com/filecoin-project/lotus/blockstore" + "github.com/filecoin-project/lotus/chain/actors" + builtin2 "github.com/filecoin-project/lotus/chain/actors/builtin" + "github.com/filecoin-project/lotus/chain/consensus/filcns" + "github.com/filecoin-project/lotus/chain/state" + "github.com/filecoin-project/lotus/chain/stmgr" + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/types/ethtypes" + "github.com/filecoin-project/lotus/chain/vm" + "github.com/filecoin-project/lotus/itests/kit" + "github.com/filecoin-project/lotus/node/impl" +) + +func TestMigrationNV20(t *testing.T) { + kit.QuietMiningLogs() + + upgradeEpoch := abi.ChainEpoch(100) + testClient, testMiner, ens := kit.EnsembleMinimal(t, kit.MockProofs(), + kit.UpgradeSchedule(stmgr.Upgrade{ + Network: network.Version19, + Height: -1, + }, stmgr.Upgrade{ + Network: network.Version20, + Height: upgradeEpoch, + Migration: filcns.UpgradeActorsV12, + }, + )) + + ens.InterconnectAll().BeginMining(10 * time.Millisecond) + + clientApi := testClient.FullNode.(*impl.FullNodeAPI) + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + testClient.WaitTillChain(ctx, kit.HeightAtLeast(upgradeEpoch+5)) + + bs := blockstore.NewAPIBlockstore(testClient) + ctxStore := gstStore.WrapBlockStore(ctx, bs) + + currTs, err := clientApi.ChainHead(ctx) + require.NoError(t, err) + + // Verify network version. + nv, err := testClient.StateNetworkVersion(ctx, types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, network.Version20, nv) + + newStateTree, err := state.LoadStateTree(ctxStore, currTs.Blocks()[0].ParentStateRoot) + require.NoError(t, err) + + require.Equal(t, types.StateTreeVersion5, newStateTree.Version()) + + codeCids1, ok := actors.GetActorCodeIDsFromManifest(actorstypes.Version12) + require.True(t, ok) + + codeCids2, err := clientApi.StateActorCodeCIDs(ctx, nv) + require.NoError(t, err) + require.Equal(t, codeCids1, codeCids2) + + // Check that the miner actor was migrated. + minerActor, err := clientApi.StateGetActor(ctx, testMiner.ActorAddr, types.EmptyTSK) + require.NoError(t, err) + require.Equal(t, codeCids1[manifest.MinerKey], minerActor.Code) + + // check EAM + eamActor, err := newStateTree.GetActor(builtin.EthereumAddressManagerActorAddr) + require.NoError(t, err) + eamCodeCid, ok := codeCids1[manifest.EamKey] + require.True(t, ok) + require.Equal(t, eamCodeCid, eamActor.Code) + + // check the EthZeroAddress + ethZeroAddr, err := (ethtypes.EthAddress{}).ToFilecoinAddress() + require.NoError(t, err) + ethZeroAddrID, err := newStateTree.LookupID(ethZeroAddr) + require.NoError(t, err) + ethZeroActor, err := newStateTree.GetActor(ethZeroAddrID) + require.NoError(t, err) + require.True(t, builtin2.IsEthAccountActor(ethZeroActor.Code)) + require.Equal(t, vm.EmptyObjectCid, ethZeroActor.Head) +} diff --git a/itests/specs/eth_openrpc.json b/itests/specs/eth_openrpc.json new file mode 100644 index 000000000..1947bbd32 --- /dev/null +++ b/itests/specs/eth_openrpc.json @@ -0,0 +1,4392 @@ +{ + "openrpc": "1.2.4", + "info": { + "title": "Ethereum JSON-RPC Specification", + "description": "A specification of the standard interface for Ethereum clients.", + "license": { + "name": "CC0-1.0", + "url": "https://creativecommons.org/publicdomain/zero/1.0/legalcode" + }, + "version": "0.0.0" + }, + "methods": [ + { + "name": "eth_getBlockByHash", + "summary": "Returns information about a block by hash.", + "params": [ + { + "name": "Block hash", + "required": true, + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "name": "Hydrated transactions", + "required": true, + "schema": { + "title": "hydrated", + "type": "boolean" + } + } + ], + "result": { + "name": "Block information", + "schema": { + "title": "Block object", + "type": "object", + "required": [ + "parentHash", + "sha3Uncles", + "miner", + "stateRoot", + "transactionsRoot", + "receiptsRoot", + "logsBloom", + "number", + "gasLimit", + "gasUsed", + "timestamp", + "extraData", + "mixHash", + "nonce", + "size", + "transactions", + "uncles" + ], + "properties": { + "parentHash": { + "title": "Parent block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "sha3Uncles": { + "title": "Ommers hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "miner": { + "title": "Coinbase", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "stateRoot": { + "title": "State root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionsRoot": { + "title": "Transactions root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "receiptsRoot": { + "title": "Receipts root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "logsBloom": { + "title": "Bloom filter", + "type": "string", + "pattern": "^0x[0-9a-f]{512}$" + }, + "difficulty": { + "title": "Difficulty", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "number": { + "title": "Number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "gasLimit": { + "title": "Gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "gasUsed": { + "title": "Gas used", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "timestamp": { + "title": "Timestamp", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "extraData": { + "title": "Extra data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "mixHash": { + "title": "Mix hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "nonce": { + "title": "Nonce", + "type": "string", + "pattern": "^0x[0-9a-f]{16}$" + }, + "totalDifficulty": { + "title": "Total difficult", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "baseFeePerGas": { + "title": "Base fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "size": { + "title": "Block size", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactions": { + "anyOf": [ + { + "title": "Transaction hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "Full transactions", + "type": "array", + "items": { + "oneOf": [ + { + "title": "Signed 1559 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "input", + "maxFeePerGas", + "maxPriorityFeePerGas", + "nonce", + "r", + "s", + "type", + "value", + "v" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed 2930 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "value", + "v" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed Legacy Transaction", + "type": "object", + "required": [ + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "v", + "value" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + ] + } + } + ] + }, + "uncles": { + "title": "Uncles", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + } + }, + { + "name": "eth_getBlockByNumber", + "summary": "Returns information about a block by number.", + "params": [ + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + }, + { + "name": "Hydrated transactions", + "required": true, + "schema": { + "title": "hydrated", + "type": "boolean" + } + } + ], + "result": { + "name": "Block information", + "schema": { + "title": "Block object", + "type": "object", + "required": [ + "parentHash", + "sha3Uncles", + "miner", + "stateRoot", + "transactionsRoot", + "receiptsRoot", + "logsBloom", + "number", + "gasLimit", + "gasUsed", + "timestamp", + "extraData", + "mixHash", + "nonce", + "size", + "transactions", + "uncles" + ], + "properties": { + "parentHash": { + "title": "Parent block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "sha3Uncles": { + "title": "Ommers hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "miner": { + "title": "Coinbase", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "stateRoot": { + "title": "State root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionsRoot": { + "title": "Transactions root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "receiptsRoot": { + "title": "Receipts root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "logsBloom": { + "title": "Bloom filter", + "type": "string", + "pattern": "^0x[0-9a-f]{512}$" + }, + "difficulty": { + "title": "Difficulty", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "number": { + "title": "Number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "gasLimit": { + "title": "Gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "gasUsed": { + "title": "Gas used", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "timestamp": { + "title": "Timestamp", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "extraData": { + "title": "Extra data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "mixHash": { + "title": "Mix hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "nonce": { + "title": "Nonce", + "type": "string", + "pattern": "^0x[0-9a-f]{16}$" + }, + "totalDifficulty": { + "title": "Total difficult", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "baseFeePerGas": { + "title": "Base fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "size": { + "title": "Block size", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactions": { + "anyOf": [ + { + "title": "Transaction hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "Full transactions", + "type": "array", + "items": { + "oneOf": [ + { + "title": "Signed 1559 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "input", + "maxFeePerGas", + "maxPriorityFeePerGas", + "nonce", + "r", + "s", + "type", + "value", + "v" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed 2930 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "value", + "v" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed Legacy Transaction", + "type": "object", + "required": [ + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "v", + "value" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + ] + } + } + ] + }, + "uncles": { + "title": "Uncles", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + } + }, + { + "name": "eth_getBlockTransactionCountByHash", + "summary": "Returns the number of transactions in a block from a block matching the given block hash.", + "params": [ + { + "name": "Block hash", + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Transaction count", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getBlockTransactionCountByNumber", + "summary": "Returns the number of transactions in a block matching the given block number.", + "params": [ + { + "name": "Block", + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Transaction count", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getUncleCountByBlockHash", + "summary": "Returns the number of uncles in a block from a block matching the given block hash.", + "params": [ + { + "name": "Block hash", + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Uncle count", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getUncleCountByBlockNumber", + "summary": "Returns the number of transactions in a block matching the given block number.", + "params": [ + { + "name": "Block", + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Uncle count", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_chainId", + "summary": "Returns the chain ID of the current network.", + "params": [], + "result": { + "name": "Chain ID", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_syncing", + "summary": "Returns an object with data about the sync status or false.", + "params": [], + "result": { + "name": "Syncing status", + "schema": { + "title": "Syncing status", + "oneOf": [ + { + "title": "Syncing progress", + "type": "object", + "properties": { + "startingBlock": { + "title": "Starting block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "currentBlock": { + "title": "Current block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "highestBlock": { + "title": "Highest block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Not syncing", + "description": "Should always return false if not syncing.", + "type": "boolean" + } + ] + } + } + }, + { + "name": "eth_coinbase", + "summary": "Returns the client coinbase address.", + "params": [], + "result": { + "name": "Coinbase address", + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + } + }, + { + "name": "eth_accounts", + "summary": "Returns a list of addresses owned by client.", + "params": [], + "result": { + "name": "Accounts", + "schema": { + "title": "Accounts", + "type": "array", + "items": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + } + } + }, + { + "name": "eth_blockNumber", + "summary": "Returns the number of most recent block.", + "params": [], + "result": { + "name": "Block number", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_call", + "summary": "Executes a new message call immediately without creating a transaction on the block chain.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "type": "object", + "title": "Transaction object generic to all types", + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + } + } + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Return data", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "eth_estimateGas", + "summary": "Generates and returns an estimate of how much gas is necessary to allow the transaction to complete.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "type": "object", + "title": "Transaction object generic to all types", + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + } + } + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Gas used", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_createAccessList", + "summary": "Generates an access list for a transaction.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "type": "object", + "title": "Transaction object generic to all types", + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + } + } + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Gas used", + "schema": { + "title": "Access list result", + "type": "object", + "properties": { + "accessList": { + "title": "accessList", + "type": "array", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "error": { + "title": "error", + "type": "string" + }, + "gasUsed": { + "title": "Gas used", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + } + }, + { + "name": "eth_gasPrice", + "summary": "Returns the current price per gas in wei.", + "params": [], + "result": { + "name": "Gas price", + "schema": { + "title": "Gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_maxPriorityFeePerGas", + "summary": "Returns the current maxPriorityFeePerGas per gas in wei.", + "params": [], + "result": { + "name": "Max priority fee per gas", + "schema": { + "title": "Max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_feeHistory", + "summary": "Transaction fee history", + "description": "Returns transaction base fee per gas and effective priority fee per gas for the requested/supported block range.", + "params": [ + { + "name": "blockCount", + "description": "Requested range of blocks. Clients will return less than the requested range if not all blocks are available.", + "required": true, + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + }, + { + "name": "newestBlock", + "description": "Highest block of the requested range.", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + }, + { + "name": "rewardPercentiles", + "description": "A monotonically increasing list of percentile values. For each block in the requested range, the transactions will be sorted in ascending order by effective tip per gas and the coresponding effective tip for the percentile will be determined, accounting for gas consumed.", + "required": true, + "schema": { + "title": "rewardPercentiles", + "type": "array", + "items": { + "title": "rewardPercentile", + "description": "Floating point value between 0 and 100.", + "type": "number" + } + } + } + ], + "result": { + "name": "feeHistoryResult", + "description": "Fee history for the returned block range. This can be a subsection of the requested range if not all blocks are available.", + "schema": { + "title": "feeHistoryResults", + "description": "Fee history results.", + "type": "object", + "required": [ + "oldestBlock", + "baseFeePerGas", + "gasUsedRatio" + ], + "properties": { + "oldestBlock": { + "title": "oldestBlock", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Lowest number block of returned range." + }, + "baseFeePerGas": { + "title": "baseFeePerGasArray", + "description": "An array of block base fees per gas. This includes the next block after the newest of the returned range, because this value can be derived from the newest block. Zeroes are returned for pre-EIP-1559 blocks.", + "type": "array", + "items": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + }, + "reward": { + "title": "rewardArray", + "description": "A two-dimensional array of effective priority fees per gas at the requested block percentiles.", + "type": "array", + "items": { + "title": "rewardPercentile", + "description": "An array of effective priority fee per gas data points from a single block. All zeroes are returned if the block is empty.", + "type": "array", + "items": { + "title": "rewardPercentile", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "A given percentile sample of effective priority fees per gas from a single block in ascending order, weighted by gas used. Zeroes are returned if the block is empty." + } + } + } + } + } + } + }, + { + "name": "eth_newFilter", + "summary": "Creates a filter object, based on filter options, to notify when the state changes (logs).", + "params": [ + { + "name": "Filter", + "schema": { + "title": "filter", + "type": "object", + "properties": { + "fromBlock": { + "title": "from block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "toBlock": { + "title": "to block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "Address(es)", + "oneOf": [ + { + "title": "Address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + { + "title": "Addresses", + "type": "array", + "items": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + } + ] + }, + "topics": { + "title": "Topics", + "type": "array", + "items": { + "title": "Filter Topic List Entry", + "oneOf": [ + { + "title": "Any Topic Match", + "type": "null" + }, + { + "title": "Single Topic Match", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + { + "title": "Multiple Topic Match", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ] + } + } + } + } + } + ], + "result": { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_newBlockFilter", + "summary": "Creates a filter in the node, to notify when a new block arrives.", + "params": [], + "result": { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_newPendingTransactionFilter", + "summary": "Creates a filter in the node, to notify when new pending transactions arrive.", + "params": [], + "result": { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_uninstallFilter", + "summary": "Uninstalls a filter with given id.", + "params": [ + { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + ], + "result": { + "name": "Success", + "schema": { + "type": "boolean" + } + } + }, + { + "name": "eth_getFilterChanges", + "summary": "Polling method for a filter, which returns an array of logs which occurred since last poll.", + "params": [ + { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + ], + "result": { + "name": "Log objects", + "schema": { + "title": "Filter results", + "oneOf": [ + { + "title": "new block hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new transaction hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new logs", + "type": "array", + "items": { + "title": "log", + "type": "object", + "required": [ + "transactionHash" + ], + "properties": { + "removed": { + "title": "removed", + "type": "boolean" + }, + "logIndex": { + "title": "log index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionHash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "data": { + "title": "data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "topics": { + "title": "topics", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + } + ] + } + } + }, + { + "name": "eth_getFilterLogs", + "summary": "Returns an array of all logs matching filter with given id.", + "params": [ + { + "name": "Filter Identifier", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + ], + "result": { + "name": "Log objects", + "schema": { + "title": "Filter results", + "oneOf": [ + { + "title": "new block hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new transaction hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new logs", + "type": "array", + "items": { + "title": "log", + "type": "object", + "required": [ + "transactionHash" + ], + "properties": { + "removed": { + "title": "removed", + "type": "boolean" + }, + "logIndex": { + "title": "log index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionHash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "data": { + "title": "data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "topics": { + "title": "topics", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + } + ] + } + } + }, + { + "name": "eth_getLogs", + "summary": "Returns an array of all logs matching filter with given id.", + "params": [ + { + "name": "Filter", + "schema": { + "title": "filter", + "type": "object", + "properties": { + "fromBlock": { + "title": "from block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "toBlock": { + "title": "to block", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "Address(es)", + "oneOf": [ + { + "title": "Address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + { + "title": "Addresses", + "type": "array", + "items": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + } + ] + }, + "topics": { + "title": "Topics", + "type": "array", + "items": { + "title": "Filter Topic List Entry", + "oneOf": [ + { + "title": "Any Topic Match", + "type": "null" + }, + { + "title": "Single Topic Match", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + { + "title": "Multiple Topic Match", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ] + } + } + } + } + } + ], + "result": { + "name": "Log objects", + "schema": { + "title": "Filter results", + "oneOf": [ + { + "title": "new block hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new transaction hashes", + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "title": "new logs", + "type": "array", + "items": { + "title": "log", + "type": "object", + "required": [ + "transactionHash" + ], + "properties": { + "removed": { + "title": "removed", + "type": "boolean" + }, + "logIndex": { + "title": "log index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionHash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "data": { + "title": "data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "topics": { + "title": "topics", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + } + ] + } + } + }, + { + "name": "eth_mining", + "summary": "Returns whether the client is actively mining new blocks.", + "params": [], + "result": { + "name": "Mining status", + "schema": { + "title": "miningStatus", + "type": "boolean" + } + } + }, + { + "name": "eth_hashrate", + "summary": "Returns the number of hashes per second that the node is mining with.", + "params": [], + "result": { + "name": "Mining status", + "schema": { + "title": "Hashrate", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getWork", + "summary": "Returns the hash of the current block, the seedHash, and the boundary condition to be met (“target”).", + "params": [], + "result": { + "name": "Current work", + "schema": { + "type": "array", + "items": [ + { + "title": "Proof-of-work hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + { + "title": "seed hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + { + "title": "difficulty", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + }, + { + "name": "eth_submitWork", + "summary": "Used for submitting a proof-of-work solution.", + "params": [ + { + "name": "nonce", + "required": true, + "schema": { + "title": "8 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{16}$" + } + }, + { + "name": "hash", + "required": true, + "schema": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "name": "digest", + "required": true, + "schema": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Success", + "schema": { + "type": "boolean" + } + } + }, + { + "name": "eth_submitHashrate", + "summary": "Used for submitting mining hashrate.", + "params": [ + { + "name": "Hashrate", + "required": true, + "schema": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "name": "ID", + "required": true, + "schema": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Success", + "schema": { + "type": "boolean" + } + } + }, + { + "name": "eth_sign", + "summary": "Returns an EIP-191 signature over the provided data.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "Message", + "required": true, + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + ], + "result": { + "name": "Signature", + "schema": { + "title": "65 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{65}$" + } + } + }, + { + "name": "eth_signTransaction", + "summary": "Returns an RLP encoded transaction signed by the specified account.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "type": "object", + "title": "Transaction object generic to all types", + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + } + } + } + } + ], + "result": { + "name": "Encoded transaction", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "eth_getBalance", + "summary": "Returns the balance of the account of given address.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Balance", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getStorageAt", + "summary": "Returns the value from a storage position at a given address.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "Storage slot", + "required": true, + "schema": { + "title": "hex encoded 256 bit unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]{0,31})|0$" + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Value", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "eth_getTransactionCount", + "summary": "Returns the number of transactions sent from an address.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Transaction count", + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "name": "eth_getCode", + "summary": "Returns code at a given address.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "Block", + "required": false, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Bytecode", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "eth_getProof", + "summary": "Returns the merkle proof for a given account and optionally some storage keys.", + "params": [ + { + "name": "Address", + "required": true, + "schema": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + } + }, + { + "name": "StorageKeys", + "required": true, + "schema": { + "title": "Storage keys", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{0,64}$" + } + } + }, + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number, tag, or block hash", + "anyOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + }, + { + "title": "Block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + ] + } + } + ], + "result": { + "name": "Account", + "schema": { + "title": "Account proof", + "type": "object", + "required": [ + "address", + "accountProof", + "balance", + "codeHash", + "nonce", + "storageHash", + "storageProof" + ], + "properties": { + "address": { + "title": "address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "accountProof": { + "title": "accountProof", + "type": "array", + "items": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + }, + "balance": { + "title": "balance", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]{0,31})|0$" + }, + "codeHash": { + "title": "codeHash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]{0,15})|0$" + }, + "storageHash": { + "title": "storageHash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "storageProof": { + "title": "Storage proofs", + "type": "array", + "items": { + "title": "Storage proof", + "type": "object", + "required": [ + "key", + "value", + "proof" + ], + "properties": { + "key": { + "title": "key", + "type": "string", + "pattern": "^0x[0-9a-f]{0,64}$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]{0,31})|0$" + }, + "proof": { + "title": "proof", + "type": "array", + "items": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + } + } + } + } + } + } + }, + { + "name": "eth_sendTransaction", + "summary": "Signs and submits a transaction.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "type": "object", + "title": "Transaction object generic to all types", + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + } + } + } + } + ], + "result": { + "name": "Transaction hash", + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + }, + { + "name": "eth_sendRawTransaction", + "summary": "Submits a raw transaction.", + "params": [ + { + "name": "Transaction", + "required": true, + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + ], + "result": { + "name": "Transaction hash", + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + }, + { + "name": "eth_getTransactionByHash", + "summary": "Returns the information about a transaction requested by transaction hash.", + "params": [ + { + "name": "Transaction hash", + "required": true, + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Transaction information", + "schema": { + "type": "object", + "title": "Transaction information", + "required": [ + "blockHash", + "blockNumber", + "from", + "hash", + "transactionIndex" + ], + "oneOf": [ + { + "title": "Signed 1559 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "input", + "maxFeePerGas", + "maxPriorityFeePerGas", + "nonce", + "r", + "s", + "type", + "value", + "v" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed 2930 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "value", + "v" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed Legacy Transaction", + "type": "object", + "required": [ + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "v", + "value" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + ], + "properties": { + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "hash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + } + }, + { + "name": "eth_getTransactionByBlockHashAndIndex", + "summary": "Returns information about a transaction by block hash and transaction index position.", + "params": [ + { + "name": "Block hash", + "required": true, + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + }, + { + "name": "Transaction index", + "required": true, + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + ], + "result": { + "name": "Transaction information", + "schema": { + "type": "object", + "title": "Transaction information", + "required": [ + "blockHash", + "blockNumber", + "from", + "hash", + "transactionIndex" + ], + "oneOf": [ + { + "title": "Signed 1559 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "input", + "maxFeePerGas", + "maxPriorityFeePerGas", + "nonce", + "r", + "s", + "type", + "value", + "v" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed 2930 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "value", + "v" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed Legacy Transaction", + "type": "object", + "required": [ + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "v", + "value" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + ], + "properties": { + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "hash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + } + }, + { + "name": "eth_getTransactionByBlockNumberAndIndex", + "summary": "Returns information about a transaction by block number and transaction index position.", + "params": [ + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + }, + { + "name": "Transaction index", + "required": true, + "schema": { + "title": "hex encoded unsigned integer", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + ], + "result": { + "name": "Transaction information", + "schema": { + "type": "object", + "title": "Transaction information", + "required": [ + "blockHash", + "blockNumber", + "from", + "hash", + "transactionIndex" + ], + "oneOf": [ + { + "title": "Signed 1559 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "input", + "maxFeePerGas", + "maxPriorityFeePerGas", + "nonce", + "r", + "s", + "type", + "value", + "v" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "maxPriorityFeePerGas": { + "title": "max priority fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Maximum fee per gas the sender is willing to pay to miners in wei" + }, + "maxFeePerGas": { + "title": "max fee per gas", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The maximum total fee per gas the sender is willing to pay (includes the network / base fee and miner / priority fee) in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed 2930 Transaction", + "type": "object", + "required": [ + "accessList", + "chainId", + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "value", + "v" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "accessList": { + "title": "accessList", + "type": "array", + "description": "EIP-2930 access list", + "items": { + "title": "Access list entry", + "type": "object", + "properties": { + "address": { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "storageKeys": { + "type": "array", + "items": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The parity (0 for even, 1 for odd) of the y-value of the secp256k1 signature." + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + }, + { + "title": "Signed Legacy Transaction", + "type": "object", + "required": [ + "gas", + "gasPrice", + "input", + "nonce", + "r", + "s", + "type", + "v", + "value" + ], + "properties": { + "type": { + "title": "type", + "type": "string", + "pattern": "^0x([0-9,a-f,A-F]?){1,2}$" + }, + "nonce": { + "title": "nonce", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "to": { + "title": "to address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "gas": { + "title": "gas limit", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "value": { + "title": "value", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "input": { + "title": "input data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "gasPrice": { + "title": "gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The gas price willing to be paid by the sender in wei" + }, + "chainId": { + "title": "chainId", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Chain ID that this transaction is valid on." + }, + "v": { + "title": "v", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "r": { + "title": "r", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "s": { + "title": "s", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + ], + "properties": { + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "from": { + "title": "from address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "hash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + } + } + } + } + }, + { + "name": "eth_getTransactionReceipt", + "summary": "Returns the receipt of a transaction by transaction hash.", + "params": [ + { + "name": "Transaction hash", + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "Receipt Information", + "schema": { + "type": "object", + "title": "Receipt info", + "required": [ + "blockHash", + "blockNumber", + "from", + "cumulativeGasUsed", + "gasUsed", + "logs", + "logsBloom", + "transactionHash", + "transactionIndex", + "effectiveGasPrice" + ], + "properties": { + "transactionHash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "from": { + "title": "from", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "to": { + "title": "to", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$", + "description": "Address of the receiver or null in a contract creation transaction." + }, + "cumulativeGasUsed": { + "title": "cumulative gas used", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The sum of gas used by this transaction and all preceding transactions in the same block." + }, + "gasUsed": { + "title": "gas used", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The amount of gas used for this specific transaction alone." + }, + "contractAddress": { + "title": "contract address", + "description": "The contract address created, if the transaction was a contract creation, otherwise null.", + "oneOf": [ + { + "title": "hex encoded address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + { + "name": null, + "type": "null" + } + ] + }, + "logs": { + "title": "logs", + "type": "array", + "items": { + "title": "log", + "type": "object", + "required": [ + "transactionHash" + ], + "properties": { + "removed": { + "title": "removed", + "type": "boolean" + }, + "logIndex": { + "title": "log index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionIndex": { + "title": "transaction index", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "transactionHash": { + "title": "transaction hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockHash": { + "title": "block hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "blockNumber": { + "title": "block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + "address": { + "title": "address", + "type": "string", + "pattern": "^0x[0-9,a-f,A-F]{40}$" + }, + "data": { + "title": "data", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "topics": { + "title": "topics", + "type": "array", + "items": { + "title": "32 hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + } + } + }, + "logsBloom": { + "title": "logs bloom", + "type": "string", + "pattern": "^0x[0-9a-f]{512}$" + }, + "root": { + "title": "state root", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$", + "description": "The post-transaction state root. Only specified for transactions included before the Byzantium upgrade." + }, + "status": { + "title": "status", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "Either 1 (success) or 0 (failure). Only specified for transactions included after the Byzantium upgrade." + }, + "effectiveGasPrice": { + "title": "effective gas price", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$", + "description": "The actual value per gas deducted from the senders account. Before EIP-1559, this is equal to the transaction's gas price. After, it is equal to baseFeePerGas + min(maxFeePerGas - baseFeePerGas, maxPriorityFeePerGas)." + } + } + } + } + }, + { + "name": "debug_getRawHeader", + "summary": "Returns an RLP-encoded header.", + "params": [ + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Header RLP", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "debug_getRawBlock", + "summary": "Returns an RLP-encoded block.", + "params": [ + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Block RLP", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "debug_getRawTransaction", + "summary": "Returns an array of EIP-2718 binary-encoded transactions.", + "params": [ + { + "name": "Transaction hash", + "required": true, + "schema": { + "title": "32 byte hex value", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + } + } + ], + "result": { + "name": "EIP-2718 binary-encoded transaction", + "schema": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + }, + { + "name": "debug_getRawReceipts", + "summary": "Returns an array of EIP-2718 binary-encoded receipts.", + "params": [ + { + "name": "Block", + "required": true, + "schema": { + "title": "Block number or tag", + "oneOf": [ + { + "title": "Block number", + "type": "string", + "pattern": "^0x([1-9a-f]+[0-9a-f]*|0)$" + }, + { + "title": "Block tag", + "type": "string", + "enum": [ + "earliest", + "finalized", + "safe", + "latest", + "pending" + ], + "description": "`earliest`: The lowest numbered block the client has available; `finalized`: The most recent crypto-economically secure block, cannot be re-orged outside of manual intervention driven by community coordination; `safe`: The most recent block that is safe from re-orgs under honest majority and certain synchronicity assumptions; `latest`: The most recent block in the canonical chain observed by the client, this block may be re-orged out of the canonical chain even under healthy/normal conditions; `pending`: A sample next block built by the client on top of `latest` and containing the set of transactions usually taken from local mempool. Before the merge transition is finalized, any call querying for `finalized` or `safe` block MUST be responded to with `-39001: Unknown block` error" + } + ] + } + } + ], + "result": { + "name": "Receipts", + "schema": { + "title": "Receipt array", + "type": "array", + "items": { + "title": "hex encoded bytes", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + } + }, + { + "name": "debug_getBadBlocks", + "summary": "Returns an array of recent bad blocks that the client has seen on the network.", + "params": [], + "result": { + "name": "Blocks", + "schema": { + "title": "Bad block array", + "type": "array", + "items": { + "title": "Bad block", + "type": "object", + "required": [ + "block", + "hash", + "rlp" + ], + "properties": { + "block": { + "title": "Block", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + }, + "hash": { + "title": "Hash", + "type": "string", + "pattern": "^0x[0-9a-f]{64}$" + }, + "rlp": { + "title": "RLP", + "type": "string", + "pattern": "^0x[0-9a-f]*$" + } + } + } + } + } + } + ], + "components": {} +} diff --git a/itests/wdpost_dispute_test.go b/itests/wdpost_dispute_test.go index eedb3e8f6..c4512874a 100644 --- a/itests/wdpost_dispute_test.go +++ b/itests/wdpost_dispute_test.go @@ -113,7 +113,7 @@ func TestWindowPostDispute(t *testing.T) { //stm: @CHAIN_STATE_MINER_CALCULATE_DEADLINE_001 di, err = client.StateMinerProvingDeadline(ctx, evilMinerAddr, types.EmptyTSK) require.NoError(t, err) - if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch-di.PeriodStart > 1 { + if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch-di.Open > 1 { break } build.Clock.Sleep(blocktime) @@ -217,7 +217,8 @@ func TestWindowPostDispute(t *testing.T) { //stm: @CHAIN_STATE_MINER_CALCULATE_DEADLINE_001 di, err = client.StateMinerProvingDeadline(ctx, evilMinerAddr, types.EmptyTSK) require.NoError(t, err) - if di.Index == evilSectorLoc.Deadline { + + if di.Index == evilSectorLoc.Deadline && di.CurrentEpoch-di.Open > 1 { break } build.Clock.Sleep(blocktime) diff --git a/lib/must/must.go b/lib/must/must.go new file mode 100644 index 000000000..e072b4e04 --- /dev/null +++ b/lib/must/must.go @@ -0,0 +1,9 @@ +package must + +func One[R any](r R, err error) R { + if err != nil { + panic(err) + } + + return r +} diff --git a/node/builder.go b/node/builder.go index ddba82112..88b67a96d 100644 --- a/node/builder.go +++ b/node/builder.go @@ -126,6 +126,8 @@ const ( SetApiEndpointKey + StoreEventsKey + _nInvokes // keep this last ) diff --git a/node/builder_chain.go b/node/builder_chain.go index 545c061b2..4e5bad61d 100644 --- a/node/builder_chain.go +++ b/node/builder_chain.go @@ -219,6 +219,11 @@ func ConfigFullNode(c interface{}) Option { Override(SetupFallbackBlockstoresKey, modules.InitFallbackBlockstores), ), + // If the Eth JSON-RPC is enabled, enable storing events at the ChainStore. + // This is the case even if real-time and historic filtering are disabled, + // as it enables us to serve logs in eth_getTransactionReceipt. + If(cfg.Fevm.EnableEthRPC, Override(StoreEventsKey, modules.EnableStoringEvents)), + Override(new(dtypes.ClientImportMgr), modules.ClientImportMgr), Override(new(dtypes.ClientBlockstore), modules.ClientBlockstore), @@ -258,11 +263,18 @@ func ConfigFullNode(c interface{}) Option { // Actor event filtering support Override(new(events.EventAPI), From(new(modules.EventAPI))), - // in lite-mode Eth event api is provided by gateway - ApplyIf(isFullNode, Override(new(full.EthEventAPI), modules.EthEventAPI(cfg.Fevm))), - If(cfg.Fevm.EnableEthRPC, Override(new(full.EthModuleAPI), modules.EthModuleAPI(cfg.Fevm))), - If(!cfg.Fevm.EnableEthRPC, Override(new(full.EthModuleAPI), &full.EthModuleDummy{})), + // in lite-mode Eth api is provided by gateway + ApplyIf(isFullNode, + If(cfg.Fevm.EnableEthRPC, + Override(new(full.EthModuleAPI), modules.EthModuleAPI(cfg.Fevm)), + Override(new(full.EthEventAPI), modules.EthEventAPI(cfg.Fevm)), + ), + If(!cfg.Fevm.EnableEthRPC, + Override(new(full.EthModuleAPI), &full.EthModuleDummy{}), + Override(new(full.EthEventAPI), &full.EthModuleDummy{}), + ), + ), ) } diff --git a/node/impl/full/chain.go b/node/impl/full/chain.go index 8448a542e..acbc056af 100644 --- a/node/impl/full/chain.go +++ b/node/impl/full/chain.go @@ -665,18 +665,41 @@ func (a *ChainAPI) ChainGetEvents(ctx context.Context, root cid.Cid) ([]types.Ev if err != nil { return nil, xerrors.Errorf("load events amt: %w", err) } + if evtArr.Len() == 0 { + return nil, nil + } + + // Hyperspace nv20: we peek into the events array to figure out if these + // are legacy events or not. + isLegacy, err := isLegacyEvents(ctx, evtArr) + if err != nil { + return nil, xerrors.Errorf("failed to determine if events AMT was legacy: %w", err) + } ret := make([]types.Event, 0, evtArr.Len()) - var evt types.Event err = evtArr.ForEach(ctx, func(u uint64, deferred *cbg.Deferred) error { if u > math.MaxInt { return xerrors.Errorf("too many events") } - if err := evt.UnmarshalCBOR(bytes.NewReader(deferred.Raw)); err != nil { - return err - } - ret = append(ret, evt) + r := bytes.NewReader(deferred.Raw) + if isLegacy { + var evt types.LegacyEvent + if err := evt.UnmarshalCBOR(r); err != nil { + return err + } + adapted, err := evt.Adapt() + if err != nil { + return err + } + ret = append(ret, adapted) + } else { + var evt types.Event + if err := evt.UnmarshalCBOR(r); err != nil { + return err + } + ret = append(ret, evt) + } return nil }) @@ -693,3 +716,44 @@ func (a *ChainAPI) ChainPrune(ctx context.Context, opts api.PruneOpts) error { return pruner.PruneChain(opts) } + +func isLegacyEvents(ctx context.Context, root *amt4.Root) (bool, error) { + var obj cbg.Deferred + if _, err := root.Get(ctx, 0, &obj); err != nil { + return false, xerrors.Errorf("failed to peek into events AMT: %w", err) + } + + r := cbg.NewCborReader(bytes.NewReader(obj.Raw)) + + // StampedEvent. + if typ, len, err := r.ReadHeader(); err != nil || typ != cbg.MajArray || len != 2 { + return false, xerrors.Errorf("expected cbor list with length 2 (stamped event); type: %d, size: %d, err: %s", typ, len, err) + } + + // ActorID + if typ, _, err := r.ReadHeader(); err != nil || typ != cbg.MajUnsignedInt { + return false, xerrors.Errorf("expected cbor unsigned int (actor id); err: %s", err) + } + + // Entries + if typ, len, err := r.ReadHeader(); err != nil || typ != cbg.MajArray { + return false, xerrors.Errorf("expected non-empty cbor list (entries); type: %d, size: %d, err: %s", typ, len, err) + } else if len == 0 { + // if we have no entries, it doesn't matter if we're new or legacy, so we'll assume we're new + return true, nil + } + + // First entry, finally + switch h, len, err := r.ReadHeader(); { + case err != nil: + return false, err + case h != cbg.MajArray: + return false, xerrors.Errorf("expected cbor major type %d when reading event; got: %d", cbg.MajArray, h) + case len == 3: + return true, nil + case len == 4: + return false, nil + default: + return false, xerrors.Errorf("unexpected event tuple length: %d", len) + } +} diff --git a/node/impl/full/dummy.go b/node/impl/full/dummy.go index 182772189..9abe00321 100644 --- a/node/impl/full/dummy.go +++ b/node/impl/full/dummy.go @@ -6,11 +6,13 @@ import ( "github.com/ipfs/go-cid" + "github.com/filecoin-project/go-jsonrpc" + "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/chain/types/ethtypes" ) -var ErrModuleDisabled = errors.New("module disabled, enable with Fevm.EnableEthRPC / LOTUS_FEVM_ENABLEETHPRC") +var ErrModuleDisabled = errors.New("module disabled, enable with Fevm.EnableEthRPC / LOTUS_FEVM_ENABLEETHRPC") type EthModuleDummy struct{} @@ -78,7 +80,7 @@ func (e *EthModuleDummy) EthGetBalance(ctx context.Context, address ethtypes.Eth return ethtypes.EthBigIntZero, ErrModuleDisabled } -func (e *EthModuleDummy) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) { +func (e *EthModuleDummy) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) { return ethtypes.EthFeeHistory{}, ErrModuleDisabled } @@ -122,4 +124,41 @@ func (e *EthModuleDummy) Web3ClientVersion(ctx context.Context) (string, error) return "", ErrModuleDisabled } +func (e *EthModuleDummy) EthGetLogs(ctx context.Context, filter *ethtypes.EthFilterSpec) (*ethtypes.EthFilterResult, error) { + return ðtypes.EthFilterResult{}, ErrModuleDisabled +} + +func (e *EthModuleDummy) EthGetFilterChanges(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) { + return ðtypes.EthFilterResult{}, ErrModuleDisabled +} + +func (e *EthModuleDummy) EthGetFilterLogs(ctx context.Context, id ethtypes.EthFilterID) (*ethtypes.EthFilterResult, error) { + return ðtypes.EthFilterResult{}, ErrModuleDisabled +} + +func (e *EthModuleDummy) EthNewFilter(ctx context.Context, filter *ethtypes.EthFilterSpec) (ethtypes.EthFilterID, error) { + return ethtypes.EthFilterID{}, ErrModuleDisabled +} + +func (e *EthModuleDummy) EthNewBlockFilter(ctx context.Context) (ethtypes.EthFilterID, error) { + return ethtypes.EthFilterID{}, ErrModuleDisabled +} + +func (e *EthModuleDummy) EthNewPendingTransactionFilter(ctx context.Context) (ethtypes.EthFilterID, error) { + return ethtypes.EthFilterID{}, ErrModuleDisabled +} + +func (e *EthModuleDummy) EthUninstallFilter(ctx context.Context, id ethtypes.EthFilterID) (bool, error) { + return false, ErrModuleDisabled +} + +func (e *EthModuleDummy) EthSubscribe(ctx context.Context, params jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) { + return ethtypes.EthSubscriptionID{}, ErrModuleDisabled +} + +func (e *EthModuleDummy) EthUnsubscribe(ctx context.Context, id ethtypes.EthSubscriptionID) (bool, error) { + return false, ErrModuleDisabled +} + var _ EthModuleAPI = &EthModuleDummy{} +var _ EthEventAPI = &EthModuleDummy{} diff --git a/node/impl/full/eth.go b/node/impl/full/eth.go index a9e5a1cb4..772f7090b 100644 --- a/node/impl/full/eth.go +++ b/node/impl/full/eth.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "sort" "strconv" "sync" "time" @@ -24,6 +25,8 @@ import ( "github.com/filecoin-project/go-state-types/builtin/v10/eam" "github.com/filecoin-project/go-state-types/builtin/v10/evm" "github.com/filecoin-project/go-state-types/crypto" + "github.com/filecoin-project/go-state-types/exitcode" + "github.com/filecoin-project/go-state-types/network" "github.com/filecoin-project/lotus/api" "github.com/filecoin-project/lotus/build" @@ -56,7 +59,7 @@ type EthModuleAPI interface { EthGetCode(ctx context.Context, address ethtypes.EthAddress, blkOpt string) (ethtypes.EthBytes, error) EthGetStorageAt(ctx context.Context, address ethtypes.EthAddress, position ethtypes.EthBytes, blkParam string) (ethtypes.EthBytes, error) EthGetBalance(ctx context.Context, address ethtypes.EthAddress, blkParam string) (ethtypes.EthBigInt, error) - EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlk string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) + EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) EthChainId(ctx context.Context) (ethtypes.EthUint64, error) NetVersion(ctx context.Context) (string, error) NetListening(ctx context.Context) (bool, error) @@ -84,6 +87,8 @@ type EthEventAPI interface { var ( _ EthModuleAPI = *new(api.FullNode) _ EthEventAPI = *new(api.FullNode) + + _ EthModuleAPI = *new(api.Gateway) ) // EthModule provides the default implementation of the standard Ethereum JSON-RPC API. @@ -423,13 +428,32 @@ func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress, return nil, xerrors.Errorf("cannot get Filecoin address: %w", err) } - // use the system actor as the caller - from, err := address.NewIDAddress(0) + ts, err := a.parseBlkParam(ctx, blkParam) if err != nil { - return nil, fmt.Errorf("failed to construct system sender address: %w", err) + return nil, xerrors.Errorf("cannot parse block param: %s", blkParam) } + + // StateManager.Call will panic if there is no parent + if ts.Height() == 0 { + return nil, xerrors.Errorf("block param must not specify genesis block") + } + + actor, err := a.StateManager.LoadActor(ctx, to, ts) + if err != nil { + if xerrors.Is(err, types.ErrActorNotFound) { + return nil, nil + } + return nil, xerrors.Errorf("failed to lookup contract %s: %w", ethAddr, err) + } + + // Not a contract. We could try to distinguish between accounts and "native" contracts here, + // but it's not worth it. + if !builtinactors.IsEvmActor(actor.Code) { + return nil, nil + } + msg := &types.Message{ - From: from, + From: builtinactors.SystemActorAddr, To: to, Value: big.Zero(), Method: builtintypes.MethodsEVM.GetBytecode, @@ -439,11 +463,6 @@ func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress, GasPremium: big.Zero(), } - ts, err := a.parseBlkParam(ctx, blkParam) - if err != nil { - return nil, xerrors.Errorf("cannot parse block param: %s", blkParam) - } - // Try calling until we find a height with no migration. var res *api.InvocResult for { @@ -458,9 +477,7 @@ func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress, } if err != nil { - // if the call resulted in error, this is not an EVM smart contract; - // return no bytecode. - return nil, nil + return nil, xerrors.Errorf("failed to call GetBytecode: %w", err) } if res.MsgRct == nil { @@ -468,15 +485,20 @@ func (a *EthModule) EthGetCode(ctx context.Context, ethAddr ethtypes.EthAddress, } if res.MsgRct.ExitCode.IsError() { - return nil, xerrors.Errorf("message execution failed: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error) + return nil, xerrors.Errorf("GetBytecode failed: %s", res.Error) } - var bytecodeCid cbg.CborCid - if err := bytecodeCid.UnmarshalCBOR(bytes.NewReader(res.MsgRct.Return)); err != nil { + var getBytecodeReturn evm.GetBytecodeReturn + if err := getBytecodeReturn.UnmarshalCBOR(bytes.NewReader(res.MsgRct.Return)); err != nil { return nil, fmt.Errorf("failed to decode EVM bytecode CID: %w", err) } - blk, err := a.Chain.StateBlockstore().Get(ctx, cid.Cid(bytecodeCid)) + // The contract has selfdestructed, so the code is "empty". + if getBytecodeReturn.Cid == nil { + return nil, nil + } + + blk, err := a.Chain.StateBlockstore().Get(ctx, *getBytecodeReturn.Cid) if err != nil { return nil, fmt.Errorf("failed to get EVM bytecode: %w", err) } @@ -573,49 +595,78 @@ func (a *EthModule) EthChainId(ctx context.Context) (ethtypes.EthUint64, error) return ethtypes.EthUint64(build.Eip155ChainId), nil } -func (a *EthModule) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint64, newestBlkNum string, rewardPercentiles []float64) (ethtypes.EthFeeHistory, error) { - if blkCount > 1024 { +func (a *EthModule) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthFeeHistory, error) { + params, err := jsonrpc.DecodeParams[ethtypes.EthFeeHistoryParams](p) + if err != nil { + return ethtypes.EthFeeHistory{}, xerrors.Errorf("decoding params: %w", err) + } + if params.BlkCount > 1024 { return ethtypes.EthFeeHistory{}, fmt.Errorf("block count should be smaller than 1024") } - - newestBlkHeight := uint64(a.Chain.GetHeaviestTipSet().Height()) - - // TODO https://github.com/filecoin-project/ref-fvm/issues/1016 - var blkNum ethtypes.EthUint64 - err := blkNum.UnmarshalJSON([]byte(`"` + newestBlkNum + `"`)) - if err == nil && uint64(blkNum) < newestBlkHeight { - newestBlkHeight = uint64(blkNum) + rewardPercentiles := make([]float64, 0) + if params.RewardPercentiles != nil { + rewardPercentiles = append(rewardPercentiles, *params.RewardPercentiles...) + } + for i, rp := range rewardPercentiles { + if rp < 0 || rp > 100 { + return ethtypes.EthFeeHistory{}, fmt.Errorf("invalid reward percentile: %f should be between 0 and 100", rp) + } + if i > 0 && rp < rewardPercentiles[i-1] { + return ethtypes.EthFeeHistory{}, fmt.Errorf("invalid reward percentile: %f should be larger than %f", rp, rewardPercentiles[i-1]) + } } - // Deal with the case that the chain is shorter than the number of - // requested blocks. - oldestBlkHeight := uint64(1) - if uint64(blkCount) <= newestBlkHeight { - oldestBlkHeight = newestBlkHeight - uint64(blkCount) + 1 - } - - ts, err := a.Chain.GetTipsetByHeight(ctx, abi.ChainEpoch(newestBlkHeight), nil, false) + ts, err := a.parseBlkParam(ctx, params.NewestBlkNum) if err != nil { - return ethtypes.EthFeeHistory{}, fmt.Errorf("cannot load find block height: %v", newestBlkHeight) + return ethtypes.EthFeeHistory{}, fmt.Errorf("bad block parameter %s: %s", params.NewestBlkNum, err) } - // FIXME: baseFeePerGas should include the next block after the newest of the returned range, because this - // can be inferred from the newest block. we use the newest block's baseFeePerGas for now but need to fix it - // In other words, due to deferred execution, we might not be returning the most useful value here for the client. + // Deal with the case that the chain is shorter than the number of requested blocks. + oldestBlkHeight := uint64(1) + if abi.ChainEpoch(params.BlkCount) <= ts.Height() { + oldestBlkHeight = uint64(ts.Height()) - uint64(params.BlkCount) + 1 + } + + // NOTE: baseFeePerGas should include the next block after the newest of the returned range, + // because the next base fee can be inferred from the messages in the newest block. + // However, this is NOT the case in Filecoin due to deferred execution, so the best + // we can do is duplicate the last value. baseFeeArray := []ethtypes.EthBigInt{ethtypes.EthBigInt(ts.Blocks()[0].ParentBaseFee)} gasUsedRatioArray := []float64{} + rewardsArray := make([][]ethtypes.EthBigInt, 0) 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.StateAPI) + msgs, err := a.Chain.MessagesForTipset(ctx, ts) if err != nil { - return ethtypes.EthFeeHistory{}, fmt.Errorf("cannot create eth block: %v", err) + return ethtypes.EthFeeHistory{}, xerrors.Errorf("error loading messages for tipset: %v: %w", ts, err) } - // both arrays should be reversed at the end + txGasRewards := gasRewardSorter{} + for txIdx, msg := range msgs { + msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, msg.Cid(), api.LookbackNoLimit, false) + if err != nil || msgLookup == nil { + return ethtypes.EthFeeHistory{}, nil + } + + tx, err := newEthTxFromMessageLookup(ctx, msgLookup, txIdx, a.Chain, a.StateAPI) + if err != nil { + return ethtypes.EthFeeHistory{}, nil + } + + txGasRewards = append(txGasRewards, gasRewardTuple{ + reward: tx.Reward(ts.Blocks()[0].ParentBaseFee), + gas: uint64(msgLookup.Receipt.GasUsed), + }) + } + + rewards, totalGasUsed := calculateRewardsAndGasUsed(rewardPercentiles, txGasRewards) + + // arrays should be reversed at the end baseFeeArray = append(baseFeeArray, ethtypes.EthBigInt(ts.Blocks()[0].ParentBaseFee)) - gasUsedRatioArray = append(gasUsedRatioArray, float64(block.GasUsed)/float64(build.BlockGasLimit)) + gasUsedRatioArray = append(gasUsedRatioArray, float64(totalGasUsed)/float64(build.BlockGasLimit)) + rewardsArray = append(rewardsArray, rewards) parentTsKey := ts.Parents() ts, err = a.Chain.LoadTipSet(ctx, parentTsKey) @@ -625,19 +676,25 @@ func (a *EthModule) EthFeeHistory(ctx context.Context, blkCount ethtypes.EthUint } // Reverse the arrays; we collected them newest to oldest; the client expects oldest to newest. - for i, j := 0, len(baseFeeArray)-1; i < j; i, j = i+1, j-1 { baseFeeArray[i], baseFeeArray[j] = baseFeeArray[j], baseFeeArray[i] } for i, j := 0, len(gasUsedRatioArray)-1; i < j; i, j = i+1, j-1 { gasUsedRatioArray[i], gasUsedRatioArray[j] = gasUsedRatioArray[j], gasUsedRatioArray[i] } + for i, j := 0, len(rewardsArray)-1; i < j; i, j = i+1, j-1 { + rewardsArray[i], rewardsArray[j] = rewardsArray[j], rewardsArray[i] + } - return ethtypes.EthFeeHistory{ - OldestBlock: oldestBlkHeight, + ret := ethtypes.EthFeeHistory{ + OldestBlock: ethtypes.EthUint64(oldestBlkHeight), BaseFeePerGas: baseFeeArray, GasUsedRatio: gasUsedRatioArray, - }, nil + } + if params.RewardPercentiles != nil { + ret.Reward = &rewardsArray + } + return ret, nil } func (a *EthModule) NetVersion(ctx context.Context) (string, error) { @@ -693,6 +750,13 @@ func (a *EthModule) EthSendRawTransaction(ctx context.Context, rawTx ethtypes.Et return ethtypes.EmptyEthHash, err } + // Prior to nv20, delegated signature messages with no parameters carried MethodSend. + // Reset the value so the signature will roundtrip. + nv := a.StateManager.GetNetworkVersion(ctx, a.Chain.GetHeaviestTipSet().Height()) + if nv < network.Version20 && smsg.Message.Method == builtintypes.MethodsEVM.InvokeContract && len(smsg.Message.Params) == 0 { + smsg.Message.Method = builtintypes.MethodSend + } + _, err = a.MpoolAPI.MpoolPush(ctx, smsg) if err != nil { return ethtypes.EmptyEthHash, err @@ -727,18 +791,20 @@ func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.Et } var params []byte + if len(tx.Data) > 0 { + initcode := abi.CborBytes(tx.Data) + params2, err := actors.SerializeParams(&initcode) + if err != nil { + return nil, fmt.Errorf("failed to serialize params: %w", err) + } + params = params2 + } + var to address.Address var method abi.MethodNum if tx.To == nil { // this is a contract creation to = builtintypes.EthereumAddressManagerActorAddr - - initcode := abi.CborBytes(tx.Data) - params2, err := actors.SerializeParams(&initcode) - if err != nil { - return nil, fmt.Errorf("failed to serialize Create params: %w", err) - } - params = params2 method = builtintypes.MethodsEAM.CreateExternal } else { addr, err := tx.To.ToFilecoinAddress() @@ -746,15 +812,11 @@ func (a *EthModule) ethCallToFilecoinMessage(ctx context.Context, tx ethtypes.Et return nil, xerrors.Errorf("cannot get Filecoin address: %w", err) } to = addr + method = builtintypes.MethodsEVM.InvokeContract - if len(tx.Data) > 0 { - var buf bytes.Buffer - if err := cbg.WriteByteArray(&buf, tx.Data); err != nil { - return nil, fmt.Errorf("failed to encode tx input into a cbor byte-string") - } - params = buf.Bytes() - method = builtintypes.MethodsEVM.InvokeContract - } else { + // < nv20 (Hyperspace): reset method to MethodSend if no params. + nv := a.StateManager.GetNetworkVersion(ctx, a.Chain.GetHeaviestTipSet().Height()) + if nv < network.Version20 && len(tx.Data) == 0 { method = builtintypes.MethodSend } } @@ -807,12 +869,137 @@ func (a *EthModule) EthEstimateGas(ctx context.Context, tx ethtypes.EthCall) (et // gas estimation actually run. msg.GasLimit = 0 - msg, err = a.GasAPI.GasEstimateMessageGas(ctx, msg, nil, types.EmptyTSK) + ts := a.Chain.GetHeaviestTipSet() + msg, err = a.GasAPI.GasEstimateMessageGas(ctx, msg, nil, ts.Key()) if err != nil { - return ethtypes.EthUint64(0), err + return ethtypes.EthUint64(0), xerrors.Errorf("failed to estimate gas: %w", err) } - return ethtypes.EthUint64(msg.GasLimit), nil + expectedGas, err := ethGasSearch(ctx, a.Chain, a.Stmgr, a.Mpool, msg, ts) + if err != nil { + log.Errorw("expected gas", "err", err) + } + + return ethtypes.EthUint64(expectedGas), nil +} + +// gasSearch does an exponential search to find a gas value to execute the +// message with. It first finds a high gas limit that allows the message to execute +// by doubling the previous gas limit until it succeeds then does a binary +// search till it gets within a range of 1% +func gasSearch( + ctx context.Context, + smgr *stmgr.StateManager, + msgIn *types.Message, + priorMsgs []types.ChainMsg, + ts *types.TipSet, +) (int64, error) { + msg := *msgIn + + high := msg.GasLimit + low := msg.GasLimit + + canSucceed := func(limit int64) (bool, error) { + msg.GasLimit = limit + + res, err := smgr.CallWithGas(ctx, &msg, priorMsgs, ts) + if err != nil { + return false, xerrors.Errorf("CallWithGas failed: %w", err) + } + + if res.MsgRct.ExitCode.IsSuccess() { + return true, nil + } + + return false, nil + } + + for { + ok, err := canSucceed(high) + if err != nil { + return -1, xerrors.Errorf("searching for high gas limit failed: %w", err) + } + if ok { + break + } + + low = high + high = high * 2 + + if high > build.BlockGasLimit { + high = build.BlockGasLimit + break + } + } + + checkThreshold := high / 100 + for (high - low) > checkThreshold { + median := (low + high) / 2 + ok, err := canSucceed(median) + if err != nil { + return -1, xerrors.Errorf("searching for optimal gas limit failed: %w", err) + } + + if ok { + high = median + } else { + low = median + } + + checkThreshold = median / 100 + } + + return high, nil +} + +func traceContainsExitCode(et types.ExecutionTrace, ex exitcode.ExitCode) bool { + if et.MsgRct.ExitCode == ex { + return true + } + + for _, et := range et.Subcalls { + if traceContainsExitCode(et, ex) { + return true + } + } + + return false +} + +// ethGasSearch executes a message for gas estimation using the previously estimated gas. +// If the message fails due to an out of gas error then a gas search is performed. +// See gasSearch. +func ethGasSearch( + ctx context.Context, + cstore *store.ChainStore, + smgr *stmgr.StateManager, + mpool *messagepool.MessagePool, + msgIn *types.Message, + ts *types.TipSet, +) (int64, error) { + msg := *msgIn + currTs := ts + + res, priorMsgs, ts, err := gasEstimateCallWithGas(ctx, cstore, smgr, mpool, &msg, currTs) + if err != nil { + return -1, xerrors.Errorf("gas estimation failed: %w", err) + } + + if res.MsgRct.ExitCode.IsSuccess() { + return msg.GasLimit, nil + } + + if traceContainsExitCode(res.ExecutionTrace, exitcode.SysErrOutOfGas) { + ret, err := gasSearch(ctx, smgr, &msg, priorMsgs, ts) + if err != nil { + return -1, xerrors.Errorf("gas estimation search failed: %w", err) + } + + ret = int64(float64(ret) * mpool.GetConfig().GasLimitOverestimation) + return ret, nil + } + + return -1, xerrors.Errorf("message execution failed: exit %s, reason: %s", res.MsgRct.ExitCode, res.Error) } func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam string) (ethtypes.EthBytes, error) { @@ -834,7 +1021,6 @@ func (a *EthModule) EthCall(ctx context.Context, tx ethtypes.EthCall, blkParam s if msg.To == builtintypes.EthereumAddressManagerActorAddr { // As far as I can tell, the Eth API always returns empty on contract deployment return ethtypes.EthBytes{}, nil - } else if len(invokeResult.MsgRct.Return) > 0 { return cbg.ReadByteArray(bytes.NewReader(invokeResult.MsgRct.Return), uint64(len(invokeResult.MsgRct.Return))) } @@ -1099,8 +1285,9 @@ func (e *EthEvent) uninstallFilter(ctx context.Context, f filter.Filter) error { } const ( - EthSubscribeEventTypeHeads = "newHeads" - EthSubscribeEventTypeLogs = "logs" + EthSubscribeEventTypeHeads = "newHeads" + EthSubscribeEventTypeLogs = "logs" + EthSubscribeEventTypePendingTransactions = "newPendingTransactions" ) func (e *EthEvent) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethtypes.EthSubscriptionID, error) { @@ -1145,12 +1332,32 @@ func (e *EthEvent) EthSubscribe(ctx context.Context, p jsonrpc.RawParams) (ethty } } - f, err := e.EventFilterManager.Install(ctx, -1, -1, cid.Undef, []address.Address{}, keys) + var addresses []address.Address + if params.Params != nil { + for _, ea := range params.Params.Address { + a, err := ea.ToFilecoinAddress() + if err != nil { + return ethtypes.EthSubscriptionID{}, xerrors.Errorf("invalid address %x", ea) + } + addresses = append(addresses, a) + } + } + + f, err := e.EventFilterManager.Install(ctx, -1, -1, cid.Undef, addresses, keys) if err != nil { // clean up any previous filters added and stop the sub _, _ = e.EthUnsubscribe(ctx, sub.id) return ethtypes.EthSubscriptionID{}, err } + sub.addFilter(ctx, f) + case EthSubscribeEventTypePendingTransactions: + f, err := e.MemPoolFilterManager.Install(ctx) + if err != nil { + // clean up any previous filters added and stop the sub + _, _ = e.EthUnsubscribe(ctx, sub.id) + return ethtypes.EthSubscriptionID{}, err + } + sub.addFilter(ctx, f) default: return ethtypes.EthSubscriptionID{}, xerrors.Errorf("unsupported event type: %s", params.EventType) @@ -1215,6 +1422,67 @@ type filterTipSetCollector interface { TakeCollectedTipSets(context.Context) []types.TipSetKey } +func ethLogFromEvent(entries []types.EventEntry) (data []byte, topics []ethtypes.EthHash, ok bool) { + var ( + topicsFound [4]bool + topicsFoundCount int + dataFound bool + ) + for _, entry := range entries { + // Drop events with non-raw topics to avoid mistakes. + if entry.Codec != cid.Raw { + log.Warnw("did not expect an event entry with a non-raw codec", "codec", entry.Codec, "key", entry.Key) + return nil, nil, false + } + // Check if the key is t1..t4 + if len(entry.Key) == 2 && "t1" <= entry.Key && entry.Key <= "t4" { + // '1' - '1' == 0, etc. + idx := int(entry.Key[1] - '1') + + // Drop events with mis-sized topics. + if len(entry.Value) != 32 { + log.Warnw("got an EVM event topic with an invalid size", "key", entry.Key, "size", len(entry.Value)) + return nil, nil, false + } + + // Drop events with duplicate topics. + if topicsFound[idx] { + log.Warnw("got a duplicate EVM event topic", "key", entry.Key) + return nil, nil, false + } + topicsFound[idx] = true + topicsFoundCount++ + + // Extend the topics array + for len(topics) <= idx { + topics = append(topics, ethtypes.EthHash{}) + } + copy(topics[idx][:], entry.Value) + } else if entry.Key == "d" { + // Drop events with duplicate data fields. + if dataFound { + log.Warnw("got duplicate EVM event data") + return nil, nil, false + } + + dataFound = true + data = entry.Value + } else { + // Skip entries we don't understand (makes it easier to extend things). + // But we warn for now because we don't expect them. + log.Warnw("unexpected event entry", "key", entry.Key) + } + + } + + // Drop events with skipped topics. + if len(topics) != topicsFoundCount { + log.Warnw("EVM event topic length mismatch", "expected", len(topics), "actual", topicsFoundCount) + return nil, nil, false + } + return data, topics, true +} + func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*ethtypes.EthFilterResult, error) { res := ðtypes.EthFilterResult{} for _, ev := range evs { @@ -1224,19 +1492,14 @@ func ethFilterResultFromEvents(evs []*filter.CollectedEvent, sa StateAPI) (*etht TransactionIndex: ethtypes.EthUint64(ev.MsgIdx), BlockNumber: ethtypes.EthUint64(ev.Height), } + var ( + err error + ok bool + ) - var err error - - for _, entry := range ev.Entries { - value, err := cborDecodeTopicValue(entry.Value) - if err != nil { - return nil, err - } - if entry.Key == ethtypes.EthTopic1 || entry.Key == ethtypes.EthTopic2 || entry.Key == ethtypes.EthTopic3 || entry.Key == ethtypes.EthTopic4 { - log.Topics = append(log.Topics, value) - } else { - log.Data = value - } + log.Data, log.Topics, ok = ethLogFromEvent(ev.Entries) + if !ok { + continue } log.Address, err = ethtypes.EthAddressFromFilecoinAddress(ev.EmitterAddr) @@ -1374,45 +1637,59 @@ func (e *ethSubscription) addFilter(ctx context.Context, f filter.Filter) { e.filters = append(e.filters, f) } +func (e *ethSubscription) send(ctx context.Context, v interface{}) { + resp := ethtypes.EthSubscriptionResponse{ + SubscriptionID: e.id, + Result: v, + } + + outParam, err := json.Marshal(resp) + if err != nil { + log.Warnw("marshaling subscription response", "sub", e.id, "error", err) + return + } + + if err := e.out(ctx, outParam); err != nil { + log.Warnw("sending subscription response", "sub", e.id, "error", err) + return + } +} + func (e *ethSubscription) start(ctx context.Context) { for { select { case <-ctx.Done(): return case v := <-e.in: - resp := ethtypes.EthSubscriptionResponse{ - SubscriptionID: e.id, - } - - var err error switch vt := v.(type) { case *filter.CollectedEvent: - resp.Result, err = ethFilterResultFromEvents([]*filter.CollectedEvent{vt}, e.StateAPI) + evs, err := ethFilterResultFromEvents([]*filter.CollectedEvent{vt}, e.StateAPI) + if err != nil { + continue + } + + for _, r := range evs.Results { + e.send(ctx, r) + } case *types.TipSet: - eb, err := newEthBlockFromFilecoinTipSet(ctx, vt, true, e.Chain, e.StateAPI) + ev, err := newEthBlockFromFilecoinTipSet(ctx, vt, true, e.Chain, e.StateAPI) if err != nil { break } - resp.Result = eb + e.send(ctx, ev) + case *types.SignedMessage: // mpool txid + evs, err := ethFilterResultFromMessages([]*types.SignedMessage{vt}, e.StateAPI) + if err != nil { + continue + } + + for _, r := range evs.Results { + e.send(ctx, r) + } default: log.Warnf("unexpected subscription value type: %T", vt) } - - if err != nil { - continue - } - - 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 - } } } } @@ -1455,7 +1732,7 @@ func newEthBlockFromFilecoinTipSet(ctx context.Context, ts *types.TipSet, fullTx return ethtypes.EthBlock{}, xerrors.Errorf("error loading messages for tipset: %v: %w", ts, err) } - block := ethtypes.NewEthBlock() + block := ethtypes.NewEthBlock(len(msgs) > 0) // this seems to be a very expensive way to get gasUsed of the block. may need to find an efficient way to do it gasUsed := int64(0) @@ -1614,6 +1891,7 @@ func ethTxFromNativeMessage(ctx context.Context, msg *types.Message, sa StateAPI Gas: ethtypes.EthUint64(msg.GasLimit), MaxFeePerGas: ethtypes.EthBigInt(msg.GasFeeCap), MaxPriorityFeePerGas: ethtypes.EthBigInt(msg.GasPremium), + AccessList: []ethtypes.EthHash{}, } } @@ -1747,11 +2025,6 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook } 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 - // should be feasible here since we are iterating over the logs anyway - receipt.LogsBloom[255] = 0x01 - receipt.Logs = make([]ethtypes.EthLog, 0, len(events)) for i, evt := range events { l := ethtypes.EthLog{ @@ -1763,17 +2036,16 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook BlockNumber: blockNumber, } - for _, entry := range evt.Entries { - value, err := cborDecodeTopicValue(entry.Value) - if err != nil { - return api.EthTxReceipt{}, xerrors.Errorf("failed to decode event log value: %w", err) - } - if entry.Key == ethtypes.EthTopic1 || entry.Key == ethtypes.EthTopic2 || entry.Key == ethtypes.EthTopic3 || entry.Key == ethtypes.EthTopic4 { - l.Topics = append(l.Topics, value) - } else { - l.Data = value - } + data, topics, ok := ethLogFromEvent(evt.Entries) + if !ok { + // not an eth event. + continue } + for _, topic := range topics { + ethtypes.EthBloomSet(receipt.LogsBloom, topic[:]) + } + l.Data = data + l.Topics = topics addr, err := address.NewIDAddress(uint64(evt.Emitter)) if err != nil { @@ -1785,6 +2057,7 @@ func newEthTxReceipt(ctx context.Context, tx ethtypes.EthTx, lookup *api.MsgLook return api.EthTxReceipt{}, xerrors.Errorf("failed to resolve Ethereum address: %w", err) } + ethtypes.EthBloomSet(receipt.LogsBloom, l.Address[:]) receipt.Logs = append(receipt.Logs, l) } } @@ -1828,6 +2101,52 @@ func (m *EthTxHashManager) Revert(ctx context.Context, from, to *types.TipSet) e return nil } +func (m *EthTxHashManager) PopulateExistingMappings(ctx context.Context, minHeight abi.ChainEpoch) error { + if minHeight < build.UpgradeHyggeHeight { + minHeight = build.UpgradeHyggeHeight + } + + ts := m.StateAPI.Chain.GetHeaviestTipSet() + for ts.Height() > minHeight { + for _, block := range ts.Blocks() { + msgs, err := m.StateAPI.Chain.SecpkMessagesForBlock(ctx, block) + if err != nil { + // If we can't find the messages, we've either imported from snapshot or pruned the store + log.Debug("exiting message mapping population at epoch ", ts.Height()) + return nil + } + + for _, msg := range msgs { + m.ProcessSignedMessage(ctx, msg) + } + } + + var err error + ts, err = m.StateAPI.Chain.GetTipSetFromKey(ctx, ts.Parents()) + if err != nil { + return err + } + } + + return nil +} + +func (m *EthTxHashManager) ProcessSignedMessage(ctx context.Context, msg *types.SignedMessage) { + if msg.Signature.Type != crypto.SigTypeDelegated { + return + } + + ethTx, err := newEthTxFromSignedMessage(ctx, msg, m.StateAPI) + if err != nil { + log.Errorf("error converting filecoin message to eth tx: %s", err) + } + + err = m.TransactionHashLookup.UpsertHash(ethTx.Hash, msg.Cid()) + if err != nil { + log.Errorf("error inserting tx mapping to db: %s", err) + } +} + func WaitForMpoolUpdates(ctx context.Context, ch <-chan api.MpoolUpdate, manager *EthTxHashManager) { for { select { @@ -1837,19 +2156,8 @@ func WaitForMpoolUpdates(ctx context.Context, ch <-chan api.MpoolUpdate, manager if u.Type != api.MpoolAdd { continue } - if u.Message.Signature.Type != crypto.SigTypeDelegated { - continue - } - ethTx, err := newEthTxFromSignedMessage(ctx, u.Message, manager.StateAPI) - if err != nil { - log.Errorf("error converting filecoin message to eth tx: %s", err) - } - - err = manager.TransactionHashLookup.UpsertHash(ethTx.Hash, u.Message.Cid()) - if err != nil { - log.Errorf("error inserting tx mapping to db: %s", err) - } + manager.ProcessSignedMessage(ctx, u.Message) } } } @@ -1870,45 +2178,6 @@ func EthTxHashGC(ctx context.Context, retentionDays int, manager *EthTxHashManag } } -func leftpad32(orig []byte) []byte { - needed := 32 - len(orig) - if needed <= 0 { - return orig - } - ret := make([]byte, 32) - copy(ret[needed:], orig) - return ret -} - -func trimLeadingZeros(b []byte) []byte { - for i := range b { - if b[i] != 0 { - return b[i:] - } - } - return []byte{} -} - -func cborEncodeTopicValue(orig []byte) ([]byte, error) { - var buf bytes.Buffer - err := cbg.WriteByteArray(&buf, trimLeadingZeros(orig)) - if err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func cborDecodeTopicValue(orig []byte) ([]byte, error) { - if len(orig) == 0 { - return orig, nil - } - decoded, err := cbg.ReadByteArray(bytes.NewReader(orig), uint64(len(orig))) - if err != nil { - return nil, err - } - return leftpad32(decoded), nil -} - func parseEthTopics(topics ethtypes.EthTopicSpec) (map[string][][]byte, error) { keys := map[string][][]byte{} for idx, vals := range topics { @@ -1916,14 +2185,58 @@ func parseEthTopics(topics ethtypes.EthTopicSpec) (map[string][][]byte, error) { continue } // Ethereum topics are emitted using `LOG{0..4}` opcodes resulting in topics1..4 - key := fmt.Sprintf("topic%d", idx+1) + key := fmt.Sprintf("t%d", idx+1) for _, v := range vals { - encodedVal, err := cborEncodeTopicValue(v[:]) - if err != nil { - return nil, xerrors.Errorf("failed to encode topic value") - } - keys[key] = append(keys[key], encodedVal) + v := v // copy the ethhash to avoid repeatedly referencing the same one. + keys[key] = append(keys[key], v[:]) } } return keys, nil } + +func calculateRewardsAndGasUsed(rewardPercentiles []float64, txGasRewards gasRewardSorter) ([]ethtypes.EthBigInt, uint64) { + var totalGasUsed uint64 + for _, tx := range txGasRewards { + totalGasUsed += tx.gas + } + + rewards := make([]ethtypes.EthBigInt, len(rewardPercentiles)) + for i := range rewards { + rewards[i] = ethtypes.EthBigIntZero + } + + if len(txGasRewards) == 0 { + return rewards, totalGasUsed + } + + sort.Stable(txGasRewards) + + var idx int + var sum uint64 + for i, percentile := range rewardPercentiles { + threshold := uint64(float64(totalGasUsed) * percentile / 100) + for sum < threshold && idx < len(txGasRewards)-1 { + sum += txGasRewards[idx].gas + idx++ + } + rewards[i] = txGasRewards[idx].reward + } + + return rewards, totalGasUsed +} + +type gasRewardTuple struct { + gas uint64 + reward ethtypes.EthBigInt +} + +// sorted in ascending order +type gasRewardSorter []gasRewardTuple + +func (g gasRewardSorter) Len() int { return len(g) } +func (g gasRewardSorter) Swap(i, j int) { + g[i], g[j] = g[j], g[i] +} +func (g gasRewardSorter) Less(i, j int) bool { + return g[i].reward.Int.Cmp(g[j].reward.Int) == -1 +} diff --git a/node/impl/full/eth_test.go b/node/impl/full/eth_test.go new file mode 100644 index 000000000..67a8b0500 --- /dev/null +++ b/node/impl/full/eth_test.go @@ -0,0 +1,166 @@ +package full + +import ( + "testing" + + "github.com/ipfs/go-cid" + "github.com/stretchr/testify/require" + + "github.com/filecoin-project/go-state-types/big" + + "github.com/filecoin-project/lotus/chain/types" + "github.com/filecoin-project/lotus/chain/types/ethtypes" +) + +func TestEthLogFromEvent(t *testing.T) { + // basic empty + data, topics, ok := ethLogFromEvent(nil) + require.True(t, ok) + require.Nil(t, data) + require.Nil(t, topics) + + // basic topic + data, topics, ok = ethLogFromEvent([]types.EventEntry{{ + Flags: 0, + Key: "t1", + Codec: cid.Raw, + Value: make([]byte, 32), + }}) + require.True(t, ok) + require.Nil(t, data) + require.Len(t, topics, 1) + require.Equal(t, topics[0], ethtypes.EthHash{}) + + // basic topic with data + data, topics, ok = ethLogFromEvent([]types.EventEntry{{ + Flags: 0, + Key: "t1", + Codec: cid.Raw, + Value: make([]byte, 32), + }, { + Flags: 0, + Key: "d", + Codec: cid.Raw, + Value: []byte{0x0}, + }}) + require.True(t, ok) + require.Equal(t, data, []byte{0x0}) + require.Len(t, topics, 1) + require.Equal(t, topics[0], ethtypes.EthHash{}) + + // skip topic + _, _, ok = ethLogFromEvent([]types.EventEntry{{ + Flags: 0, + Key: "t2", + Codec: cid.Raw, + Value: make([]byte, 32), + }}) + require.False(t, ok) + + // duplicate topic + _, _, ok = ethLogFromEvent([]types.EventEntry{{ + Flags: 0, + Key: "t1", + Codec: cid.Raw, + Value: make([]byte, 32), + }, { + Flags: 0, + Key: "t1", + Codec: cid.Raw, + Value: make([]byte, 32), + }}) + require.False(t, ok) + + // duplicate data + _, _, ok = ethLogFromEvent([]types.EventEntry{{ + Flags: 0, + Key: "d", + Codec: cid.Raw, + Value: make([]byte, 32), + }, { + Flags: 0, + Key: "d", + Codec: cid.Raw, + Value: make([]byte, 32), + }}) + require.False(t, ok) + + // unknown key is fine + data, topics, ok = ethLogFromEvent([]types.EventEntry{{ + Flags: 0, + Key: "t5", + Codec: cid.Raw, + Value: make([]byte, 32), + }, { + Flags: 0, + Key: "t1", + Codec: cid.Raw, + Value: make([]byte, 32), + }}) + require.True(t, ok) + require.Nil(t, data) + require.Len(t, topics, 1) + require.Equal(t, topics[0], ethtypes.EthHash{}) +} + +func TestReward(t *testing.T) { + baseFee := big.NewInt(100) + testcases := []struct { + maxFeePerGas, maxPriorityFeePerGas big.Int + answer big.Int + }{ + {maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(200), answer: big.NewInt(200)}, + {maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(300), answer: big.NewInt(300)}, + {maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(500), answer: big.NewInt(500)}, + {maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(600), answer: big.NewInt(500)}, + {maxFeePerGas: big.NewInt(600), maxPriorityFeePerGas: big.NewInt(1000), answer: big.NewInt(500)}, + {maxFeePerGas: big.NewInt(50), maxPriorityFeePerGas: big.NewInt(200), answer: big.NewInt(-50)}, + } + for _, tc := range testcases { + tx := ethtypes.EthTx{ + MaxFeePerGas: ethtypes.EthBigInt(tc.maxFeePerGas), + MaxPriorityFeePerGas: ethtypes.EthBigInt(tc.maxPriorityFeePerGas), + } + reward := tx.Reward(baseFee) + require.Equal(t, 0, reward.Int.Cmp(tc.answer.Int), reward, tc.answer) + } +} + +func TestRewardPercentiles(t *testing.T) { + testcases := []struct { + percentiles []float64 + txGasRewards gasRewardSorter + answer []int64 + }{ + { + percentiles: []float64{25, 50, 75}, + txGasRewards: []gasRewardTuple{}, + answer: []int64{0, 0, 0}, + }, + { + percentiles: []float64{25, 50, 75, 100}, + txGasRewards: []gasRewardTuple{ + {gas: uint64(0), reward: ethtypes.EthBigInt(big.NewInt(300))}, + {gas: uint64(100), reward: ethtypes.EthBigInt(big.NewInt(200))}, + {gas: uint64(350), reward: ethtypes.EthBigInt(big.NewInt(100))}, + {gas: uint64(500), reward: ethtypes.EthBigInt(big.NewInt(600))}, + {gas: uint64(300), reward: ethtypes.EthBigInt(big.NewInt(700))}, + }, + answer: []int64{200, 700, 700, 700}, + }, + } + for _, tc := range testcases { + rewards, totalGasUsed := calculateRewardsAndGasUsed(tc.percentiles, tc.txGasRewards) + gasUsed := uint64(0) + for _, tx := range tc.txGasRewards { + gasUsed += tx.gas + } + ans := []ethtypes.EthBigInt{} + for _, bi := range tc.answer { + ans = append(ans, ethtypes.EthBigInt(big.NewInt(bi))) + } + require.Equal(t, totalGasUsed, gasUsed) + require.Equal(t, len(ans), len(tc.percentiles)) + require.Equal(t, ans, rewards) + } +} diff --git a/node/impl/full/gas.go b/node/impl/full/gas.go index 435e2c65b..c0e328bc9 100644 --- a/node/impl/full/gas.go +++ b/node/impl/full/gas.go @@ -248,22 +248,23 @@ func (m *GasModule) GasEstimateGasLimit(ctx context.Context, msgIn *types.Messag } return gasEstimateGasLimit(ctx, m.Chain, m.Stmgr, m.Mpool, msgIn, ts) } -func gasEstimateGasLimit( + +// gasEstimateCallWithGas invokes a message "msgIn" on the earliest available tipset with pending +// messages in the message pool. The function returns the result of the message invocation, the +// pending messages, the tipset used for the invocation, and an error if occurred. +// The returned information can be used to make subsequent calls to CallWithGas with the same parameters. +func gasEstimateCallWithGas( ctx context.Context, cstore *store.ChainStore, smgr *stmgr.StateManager, mpool *messagepool.MessagePool, msgIn *types.Message, currTs *types.TipSet, -) (int64, error) { +) (*api.InvocResult, []types.ChainMsg, *types.TipSet, error) { msg := *msgIn - msg.GasLimit = build.BlockGasLimit - msg.GasFeeCap = big.Zero() - msg.GasPremium = big.Zero() - fromA, err := smgr.ResolveToDeterministicAddress(ctx, msgIn.From, currTs) if err != nil { - return -1, xerrors.Errorf("getting key address: %w", err) + return nil, []types.ChainMsg{}, nil, xerrors.Errorf("getting key address: %w", err) } pending, ts := mpool.PendingFor(ctx, fromA) @@ -284,12 +285,34 @@ func gasEstimateGasLimit( } ts, err = cstore.GetTipSetFromKey(ctx, ts.Parents()) if err != nil { - return -1, xerrors.Errorf("getting parent tipset: %w", err) + return nil, []types.ChainMsg{}, nil, xerrors.Errorf("getting parent tipset: %w", err) } } if err != nil { - return -1, xerrors.Errorf("CallWithGas failed: %w", err) + return nil, []types.ChainMsg{}, nil, xerrors.Errorf("CallWithGas failed: %w", err) } + + return res, priorMsgs, ts, nil +} + +func gasEstimateGasLimit( + ctx context.Context, + cstore *store.ChainStore, + smgr *stmgr.StateManager, + mpool *messagepool.MessagePool, + msgIn *types.Message, + currTs *types.TipSet, +) (int64, error) { + msg := *msgIn + msg.GasLimit = build.BlockGasLimit + msg.GasFeeCap = big.Zero() + msg.GasPremium = big.Zero() + + res, _, ts, err := gasEstimateCallWithGas(ctx, cstore, smgr, mpool, &msg, currTs) + if err != nil { + return -1, xerrors.Errorf("gas estimation failed: %w", err) + } + if res.MsgRct.ExitCode == exitcode.SysErrOutOfGas { return -1, &api.ErrOutOfGas{} } @@ -300,6 +323,8 @@ func gasEstimateGasLimit( ret := res.MsgRct.GasUsed + log.Infow("GasEstimateMessageGas CallWithGas Result", "GasUsed", ret, "ExitCode", res.MsgRct.ExitCode) + transitionalMulti := 1.0 // Overestimate gas around the upgrade if ts.Height() <= build.UpgradeSkyrHeight && (build.UpgradeSkyrHeight-ts.Height() <= 20) { diff --git a/node/impl/full/wallet.go b/node/impl/full/wallet.go index 0927e5aca..fdf00e086 100644 --- a/node/impl/full/wallet.go +++ b/node/impl/full/wallet.go @@ -15,7 +15,6 @@ import ( "github.com/filecoin-project/lotus/chain/stmgr" "github.com/filecoin-project/lotus/chain/types" "github.com/filecoin-project/lotus/chain/wallet" - "github.com/filecoin-project/lotus/chain/wallet/key" "github.com/filecoin-project/lotus/lib/sigs" ) @@ -53,11 +52,7 @@ func (a *WalletAPI) WalletSignMessage(ctx context.Context, k address.Address, ms return nil, xerrors.Errorf("failed to resolve ID address: %w", keyAddr) } - keyInfo, err := a.Wallet.WalletExport(ctx, k) - if err != nil { - return nil, err - } - sb, err := messagesigner.SigningBytes(msg, key.ActSigType(keyInfo.Type)) + sb, err := messagesigner.SigningBytes(msg, keyAddr.Protocol()) if err != nil { return nil, err } diff --git a/node/modules/actorevent.go b/node/modules/actorevent.go index 55a79a59a..a40d208aa 100644 --- a/node/modules/actorevent.go +++ b/node/modules/actorevent.go @@ -92,8 +92,9 @@ func EthEventAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRepo } ee.EventFilterManager = &filter.EventFilterManager{ - ChainStore: cs, - EventIndex: eventIndex, // will be nil unless EnableHistoricFilterAPI is true + ChainStore: cs, + EventGetter: chainapi.ChainGetEvents, + EventIndex: eventIndex, // will be nil unless EnableHistoricFilterAPI is true AddressResolver: func(ctx context.Context, emitter abi.ActorID, ts *types.TipSet) (address.Address, bool) { // we only want to match using f4 addresses idAddr, err := address.NewIDAddress(uint64(emitter)) diff --git a/node/modules/chain.go b/node/modules/chain.go index 49418d883..6129f0345 100644 --- a/node/modules/chain.go +++ b/node/modules/chain.go @@ -181,3 +181,7 @@ func NewSlashFilter(ds dtypes.MetadataDS) *slashfilter.SlashFilter { func UpgradeSchedule() stmgr.UpgradeSchedule { return filcns.DefaultUpgradeSchedule() } + +func EnableStoringEvents(cs *store.ChainStore) { + cs.StoreEvents(true) +} diff --git a/node/modules/ethmodule.go b/node/modules/ethmodule.go index 9889233f4..eba6c54d1 100644 --- a/node/modules/ethmodule.go +++ b/node/modules/ethmodule.go @@ -2,6 +2,7 @@ package modules import ( "context" + "os" "path/filepath" "go.uber.org/fx" @@ -24,7 +25,13 @@ func EthModuleAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRep return nil, err } - transactionHashLookup, err := ethhashlookup.NewTransactionHashLookup(filepath.Join(sqlitePath, "txhash.db")) + dbPath := filepath.Join(sqlitePath, "txhash.db") + + // Check if the db exists, if not, we'll back-fill some entries + _, err = os.Stat(dbPath) + dbAlreadyExists := err == nil + + transactionHashLookup, err := ethhashlookup.NewTransactionHashLookup(dbPath) if err != nil { return nil, err } @@ -40,6 +47,13 @@ func EthModuleAPI(cfg config.FevmConfig) func(helpers.MetricsCtx, repo.LockedRep TransactionHashLookup: transactionHashLookup, } + if !dbAlreadyExists { + err = ethTxHashManager.PopulateExistingMappings(mctx, 0) + if err != nil { + return nil, err + } + } + const ChainHeadConfidence = 1 ctx := helpers.LifecycleCtx(mctx, lc)