diff --git a/app/node/builder.go b/app/node/builder.go index 2477979035..af606fc015 100644 --- a/app/node/builder.go +++ b/app/node/builder.go @@ -167,7 +167,7 @@ func (b *Builder) build(ctx context.Context) (*Node, error) { if err != nil { return nil, err } - if nd.eth, err = eth.NewEthSubModule(ctx, b.repo.Config(), nd.chain, nd.mpool, sqlitePath); err != nil { + if nd.eth, err = eth.NewEthSubModule(ctx, b.repo.Config(), nd.chain, nd.mpool, sqlitePath, nd.syncer.API()); err != nil { return nil, err } diff --git a/app/node/rpc.go b/app/node/rpc.go index c7b26ee5dd..d4fde6468d 100644 --- a/app/node/rpc.go +++ b/app/node/rpc.go @@ -187,6 +187,7 @@ func aliasETHAPI(rpcServer *jsonrpc.RPCServer) { rpcServer.AliasMethod("eth_getStorageAt", "Filecoin.EthGetStorageAt") rpcServer.AliasMethod("eth_getBalance", "Filecoin.EthGetBalance") rpcServer.AliasMethod("eth_chainId", "Filecoin.EthChainId") + rpcServer.AliasMethod("eth_syncing", "Filecoin.EthSyncing") rpcServer.AliasMethod("eth_feeHistory", "Filecoin.EthFeeHistory") rpcServer.AliasMethod("eth_protocolVersion", "Filecoin.EthProtocolVersion") rpcServer.AliasMethod("eth_maxPriorityFeePerGas", "Filecoin.EthMaxPriorityFeePerGas") diff --git a/app/submodule/eth/dummy.go b/app/submodule/eth/dummy.go index 9fe45b1570..04e4c250ef 100644 --- a/app/submodule/eth/dummy.go +++ b/app/submodule/eth/dummy.go @@ -105,6 +105,10 @@ func (e *ethAPIDummy) EthChainId(ctx context.Context) (types.EthUint64, error) { return 0, ErrModuleDisabled } +func (e *ethAPIDummy) EthSyncing(ctx context.Context) (types.EthSyncingResult, error) { + return types.EthSyncingResult{}, ErrModuleDisabled +} + func (e *ethAPIDummy) NetVersion(ctx context.Context) (string, error) { return "", ErrModuleDisabled } diff --git a/app/submodule/eth/eth_api.go b/app/submodule/eth/eth_api.go index fc9d2d5e07..e775a442f4 100644 --- a/app/submodule/eth/eth_api.go +++ b/app/submodule/eth/eth_api.go @@ -684,6 +684,43 @@ func (a *ethAPI) EthChainId(ctx context.Context) (types.EthUint64, error) { return types.EthUint64(types2.Eip155ChainID), nil } +func (a *ethAPI) EthSyncing(ctx context.Context) (types.EthSyncingResult, error) { + state, err := a.em.syncAPI.SyncState(ctx) + if err != nil { + return types.EthSyncingResult{}, fmt.Errorf("failed calling SyncState: %w", err) + } + + if len(state.ActiveSyncs) == 0 { + return types.EthSyncingResult{}, errors.New("no active syncs, try again") + } + + working := -1 + for i, ss := range state.ActiveSyncs { + if ss.Stage == types.StageIdle { + continue + } + working = i + + } + if working == -1 { + working = len(state.ActiveSyncs) - 1 + } + + ss := state.ActiveSyncs[working] + if ss.Base == nil || ss.Target == nil { + return types.EthSyncingResult{}, errors.New("missing syncing information, try again") + } + + res := types.EthSyncingResult{ + DoneSync: ss.Stage == types.StageSyncComplete, + CurrentBlock: types.EthUint64(ss.Height), + StartingBlock: types.EthUint64(ss.Base.Height()), + HighestBlock: types.EthUint64(ss.Target.Height()), + } + + return res, nil +} + func (a *ethAPI) EthFeeHistory(ctx context.Context, p jsonrpc.RawParams) (types.EthFeeHistory, error) { params, err := jsonrpc.DecodeParams[types.EthFeeHistoryParams](p) if err != nil { diff --git a/app/submodule/eth/eth_submodule.go b/app/submodule/eth/eth_submodule.go index c6e6248834..3a8d9cba59 100644 --- a/app/submodule/eth/eth_submodule.go +++ b/app/submodule/eth/eth_submodule.go @@ -18,6 +18,7 @@ func NewEthSubModule(ctx context.Context, chainModule *chain.ChainSubmodule, mpoolModule *mpool.MessagePoolSubmodule, sqlitePath string, + syncAPI v1api.ISyncer, ) (*EthSubModule, error) { ctx, cancel := context.WithCancel(ctx) em := &EthSubModule{ @@ -27,6 +28,7 @@ func NewEthSubModule(ctx context.Context, sqlitePath: sqlitePath, ctx: ctx, cancel: cancel, + syncAPI: syncAPI, } ee, err := newEthEventAPI(ctx, em) if err != nil { @@ -66,8 +68,9 @@ type EthSubModule struct { // nolint ethEventAPI *ethEventAPI ethAPIAdapter ethAPIAdapter - ctx context.Context - cancel context.CancelFunc + ctx context.Context + cancel context.CancelFunc + syncAPI v1api.ISyncer } func (em *EthSubModule) Start(_ context.Context) error { diff --git a/venus-shared/actors/types/eth.go b/venus-shared/actors/types/eth.go index 1e5a9bcbd0..8b298d9630 100644 --- a/venus-shared/actors/types/eth.go +++ b/venus-shared/actors/types/eth.go @@ -244,6 +244,30 @@ func (c *EthCall) UnmarshalJSON(b []byte) error { return nil } +type EthSyncingResult struct { + DoneSync bool + StartingBlock EthUint64 + CurrentBlock EthUint64 + HighestBlock EthUint64 +} + +func (sr EthSyncingResult) MarshalJSON() ([]byte, error) { + if sr.DoneSync { + // when done syncing, the json response should be '"result": false' + return []byte("false"), nil + } + + // need to do an anonymous struct to avoid infinite recursion + return json.Marshal(&struct { + StartingBlock EthUint64 `json:"startingblock"` + CurrentBlock EthUint64 `json:"currentblock"` + HighestBlock EthUint64 `json:"highestblock"` + }{ + StartingBlock: sr.StartingBlock, + CurrentBlock: sr.CurrentBlock, + HighestBlock: sr.HighestBlock}) +} + const ( EthAddressLength = 20 EthHashLength = 32 diff --git a/venus-shared/api/chain/v1/eth.go b/venus-shared/api/chain/v1/eth.go index 0a2bb2f6b2..c1fc105240 100644 --- a/venus-shared/api/chain/v1/eth.go +++ b/venus-shared/api/chain/v1/eth.go @@ -47,6 +47,7 @@ type IETH interface { EthGetStorageAt(ctx context.Context, address types.EthAddress, position types.EthBytes, blkParam types.EthBlockNumberOrHash) (types.EthBytes, error) //perm:read EthGetBalance(ctx context.Context, address types.EthAddress, blkParam types.EthBlockNumberOrHash) (types.EthBigInt, error) //perm:read EthChainId(ctx context.Context) (types.EthUint64, error) //perm:read + EthSyncing(ctx context.Context) (types.EthSyncingResult, error) //perm:read NetVersion(ctx context.Context) (string, error) //perm:read NetListening(ctx context.Context) (bool, error) //perm:read EthProtocolVersion(ctx context.Context) (types.EthUint64, error) //perm:read diff --git a/venus-shared/api/chain/v1/method.md b/venus-shared/api/chain/v1/method.md index dbf095b94f..564532bec7 100644 --- a/venus-shared/api/chain/v1/method.md +++ b/venus-shared/api/chain/v1/method.md @@ -93,6 +93,7 @@ curl http://:/rpc/v1 -X POST -H "Content-Type: application/json" -H " * [EthMaxPriorityFeePerGas](#ethmaxpriorityfeepergas) * [EthProtocolVersion](#ethprotocolversion) * [EthSendRawTransaction](#ethsendrawtransaction) + * [EthSyncing](#ethsyncing) * [FilecoinAddressToEthAddress](#filecoinaddresstoethaddress) * [NetListening](#netlistening) * [NetVersion](#netversion) @@ -2943,6 +2944,15 @@ Inputs: Response: `"0x0707070707070707070707070707070707070707070707070707070707070707"` +### EthSyncing + + +Perms: read + +Inputs: `[]` + +Response: `false` + ### FilecoinAddressToEthAddress FilecoinAddressToEthAddress converts an f410 or f0 Filecoin Address to an EthAddress diff --git a/venus-shared/api/chain/v1/mock/mock_fullnode.go b/venus-shared/api/chain/v1/mock/mock_fullnode.go index 485cea98b5..05340002e8 100644 --- a/venus-shared/api/chain/v1/mock/mock_fullnode.go +++ b/venus-shared/api/chain/v1/mock/mock_fullnode.go @@ -965,6 +965,21 @@ func (mr *MockFullNodeMockRecorder) EthSubscribe(arg0, arg1 interface{}) *gomock return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthSubscribe", reflect.TypeOf((*MockFullNode)(nil).EthSubscribe), arg0, arg1) } +// EthSyncing mocks base method. +func (m *MockFullNode) EthSyncing(arg0 context.Context) (types.EthSyncingResult, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "EthSyncing", arg0) + ret0, _ := ret[0].(types.EthSyncingResult) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// EthSyncing indicates an expected call of EthSyncing. +func (mr *MockFullNodeMockRecorder) EthSyncing(arg0 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthSyncing", reflect.TypeOf((*MockFullNode)(nil).EthSyncing), arg0) +} + // EthUninstallFilter mocks base method. func (m *MockFullNode) EthUninstallFilter(arg0 context.Context, arg1 types.EthFilterID) (bool, error) { m.ctrl.T.Helper() diff --git a/venus-shared/api/chain/v1/proxy_gen.go b/venus-shared/api/chain/v1/proxy_gen.go index b787cf5d41..4a9c07a44c 100644 --- a/venus-shared/api/chain/v1/proxy_gen.go +++ b/venus-shared/api/chain/v1/proxy_gen.go @@ -877,6 +877,7 @@ type IETHStruct struct { EthMaxPriorityFeePerGas func(ctx context.Context) (types.EthBigInt, error) `perm:"read"` EthProtocolVersion func(ctx context.Context) (types.EthUint64, error) `perm:"read"` EthSendRawTransaction func(ctx context.Context, rawTx types.EthBytes) (types.EthHash, error) `perm:"read"` + EthSyncing func(ctx context.Context) (types.EthSyncingResult, error) `perm:"read"` FilecoinAddressToEthAddress func(ctx context.Context, filecoinAddress address.Address) (types.EthAddress, error) `perm:"read"` NetListening func(ctx context.Context) (bool, error) `perm:"read"` NetVersion func(ctx context.Context) (string, error) `perm:"read"` @@ -965,6 +966,9 @@ func (s *IETHStruct) EthProtocolVersion(p0 context.Context) (types.EthUint64, er func (s *IETHStruct) EthSendRawTransaction(p0 context.Context, p1 types.EthBytes) (types.EthHash, error) { return s.Internal.EthSendRawTransaction(p0, p1) } +func (s *IETHStruct) EthSyncing(p0 context.Context) (types.EthSyncingResult, error) { + return s.Internal.EthSyncing(p0) +} func (s *IETHStruct) FilecoinAddressToEthAddress(p0 context.Context, p1 address.Address) (types.EthAddress, error) { return s.Internal.FilecoinAddressToEthAddress(p0, p1) } diff --git a/venus-shared/types/eth.go b/venus-shared/types/eth.go index 4db850cb6a..c760c8c8a3 100644 --- a/venus-shared/types/eth.go +++ b/venus-shared/types/eth.go @@ -44,6 +44,7 @@ type ( EthSubscriptionID = types.EthSubscriptionID EthSubscriptionParams = types.EthSubscriptionParams EthSubscriptionResponse = types.EthSubscriptionResponse + EthSyncingResult = types.EthSyncingResult EthTopicSpec = types.EthTopicSpec EthTxReceipt = types.EthTxReceipt EthUint64 = types.EthUint64