Move services to status-node

Move all the services to status-node, as some of them were there, some
of them were in the geth backend and scattered around.
This commit is contained in:
Andrea Maria Piana 2021-06-30 13:40:54 +02:00
parent 36430257fd
commit 4b0daeb47b
53 changed files with 942 additions and 1806 deletions

View File

@ -10,7 +10,7 @@ import (
"path/filepath"
"sync"
"github.com/pborman/uuid"
"github.com/google/uuid"
gethkeystore "github.com/ethereum/go-ethereum/accounts/keystore"
gethcommon "github.com/ethereum/go-ethereum/common"
@ -206,12 +206,16 @@ func (m *Manager) SetAccountAddresses(main types.Address, secondary ...types.Add
}
// SetChatAccount initializes selectedChatAccount with privKey
func (m *Manager) SetChatAccount(privKey *ecdsa.PrivateKey) {
func (m *Manager) SetChatAccount(privKey *ecdsa.PrivateKey) error {
m.mu.Lock()
defer m.mu.Unlock()
address := crypto.PubkeyToAddress(privKey.PublicKey)
id := uuid.NewRandom()
id, err := uuid.NewRandom()
if err != nil {
return err
}
key := &types.Key{
ID: id,
Address: address,
@ -222,6 +226,7 @@ func (m *Manager) SetChatAccount(privKey *ecdsa.PrivateKey) {
Address: address,
AccountKey: key,
}
return nil
}
// MainAccountAddress returns currently selected watch addresses.

View File

@ -35,7 +35,6 @@ type StatusBackend interface {
CallPrivateRPC(inputJSON string) (string, error)
CallRPC(inputJSON string) (string, error)
GetNodesFromContract(rpcEndpoint string, contractAddress string) ([]string, error)
HashTransaction(sendArgs transactions.SendTxArgs) (transactions.SendTxArgs, types.Hash, error)
HashTypedData(typed typeddata.TypedData) (types.Hash, error)
HashTypedDataV4(typed signercore.TypedData) (types.Hash, error)

View File

@ -11,18 +11,13 @@ import (
"sync"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethcrypto "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
gethnode "github.com/ethereum/go-ethereum/node"
signercore "github.com/ethereum/go-ethereum/signer/core"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/appdatabase"
"github.com/status-im/status-go/appmetrics"
"github.com/status-im/status-go/connection"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
@ -32,19 +27,8 @@ import (
"github.com/status-im/status-go/node"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc"
accountssvc "github.com/status-im/status-go/services/accounts"
appmetricsservice "github.com/status-im/status-go/services/appmetrics"
"github.com/status-im/status-go/services/browsers"
localnotifications "github.com/status-im/status-go/services/local-notifications"
"github.com/status-im/status-go/services/mailservers"
"github.com/status-im/status-go/services/permissions"
"github.com/status-im/status-go/services/personal"
"github.com/status-im/status-go/services/rpcfilters"
"github.com/status-im/status-go/services/rpcstats"
"github.com/status-im/status-go/services/subscriptions"
"github.com/status-im/status-go/services/typeddata"
"github.com/status-im/status-go/services/wakuext"
"github.com/status-im/status-go/services/wallet"
"github.com/status-im/status-go/signal"
"github.com/status-im/status-go/transactions"
)
@ -58,8 +42,6 @@ var (
ErrWhisperClearIdentitiesFailure = errors.New("failed to clear whisper identities")
// ErrWhisperIdentityInjectionFailure injecting whisper identities has failed.
ErrWhisperIdentityInjectionFailure = errors.New("failed to inject identity into Whisper")
// ErrWakuClearIdentitiesFailure clearing whisper identities has failed.
ErrWakuClearIdentitiesFailure = errors.New("failed to clear waku identities")
// ErrWakuIdentityInjectionFailure injecting whisper identities has failed.
ErrWakuIdentityInjectionFailure = errors.New("failed to inject identity into waku")
// ErrUnsupportedRPCMethod is for methods not supported by the RPC interface
@ -81,7 +63,6 @@ type GethStatusBackend struct {
appDB *sql.DB
statusNode *node.StatusNode
personalAPI *personal.PublicAPI
rpcFilters *rpcfilters.Service
multiaccountsDB *multiaccounts.Database
account *multiaccounts.Account
accountManager *account.GethManager
@ -91,6 +72,7 @@ type GethStatusBackend struct {
selectedAccountKeyID string
log log.Logger
allowAllRPC bool // used only for tests, disables api method restrictions
}
// NewGethStatusBackend create a new GethStatusBackend instance
@ -101,14 +83,12 @@ func NewGethStatusBackend() *GethStatusBackend {
accountManager := account.NewGethManager()
transactor := transactions.NewTransactor()
personalAPI := personal.NewAPI()
rpcFilters := rpcfilters.New(statusNode)
return &GethStatusBackend{
statusNode: statusNode,
accountManager: accountManager,
transactor: transactor,
personalAPI: personalAPI,
rpcFilters: rpcFilters,
log: log.New("package", "status-go/api.GethStatusBackend"),
}
}
@ -142,10 +122,12 @@ func (b *GethStatusBackend) IsNodeRunning() bool {
func (b *GethStatusBackend) StartNode(config *params.NodeConfig) error {
b.mu.Lock()
defer b.mu.Unlock()
log.Info("STARTING NODE")
if err := b.startNode(config); err != nil {
signal.SendNodeCrashed(err)
return err
}
log.Info("STARTED NODE")
return nil
}
@ -167,6 +149,7 @@ func (b *GethStatusBackend) OpenAccounts() error {
return err
}
b.multiaccountsDB = db
b.statusNode.SetMultiaccountsDB(db)
return nil
}
@ -251,6 +234,7 @@ func (b *GethStatusBackend) ensureAppDBOpened(account multiaccounts.Account, pas
b.log.Error("failed to initialize db", "err", err)
return err
}
b.statusNode.SetAppDB(b.appDB)
return nil
}
@ -503,14 +487,17 @@ func (b *GethStatusBackend) StartNodeWithAccountAndConfig(
nodecfg *params.NodeConfig,
subaccs []accounts.Account,
) error {
log.Info("STARTING 1 NODE")
err := b.SaveAccount(account)
if err != nil {
return err
}
log.Info("STARTING 2 NODE")
err = b.ensureAppDBOpened(account, password)
if err != nil {
return err
}
log.Info("STARTING 3 NODE")
err = b.saveAccountsAndSettings(settings, nodecfg, subaccs)
if err != nil {
return err
@ -565,66 +552,6 @@ func (b *GethStatusBackend) GetNodeConfig() (*params.NodeConfig, error) {
return b.loadNodeConfig()
}
func (b *GethStatusBackend) rpcFiltersService() gethnode.ServiceConstructor {
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
return rpcfilters.New(b.statusNode), nil
}
}
func (b *GethStatusBackend) subscriptionService() gethnode.ServiceConstructor {
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
return subscriptions.New(func() *rpc.Client { return b.statusNode.RPCPrivateClient() }), nil
}
}
func (b *GethStatusBackend) rpcStatsService() gethnode.ServiceConstructor {
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
return rpcstats.New(), nil
}
}
func (b *GethStatusBackend) accountsService(accountsFeed *event.Feed) gethnode.ServiceConstructor {
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
return accountssvc.NewService(accounts.NewDB(b.appDB), b.multiaccountsDB, b.accountManager.Manager, accountsFeed), nil
}
}
func (b *GethStatusBackend) browsersService() gethnode.ServiceConstructor {
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
return browsers.NewService(browsers.NewDB(b.appDB)), nil
}
}
func (b *GethStatusBackend) permissionsService() gethnode.ServiceConstructor {
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
return permissions.NewService(permissions.NewDB(b.appDB)), nil
}
}
func (b *GethStatusBackend) mailserversService() gethnode.ServiceConstructor {
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
return mailservers.NewService(mailservers.NewDB(b.appDB)), nil
}
}
func (b *GethStatusBackend) appmetricsService() gethnode.ServiceConstructor {
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
return appmetricsservice.NewService(appmetrics.NewDB(b.appDB)), nil
}
}
func (b *GethStatusBackend) walletService(network uint64, accountsFeed *event.Feed) gethnode.ServiceConstructor {
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
return wallet.NewService(wallet.NewDB(b.appDB, network), accountsFeed), nil
}
}
func (b *GethStatusBackend) localNotificationsService(network uint64) gethnode.ServiceConstructor {
return func(*gethnode.ServiceContext) (gethnode.Service, error) {
return localnotifications.NewService(b.appDB, network), nil
}
}
func (b *GethStatusBackend) startNode(config *params.NodeConfig) (err error) {
defer func() {
if r := recover(); r != nil {
@ -644,26 +571,12 @@ func (b *GethStatusBackend) startNode(config *params.NodeConfig) (err error) {
if err := config.Validate(); err != nil {
return err
}
accountsFeed := &event.Feed{}
services := []gethnode.ServiceConstructor{}
services = appendIf(config.UpstreamConfig.Enabled, services, b.rpcFiltersService())
services = append(services, b.subscriptionService())
services = append(services, b.rpcStatsService())
services = append(services, b.appmetricsService())
services = appendIf(b.appDB != nil && b.multiaccountsDB != nil, services, b.accountsService(accountsFeed))
services = appendIf(config.BrowsersConfig.Enabled, services, b.browsersService())
services = appendIf(config.PermissionsConfig.Enabled, services, b.permissionsService())
services = appendIf(config.MailserversConfig.Enabled, services, b.mailserversService())
services = appendIf(config.WalletConfig.Enabled, services, b.walletService(config.NetworkID, accountsFeed))
// We ignore for now local notifications flag as users who are upgrading have no mean to enable it
services = append(services, b.localNotificationsService(config.NetworkID))
manager := b.accountManager.GetManager()
if manager == nil {
return errors.New("ethereum accounts.Manager is nil")
}
if err = b.statusNode.StartWithOptions(config, node.StartOptions{
Services: services,
// The peers discovery protocols are started manually after
// `node.ready` signal is sent.
// It was discussed in https://github.com/status-im/status-go/pull/1333.
@ -672,13 +585,7 @@ func (b *GethStatusBackend) startNode(config *params.NodeConfig) (err error) {
}); err != nil {
return
}
if config.WalletConfig.Enabled {
walletService, err := b.statusNode.WalletService()
if err != nil {
return err
}
walletService.SetClient(b.statusNode.RPCClient().Ethclient())
}
signal.SendNodeStarted()
b.transactor.SetNetworkID(config.NetworkID)
@ -691,14 +598,6 @@ func (b *GethStatusBackend) startNode(config *params.NodeConfig) (err error) {
}
b.log.Info("Handlers registered")
if st, err := b.statusNode.StatusService(); err == nil {
st.SetAccountManager(b.accountManager)
}
if st, err := b.statusNode.PeerService(); err == nil {
st.SetDiscoverer(b.StatusNode())
}
// Handle a case when a node is stopped and resumed.
// If there is no account selected, an error is returned.
if _, err := b.accountManager.SelectedChatAccount(); err == nil {
@ -796,7 +695,7 @@ func (b *GethStatusBackend) SendTransaction(sendArgs transactions.SendTxArgs, pa
return
}
go b.rpcFilters.TriggerTransactionSentToUpstreamEvent(hash)
go b.statusNode.RPCFiltersService().TriggerTransactionSentToUpstreamEvent(hash)
return
}
@ -807,7 +706,7 @@ func (b *GethStatusBackend) SendTransactionWithSignature(sendArgs transactions.S
return
}
go b.rpcFilters.TriggerTransactionSentToUpstreamEvent(hash)
go b.statusNode.RPCFiltersService().TriggerTransactionSentToUpstreamEvent(hash)
return
}
@ -964,10 +863,7 @@ func (b *GethStatusBackend) ConnectionChange(typ string, expensive bool) {
b.log.Info("Network state change", "old", b.connectionState, "new", state)
b.connectionState = state
err := b.statusNode.ConnectionChanged(state)
if err != nil {
b.log.Error("failed to notify of connection changed", "err", err)
}
b.statusNode.ConnectionChanged(state)
// logic of handling state changes here
// restart node? force peers reconnect? etc
@ -990,54 +886,18 @@ func (b *GethStatusBackend) AppStateChange(state string) {
}
func (b *GethStatusBackend) StopLocalNotifications() error {
localPN, err := b.statusNode.LocalNotificationsService()
if err != nil {
b.log.Error("Retrieving of LocalNotifications service failed on StopLocalNotifications", "error", err)
if b.statusNode == nil {
return nil
}
if localPN.IsStarted() {
err = localPN.Stop()
if err != nil {
b.log.Error("LocalNotifications service stop failed on StopLocalNotifications", "error", err)
return nil
}
}
return nil
return b.statusNode.StopLocalNotifications()
}
func (b *GethStatusBackend) StartLocalNotifications() error {
localPN, err := b.statusNode.LocalNotificationsService()
if err != nil {
b.log.Error("Retrieving of local notifications service failed on StartLocalNotifications", "error", err)
if b.statusNode == nil {
return nil
}
return b.statusNode.StartLocalNotifications()
wallet, err := b.statusNode.WalletService()
if err != nil {
b.log.Error("Retrieving of wallet service failed on StartLocalNotifications", "error", err)
return nil
}
if !localPN.IsStarted() {
err = localPN.Start(b.statusNode.Server())
if err != nil {
b.log.Error("LocalNotifications service start failed on StartLocalNotifications", "error", err)
return nil
}
}
err = localPN.SubscribeWallet(wallet.GetFeed())
if err != nil {
b.log.Error("LocalNotifications service could not subscribe to wallet on StartLocalNotifications", "error", err)
return nil
}
return nil
}
// Logout clears whisper identities.
@ -1061,34 +921,11 @@ func (b *GethStatusBackend) Logout() error {
// cleanupServices stops parts of services that doesn't managed by a node and removes injected data from services.
func (b *GethStatusBackend) cleanupServices() error {
wakuService, err := b.statusNode.WakuService()
switch err {
case node.ErrServiceUnknown: // Waku was never registered
case nil:
if err := wakuService.DeleteKeyPairs(); err != nil {
return fmt.Errorf("%s: %v", ErrWakuClearIdentitiesFailure, err)
}
b.selectedAccountKeyID = ""
default:
return err
b.selectedAccountKeyID = ""
if b.statusNode == nil {
return nil
}
if b.statusNode.Config().WalletConfig.Enabled {
wallet, err := b.statusNode.WalletService()
switch err {
case node.ErrServiceUnknown:
case nil:
if wallet.IsStarted() {
err = wallet.Stop()
if err != nil {
return err
}
}
default:
return err
}
}
return nil
return b.statusNode.Cleanup()
}
func (b *GethStatusBackend) closeAppDB() error {
@ -1135,9 +972,6 @@ func (b *GethStatusBackend) GetActiveAccount() (*multiaccounts.Account, error) {
return b.account, nil
}
func (b *GethStatusBackend) WakuExtService() (*wakuext.Service, error) {
return b.statusNode.WakuExtService()
}
func (b *GethStatusBackend) injectAccountsIntoServices() error {
chatAccount, err := b.accountManager.SelectedChatAccount()
@ -1152,11 +986,9 @@ func (b *GethStatusBackend) injectAccountsIntoServices() error {
return err
}
wakuService, err := b.statusNode.WakuService()
wakuService := b.statusNode.WakuService()
switch err {
case node.ErrServiceUnknown: // Waku was never registered
case nil:
if wakuService != nil {
if err := wakuService.DeleteKeyPairs(); err != nil { // err is not possible; method return value is incorrect
return err
}
@ -1164,29 +996,21 @@ func (b *GethStatusBackend) injectAccountsIntoServices() error {
if err != nil {
return ErrWakuIdentityInjectionFailure
}
default:
return err
}
st := b.statusNode.WakuExtService()
if wakuService != nil {
st, err := b.statusNode.WakuExtService()
if err != nil {
return err
if st != nil {
if err := st.InitProtocol(identity, b.appDB, b.multiaccountsDB, acc, logutils.ZapLogger()); err != nil {
return err
}
// Set initial connection state
st.ConnectionChanged(b.connectionState)
}
if err := st.InitProtocol(identity, b.appDB, b.multiaccountsDB, acc, logutils.ZapLogger()); err != nil {
return err
}
// Set initial connection state
st.ConnectionChanged(b.connectionState)
}
wakuV2Service, err := b.statusNode.WakuV2Service()
wakuV2Service := b.statusNode.WakuV2Service()
switch err {
case node.ErrServiceUnknown: // WakuV2 was never registered
case nil:
if wakuV2Service != nil {
if err := wakuV2Service.DeleteKeyPairs(); err != nil { // err is not possible; method return value is incorrect
return err
}
@ -1194,15 +1018,7 @@ func (b *GethStatusBackend) injectAccountsIntoServices() error {
if err != nil {
return ErrWakuIdentityInjectionFailure
}
default:
return err
}
if wakuV2Service != nil {
st, err := b.statusNode.WakuV2ExtService()
if err != nil {
return err
}
st := b.statusNode.WakuV2ExtService()
if err := st.InitProtocol(identity, b.appDB, b.multiaccountsDB, acc, logutils.ZapLogger()); err != nil {
return err
@ -1212,13 +1028,6 @@ func (b *GethStatusBackend) injectAccountsIntoServices() error {
return nil
}
func appendIf(condition bool, services []gethnode.ServiceConstructor, service gethnode.ServiceConstructor) []gethnode.ServiceConstructor {
if !condition {
return services
}
return append(services, service)
}
// ExtractGroupMembershipSignatures extract signatures from tuples of content/signature
func (b *GethStatusBackend) ExtractGroupMembershipSignatures(signaturePairs [][2]string) ([]string, error) {
return crypto.ExtractSignatures(signaturePairs)

View File

@ -140,13 +140,13 @@ func main() {
backend := api.NewGethStatusBackend()
err = ImportAccount(*seedPhrase, backend)
if err != nil {
logger.Error("failed", "err", err)
logger.Error("failed import account", "err", err)
return
}
wakuextservice, err := backend.WakuExtService()
if err != nil {
logger.Error("failed", "err", err)
wakuextservice := backend.StatusNode().WakuExtService()
if wakuextservice == nil {
logger.Error("wakuext not available")
return
}
@ -165,14 +165,14 @@ func main() {
for i := 0; i < *nAddedContacts; i++ {
key, err := crypto.GenerateKey()
if err != nil {
logger.Error("failed", err)
logger.Error("failed generate key", err)
return
}
keyString := common.PubkeyToHex(&key.PublicKey)
_, err = wakuext.AddContact(context.Background(), keyString)
if err != nil {
logger.Error("failed", "err", err)
logger.Error("failed Add contact", "err", err)
return
}
}
@ -253,6 +253,11 @@ func main() {
}
}
url := "enode://30211cbd81c25f07b03a0196d56e6ce4604bb13db773ff1c0ea2253547fafd6c06eae6ad3533e2ba39d59564cfbdbb5e2ce7c137a5ebb85e99dcfc7a75f99f55@23.236.58.92:443"
fmt.Println("UPDATING")
wakuext.UpdateMailservers([]string{url})
time.Sleep(10 * time.Second)
fmt.Println("UPDATED")
}
@ -427,21 +432,25 @@ func ImportAccount(seedPhrase string, backend *api.GethStatusBackend) error {
manager.InitKeystore("./tmp")
err := backend.OpenAccounts()
if err != nil {
logger.Error("failed open accounts", err)
return err
}
generator := manager.AccountsGenerator()
generatedAccountInfo, err := generator.ImportMnemonic(seedPhrase, "")
if err != nil {
logger.Error("import mnemonic", err)
return err
}
derivedAddresses, err := generator.DeriveAddresses(generatedAccountInfo.ID, paths)
if err != nil {
logger.Error("deriver addressess", err)
return err
}
_, err = generator.StoreDerivedAccounts(generatedAccountInfo.ID, "", paths)
if err != nil {
logger.Error("store addressess", err)
return err
}
@ -450,11 +459,13 @@ func ImportAccount(seedPhrase string, backend *api.GethStatusBackend) error {
}
settings, err := defaultSettings(generatedAccountInfo, derivedAddresses, &seedPhrase)
if err != nil {
logger.Error("default settings", err)
return err
}
nodeConfig, err := defaultNodeConfig(settings.InstallationID)
if err != nil {
logger.Error("node config", err)
return err
}
@ -477,8 +488,15 @@ func ImportAccount(seedPhrase string, backend *api.GethStatusBackend) error {
Path: pathDefaultChat,
}
fmt.Println(nodeConfig)
accounts := []accounts.Account{walletAccount, chatAccount}
return backend.StartNodeWithAccountAndConfig(account, "", *settings, nodeConfig, accounts)
err = backend.StartNodeWithAccountAndConfig(account, "", *settings, nodeConfig, accounts)
if err != nil {
logger.Error("start node", err)
return err
}
return nil
}
var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
@ -486,7 +504,7 @@ var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func buildMessage(chat *protocol.Chat, count int) *common.Message {
key, err := crypto.GenerateKey()
if err != nil {
logger.Error("failed", err)
logger.Error("failed build message", err)
return nil
}

5
cmd/populate-db/run.sh Normal file
View File

@ -0,0 +1,5 @@
#/bin/bash
go build -mod=vendor
rm ./tmp -rf
./populate-db --added-contacts 1 --contacts 2 --public-chats 1 --one-to-one-chats 4 --number-of-messages 2 --seed-phrase "wolf uncover ancient kiss deer blossom blind expose estate average cancel kiss"

View File

@ -3,8 +3,6 @@ package main
import (
"context"
"time"
"github.com/status-im/status-go/node"
)
func createContextFromTimeout(timeout int) (context.Context, context.CancelFunc) {
@ -14,41 +12,3 @@ func createContextFromTimeout(timeout int) (context.Context, context.CancelFunc)
return context.WithTimeout(context.Background(), time.Duration(timeout)*time.Minute)
}
// syncAndStopNode tries to sync the blockchain and stop the node.
// It returns an exit code (`0` if successful or `1` in case of error)
// that can be used in `os.Exit` to exit immediately when the function returns.
// The special exit code `-1` is used if execution was interrupted.
func syncAndStopNode(interruptCh <-chan struct{}, statusNode *node.StatusNode, timeout int) (exitCode int) {
logger.Info("syncAndStopNode: node will synchronize the chain and exit", "timeoutInMins", timeout)
ctx, cancel := createContextFromTimeout(timeout)
defer cancel()
doneSync := make(chan struct{})
errSync := make(chan error)
go func() {
if err := statusNode.EnsureSync(ctx); err != nil {
errSync <- err
}
close(doneSync)
}()
select {
case err := <-errSync:
logger.Error("syncAndStopNode: failed to sync the chain", "error", err)
exitCode = 1
case <-doneSync:
case <-interruptCh:
// cancel context and return immediately if interrupted
// `-1` is used as a special exit code to denote interruption
return -1
}
if err := statusNode.Stop(); err != nil {
logger.Error("syncAndStopNode: failed to stop the node", "error", err)
return 1
}
return
}

View File

@ -71,8 +71,6 @@ var (
// don't change the name of this flag, https://github.com/ethereum/go-ethereum/blob/master/metrics/metrics.go#L41
metricsEnabled = flag.Bool("metrics", false, "Expose ethereum metrics with debug_metrics jsonrpc call")
metricsPort = flag.Int("metrics-port", 9305, "Port for the Prometheus /metrics endpoint")
syncAndExit = flag.Int("sync-and-exit", -1, "Timeout in minutes for blockchain sync and exit, zero means no timeout unless sync is finished")
)
// All general log messages in this package should be routed through this logger.
@ -182,20 +180,6 @@ func main() {
profiling.NewProfiler(*pprofPort).Go()
}
// Sync blockchain and stop.
if *syncAndExit >= 0 {
exitCode := syncAndStopNode(interruptCh, backend.StatusNode(), *syncAndExit)
// Call was interrupted. Wait for graceful shutdown.
if exitCode == -1 {
if gethNode := backend.StatusNode().GethNode(); gethNode != nil {
gethNode.Wait()
}
return
}
// Otherwise, exit immediately with a returned exit code.
os.Exit(exitCode)
}
if config.PushNotificationServerConfig.Enabled {
if config.NodeKey == "" {
logger.Error("node key needs to be set if running a push notification server")
@ -227,7 +211,7 @@ func main() {
protocol.WithDatabase(db),
}
messenger, err := protocol.NewMessenger(identity, gethbridge.NewNodeBridge(backend.StatusNode().GethNode()), installationID.String(), options...)
messenger, err := protocol.NewMessenger(identity, gethbridge.NewNodeBridge(backend.StatusNode().GethNode(), backend.StatusNode().WakuService(), backend.StatusNode().WakuV2Service()), installationID.String(), options...)
if err != nil {
logger.Error("failed to create messenger", "error", err)
return

View File

@ -3,8 +3,6 @@ package main
import (
"context"
"time"
"github.com/status-im/status-go/node"
)
func createContextFromTimeout(timeout int) (context.Context, context.CancelFunc) {
@ -14,41 +12,3 @@ func createContextFromTimeout(timeout int) (context.Context, context.CancelFunc)
return context.WithTimeout(context.Background(), time.Duration(timeout)*time.Minute)
}
// syncAndStopNode tries to sync the blockchain and stop the node.
// It returns an exit code (`0` if successful or `1` in case of error)
// that can be used in `os.Exit` to exit immediately when the function returns.
// The special exit code `-1` is used if execution was interrupted.
func syncAndStopNode(interruptCh <-chan struct{}, statusNode *node.StatusNode, timeout int) (exitCode int) {
logger.Info("syncAndStopNode: node will synchronize the chain and exit", "timeoutInMins", timeout)
ctx, cancel := createContextFromTimeout(timeout)
defer cancel()
doneSync := make(chan struct{})
errSync := make(chan error)
go func() {
if err := statusNode.EnsureSync(ctx); err != nil {
errSync <- err
}
close(doneSync)
}()
select {
case err := <-errSync:
logger.Error("syncAndStopNode: failed to sync the chain", "error", err)
exitCode = 1
case <-doneSync:
case <-interruptCh:
// cancel context and return immediately if interrupted
// `-1` is used as a special exit code to denote interruption
return -1
}
if err := statusNode.Stop(); err != nil {
logger.Error("syncAndStopNode: failed to stop the node", "error", err)
return 1
}
return
}

View File

@ -0,0 +1,13 @@
package common
import (
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
)
type StatusService interface {
Start() error
Stop() error
Protocols() []p2p.Protocol
APIs() []rpc.API
}

View File

@ -0,0 +1,103 @@
package gethbridge
import (
"crypto/ecdsa"
"errors"
"strings"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/extkeys"
)
type gethKeyStoreAdapter struct {
keystore *keystore.KeyStore
}
// WrapKeyStore creates a types.KeyStore wrapper over a keystore.KeyStore object
func WrapKeyStore(keystore *keystore.KeyStore) types.KeyStore {
return &gethKeyStoreAdapter{keystore: keystore}
}
func (k *gethKeyStoreAdapter) ImportECDSA(priv *ecdsa.PrivateKey, passphrase string) (types.Account, error) {
gethAccount, err := k.keystore.ImportECDSA(priv, passphrase)
return accountFrom(gethAccount), err
}
func (k *gethKeyStoreAdapter) ImportSingleExtendedKey(extKey *extkeys.ExtendedKey, passphrase string) (types.Account, error) {
gethAccount, err := k.keystore.ImportSingleExtendedKey(extKey, passphrase)
return accountFrom(gethAccount), err
}
func (k *gethKeyStoreAdapter) ImportExtendedKeyForPurpose(keyPurpose extkeys.KeyPurpose, extKey *extkeys.ExtendedKey, passphrase string) (types.Account, error) {
gethAccount, err := k.keystore.ImportExtendedKeyForPurpose(keyPurpose, extKey, passphrase)
return accountFrom(gethAccount), err
}
func (k *gethKeyStoreAdapter) AccountDecryptedKey(a types.Account, auth string) (types.Account, *types.Key, error) {
gethAccount, err := gethAccountFrom(a)
if err != nil {
return types.Account{}, nil, err
}
var gethKey *keystore.Key
gethAccount, gethKey, err = k.keystore.AccountDecryptedKey(gethAccount, auth)
return accountFrom(gethAccount), keyFrom(gethKey), err
}
func (k *gethKeyStoreAdapter) Delete(a types.Account, auth string) error {
gethAccount, err := gethAccountFrom(a)
if err != nil {
return err
}
return k.keystore.Delete(gethAccount, auth)
}
// parseGethURL converts a user supplied URL into the accounts specific structure.
func parseGethURL(url string) (accounts.URL, error) {
parts := strings.Split(url, "://")
if len(parts) != 2 || parts[0] == "" {
return accounts.URL{}, errors.New("protocol scheme missing")
}
return accounts.URL{
Scheme: parts[0],
Path: parts[1],
}, nil
}
func gethAccountFrom(account types.Account) (accounts.Account, error) {
var (
gethAccount accounts.Account
err error
)
gethAccount.Address = common.Address(account.Address)
if account.URL != "" {
gethAccount.URL, err = parseGethURL(account.URL)
}
return gethAccount, err
}
func accountFrom(gethAccount accounts.Account) types.Account {
return types.Account{
Address: types.Address(gethAccount.Address),
URL: gethAccount.URL.String(),
}
}
func keyFrom(k *keystore.Key) *types.Key {
if k == nil {
return nil
}
return &types.Key{
ID: k.Id,
Address: types.Address(k.Address),
PrivateKey: k.PrivateKey,
ExtendedKey: k.ExtendedKey,
SubAccountIndex: k.SubAccountIndex,
}
}

View File

@ -18,10 +18,12 @@ import (
type gethNodeWrapper struct {
stack *node.Node
waku1 *waku.Waku
waku2 *wakuv2.Waku
}
func NewNodeBridge(stack *node.Node) types.Node {
return &gethNodeWrapper{stack: stack}
func NewNodeBridge(stack *node.Node, waku1 *waku.Waku, waku2 *wakuv2.Waku) types.Node {
return &gethNodeWrapper{stack: stack, waku1: waku1, waku2: waku2}
}
func (w *gethNodeWrapper) Poll() {
@ -32,50 +34,28 @@ func (w *gethNodeWrapper) NewENSVerifier(logger *zap.Logger) enstypes.ENSVerifie
return gethens.NewVerifier(logger)
}
func (w *gethNodeWrapper) SetWaku1(waku *waku.Waku) {
w.waku1 = waku
}
func (w *gethNodeWrapper) SetWaku2(waku *wakuv2.Waku) {
w.waku2 = waku
}
func (w *gethNodeWrapper) GetWaku(ctx interface{}) (types.Waku, error) {
var nativeWaku *waku.Waku
if ctx == nil || ctx == w {
err := w.stack.Service(&nativeWaku)
if err != nil {
return nil, err
}
} else {
switch serviceProvider := ctx.(type) {
case *node.ServiceContext:
err := serviceProvider.Service(&nativeWaku)
if err != nil {
return nil, err
}
}
}
if nativeWaku == nil {
if w.waku1 == nil {
return nil, errors.New("waku service is not available")
}
return NewGethWakuWrapper(nativeWaku), nil
return NewGethWakuWrapper(w.waku1), nil
}
func (w *gethNodeWrapper) GetWakuV2(ctx interface{}) (types.Waku, error) {
var nativeWaku *wakuv2.Waku
if ctx == nil || ctx == w {
err := w.stack.Service(&nativeWaku)
if err != nil {
return nil, err
}
} else {
switch serviceProvider := ctx.(type) {
case *node.ServiceContext:
err := serviceProvider.Service(&nativeWaku)
if err != nil {
return nil, err
}
}
}
if nativeWaku == nil {
if w.waku2 == nil {
return nil, errors.New("waku service is not available")
}
return NewGethWakuV2Wrapper(nativeWaku), nil
return NewGethWakuV2Wrapper(w.waku2), nil
}
func (w *gethNodeWrapper) AddPeer(url string) error {

View File

@ -12,7 +12,7 @@ import (
"encoding/json"
"fmt"
"github.com/pborman/uuid"
"github.com/google/uuid"
"golang.org/x/crypto/pbkdf2"
"golang.org/x/crypto/scrypt"
@ -107,8 +107,13 @@ func DecryptKey(keyjson []byte, auth string) (*types.Key, error) {
}
key := crypto.ToECDSAUnsafe(keyBytes)
id, err := uuid.FromBytes(keyId)
if err != nil {
return nil, err
}
return &types.Key{
ID: uuid.UUID(keyId),
ID: id,
Address: crypto.PubkeyToAddress(key.PublicKey),
PrivateKey: key,
ExtendedKey: extKey,
@ -156,7 +161,11 @@ func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byt
if keyProtected.Version != version {
return nil, nil, fmt.Errorf("Version not supported: %v", keyProtected.Version)
}
keyId = uuid.Parse(keyProtected.Id)
id, err := uuid.Parse(keyProtected.Id)
if err != nil {
return nil, nil, err
}
keyId = id[:]
plainText, err := DecryptDataV3(keyProtected.Crypto, auth)
if err != nil {
return nil, nil, err
@ -165,7 +174,12 @@ func decryptKeyV3(keyProtected *encryptedKeyJSONV3, auth string) (keyBytes []byt
}
func decryptKeyV1(keyProtected *encryptedKeyJSONV1, auth string) (keyBytes []byte, keyId []byte, err error) {
keyId = uuid.Parse(keyProtected.Id)
id, err := uuid.Parse(keyProtected.Id)
if err != nil {
return nil, nil, err
}
keyId = id[:]
mac, err := hex.DecodeString(keyProtected.Crypto.MAC)
if err != nil {
return nil, nil, err
@ -329,8 +343,7 @@ func pkcs7Unpad(in []byte) []byte {
return in[:len(in)-int(padding)]
}
func RawKeyToCryptoJSON(rawKeyFile []byte) (cj CryptoJSON, e error){
func RawKeyToCryptoJSON(rawKeyFile []byte) (cj CryptoJSON, e error) {
var keyJSON encryptedKeyJSONV3
if e := json.Unmarshal(rawKeyFile, &keyJSON); e != nil {
return cj, fmt.Errorf("failed to read key file: %s", e)

View File

@ -3,7 +3,7 @@ package types
import (
"crypto/ecdsa"
"github.com/pborman/uuid"
"github.com/google/uuid"
"github.com/status-im/status-go/extkeys"
)

View File

@ -516,27 +516,6 @@ func makeJSONResponse(err error) string {
return string(outBytes)
}
// GetNodesFromContract returns a list of nodes from a given contract
//export GetNodesFromContract
func GetNodesFromContract(rpcEndpoint string, contractAddress string) string {
nodes, err := statusBackend.GetNodesFromContract(
rpcEndpoint,
contractAddress,
)
if err != nil {
return makeJSONResponse(err)
}
data, err := json.Marshal(struct {
Nodes []string `json:"result"`
}{Nodes: nodes})
if err != nil {
return makeJSONResponse(err)
}
return string(data)
}
// AddPeer adds an enode as a peer.
func AddPeer(enode string) string {
err := statusBackend.StatusNode().AddPeer(enode)

View File

@ -1,7 +1,7 @@
package node
import (
"context"
"database/sql"
"errors"
"fmt"
"net"
@ -16,27 +16,36 @@ import (
"github.com/syndtr/goleveldb/leveldb"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/connection"
"github.com/status-im/status-go/db"
"github.com/status-im/status-go/discovery"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/peers"
"github.com/status-im/status-go/rpc"
accountssvc "github.com/status-im/status-go/services/accounts"
appmetricsservice "github.com/status-im/status-go/services/appmetrics"
"github.com/status-im/status-go/services/browsers"
localnotifications "github.com/status-im/status-go/services/local-notifications"
"github.com/status-im/status-go/services/mailservers"
"github.com/status-im/status-go/services/nodebridge"
"github.com/status-im/status-go/services/peer"
"github.com/status-im/status-go/services/permissions"
"github.com/status-im/status-go/services/status"
"github.com/status-im/status-go/services/personal"
"github.com/status-im/status-go/services/rpcfilters"
"github.com/status-im/status-go/services/rpcstats"
"github.com/status-im/status-go/services/subscriptions"
"github.com/status-im/status-go/services/wakuext"
"github.com/status-im/status-go/services/wakuv2ext"
"github.com/status-im/status-go/services/wallet"
"github.com/status-im/status-go/timesource"
"github.com/status-im/status-go/waku"
"github.com/status-im/status-go/wakuv2"
)
@ -59,6 +68,9 @@ var (
type StatusNode struct {
mu sync.RWMutex
appDB *sql.DB
multiaccountsDB *multiaccounts.Database
config *params.NodeConfig // Status node configuration
gethNode *node.Node // reference to Geth P2P stack/node
rpcClient *rpc.Client // reference to public RPC client
@ -70,12 +82,37 @@ type StatusNode struct {
db *leveldb.DB // used as a cache for PeerPool
log log.Logger
gethAccountManager *account.GethManager
accountsManager *accounts.Manager
// services
// Not sure whether we can use the one that has already be initalized above
rpcFiltersSrvc *rpcfilters.Service
subscriptionsSrvc *subscriptions.Service
rpcStatsSrvc *rpcstats.Service
accountsSrvc *accountssvc.Service
browsersSrvc *browsers.Service
nodeBridgeSrvc *nodebridge.NodeService
permissionsSrvc *permissions.Service
mailserversSrvc *mailservers.Service
appMetricsSrvc *appmetricsservice.Service
walletSrvc *wallet.Service
peerSrvc *peer.Service
localNotificationsSrvc *localnotifications.Service
personalSrvc *personal.Service
timeSourceSrvc *timesource.NTPTimeSource
wakuSrvc *waku.Waku
wakuExtSrvc *wakuext.Service
wakuV2Srvc *wakuv2.Waku
wakuV2ExtSrvc *wakuv2ext.Service
}
// New makes new instance of StatusNode.
func New() *StatusNode {
return &StatusNode{
log: log.New("package", "status-go/node.StatusNode"),
gethAccountManager: account.NewGethManager(),
log: log.New("package", "status-go/node.StatusNode"),
}
}
@ -109,9 +146,9 @@ func (n *StatusNode) Server() *p2p.Server {
// Start starts current StatusNode, failing if it's already started.
// It accepts a list of services that should be added to the node.
func (n *StatusNode) Start(config *params.NodeConfig, accs *accounts.Manager, services ...node.ServiceConstructor) error {
func (n *StatusNode) Start(config *params.NodeConfig, accs *accounts.Manager) error {
n.accountsManager = accs
return n.StartWithOptions(config, StartOptions{
Services: services,
StartDiscovery: true,
AccountsManager: accs,
})
@ -119,7 +156,6 @@ func (n *StatusNode) Start(config *params.NodeConfig, accs *accounts.Manager, se
// StartOptions allows to control some parameters of Start() method.
type StartOptions struct {
Services []node.ServiceConstructor
StartDiscovery bool
AccountsManager *accounts.Manager
}
@ -135,6 +171,8 @@ func (n *StatusNode) StartWithOptions(config *params.NodeConfig, options StartOp
return ErrNodeRunning
}
n.accountsManager = options.AccountsManager
n.log.Debug("starting with options", "ClusterConfig", config.ClusterConfig)
db, err := db.Create(config.DataDir, params.StatusDatabase)
@ -143,9 +181,11 @@ func (n *StatusNode) StartWithOptions(config *params.NodeConfig, options StartOp
}
n.db = db
n.log.Info("starting with db")
err = n.startWithDB(config, options.AccountsManager, db, options.Services)
err = n.startWithDB(config, options.AccountsManager, db)
n.log.Info("started with db")
// continue only if there was no error when starting node with a db
if err == nil && options.StartDiscovery && n.discoveryEnabled() {
err = n.startDiscovery()
@ -162,21 +202,22 @@ func (n *StatusNode) StartWithOptions(config *params.NodeConfig, options StartOp
return nil
}
func (n *StatusNode) startWithDB(config *params.NodeConfig, accs *accounts.Manager, db *leveldb.DB, services []node.ServiceConstructor) error {
func (n *StatusNode) startWithDB(config *params.NodeConfig, accs *accounts.Manager, db *leveldb.DB) error {
if err := n.createNode(config, accs, db); err != nil {
return err
}
n.config = config
n.log.Info("starting geth node")
if err := n.startGethNode(services); err != nil {
return err
}
n.log.Info("setting up rpc client")
if err := n.setupRPCClient(); err != nil {
return err
}
return nil
if err := n.initServices(config); err != nil {
return err
}
return n.startGethNode()
}
func (n *StatusNode) createNode(config *params.NodeConfig, accs *accounts.Manager, db *leveldb.DB) (err error) {
@ -185,19 +226,13 @@ func (n *StatusNode) createNode(config *params.NodeConfig, accs *accounts.Manage
}
// startGethNode starts current StatusNode, will fail if it's already started.
func (n *StatusNode) startGethNode(services []node.ServiceConstructor) error {
for _, service := range services {
if err := n.gethNode.Register(service); err != nil {
return err
}
}
func (n *StatusNode) startGethNode() error {
return n.gethNode.Start()
}
func (n *StatusNode) setupRPCClient() (err error) {
// setup public RPC client
gethNodeClient, err := n.gethNode.AttachPublic()
gethNodeClient, err := n.gethNode.Attach()
if err != nil {
return
}
@ -315,8 +350,6 @@ func (n *StatusNode) startDiscovery() error {
options.AllowStop = len(n.config.RegisterTopics) == 0
options.TrustedMailServers = parseNodesToNodeID(n.config.ClusterConfig.TrustedMailServers)
options.MailServerRegistryAddress = n.config.MailServerRegistryAddress
n.peerPool = peers.NewPeerPool(
n.discovery,
n.config.RequireTopics,
@ -355,7 +388,7 @@ func (n *StatusNode) stop() error {
n.discovery = nil
}
if err := n.gethNode.Stop(); err != nil {
if err := n.gethNode.Close(); err != nil {
return err
}
@ -520,163 +553,12 @@ func (n *StatusNode) PeerCount() int {
return n.gethNode.Server().PeerCount()
}
// gethService is a wrapper for gethNode.Service which retrieves a currently
// running service registered of a specific type.
func (n *StatusNode) gethService(serviceInstance interface{}) error {
if !n.isRunning() {
return ErrNoRunningNode
func (n *StatusNode) ConnectionChanged(state connection.State) {
if n.wakuExtSrvc == nil {
return
}
if err := n.gethNode.Service(serviceInstance); err != nil {
return err
}
return nil
}
// LightEthereumService exposes reference to LES service running on top of the node
func (n *StatusNode) LightEthereumService() (l *les.LightEthereum, err error) {
n.mu.RLock()
defer n.mu.RUnlock()
err = n.gethService(&l)
if err == node.ErrServiceUnknown {
err = ErrServiceUnknown
}
return
}
// StatusService exposes reference to status service running on top of the node
func (n *StatusNode) StatusService() (st *status.Service, err error) {
n.mu.RLock()
defer n.mu.RUnlock()
err = n.gethService(&st)
if err == node.ErrServiceUnknown {
err = ErrServiceUnknown
}
return
}
// PeerService exposes reference to peer service running on top of the node.
func (n *StatusNode) PeerService() (st *peer.Service, err error) {
n.mu.RLock()
defer n.mu.RUnlock()
err = n.gethService(&st)
if err == node.ErrServiceUnknown {
err = ErrServiceUnknown
}
return
}
// WakuService exposes reference to Waku service running on top of the node
func (n *StatusNode) WakuService() (w *waku.Waku, err error) {
n.mu.RLock()
defer n.mu.RUnlock()
err = n.gethService(&w)
if err == node.ErrServiceUnknown {
err = ErrServiceUnknown
}
return
}
// WakuV2Service exposes reference to Whisper service running on top of the node
func (n *StatusNode) WakuV2Service() (w *wakuv2.Waku, err error) {
n.mu.RLock()
defer n.mu.RUnlock()
err = n.gethService(&w)
if err == node.ErrServiceUnknown {
err = ErrServiceUnknown
}
return
}
// WakuExtService exposes reference to shh extension service running on top of the node
func (n *StatusNode) WakuExtService() (s *wakuext.Service, err error) {
n.mu.RLock()
defer n.mu.RUnlock()
err = n.gethService(&s)
if err == node.ErrServiceUnknown {
err = ErrServiceUnknown
}
return
}
func (n *StatusNode) ConnectionChanged(state connection.State) error {
service, err := n.WakuExtService()
if err != nil {
return err
}
service.ConnectionChanged(state)
return nil
}
// WakuV2ExtService exposes reference to waku v2 extension service running on top of the node
func (n *StatusNode) WakuV2ExtService() (s *wakuv2ext.Service, err error) {
n.mu.RLock()
defer n.mu.RUnlock()
err = n.gethService(&s)
if err == node.ErrServiceUnknown {
err = ErrServiceUnknown
}
return
}
// WalletService returns wallet.Service instance if it was started.
func (n *StatusNode) WalletService() (s *wallet.Service, err error) {
n.mu.RLock()
defer n.mu.RUnlock()
err = n.gethService(&s)
if err == node.ErrServiceUnknown {
err = ErrServiceUnknown
}
return
}
// LocalNotificationsService returns localnotifications.Service instance if it was started.
func (n *StatusNode) LocalNotificationsService() (s *localnotifications.Service, err error) {
n.mu.RLock()
defer n.mu.RUnlock()
err = n.gethService(&s)
if err == node.ErrServiceUnknown {
err = ErrServiceUnknown
}
return
}
// BrowsersService returns browsers.Service instance if it was started.
func (n *StatusNode) BrowsersService() (s *browsers.Service, err error) {
n.mu.RLock()
defer n.mu.RUnlock()
err = n.gethService(&s)
if err == node.ErrServiceUnknown {
err = ErrServiceUnknown
}
return
}
// PermissionsService returns browsers.Service instance if it was started.
func (n *StatusNode) PermissionsService() (s *permissions.Service, err error) {
n.mu.RLock()
defer n.mu.RUnlock()
err = n.gethService(&s)
if err == node.ErrServiceUnknown {
err = ErrServiceUnknown
}
return
n.wakuExtSrvc.ConnectionChanged(state)
}
// AccountManager exposes reference to node's accounts manager
@ -735,67 +617,6 @@ func (n *StatusNode) ChaosModeCheckRPCClientsUpstreamURL(on bool) error {
return nil
}
// EnsureSync waits until blockchain synchronization
// is complete and returns.
func (n *StatusNode) EnsureSync(ctx context.Context) error {
// Don't wait for any blockchain sync for the
// local private chain as blocks are never mined.
if n.config.NetworkID == 0 || n.config.NetworkID == params.StatusChainNetworkID {
return nil
}
return n.ensureSync(ctx)
}
func (n *StatusNode) ensureSync(ctx context.Context) error {
les, err := n.LightEthereumService()
if err != nil {
return fmt.Errorf("failed to get LES service: %v", err)
}
downloader := les.Downloader()
if downloader == nil {
return errors.New("LightEthereumService downloader is nil")
}
progress := downloader.Progress()
if n.PeerCount() > 0 && progress.CurrentBlock >= progress.HighestBlock {
n.log.Debug("Synchronization completed", "current block", progress.CurrentBlock, "highest block", progress.HighestBlock)
return nil
}
ticker := time.NewTicker(tickerResolution)
defer ticker.Stop()
progressTicker := time.NewTicker(time.Minute)
defer progressTicker.Stop()
for {
select {
case <-ctx.Done():
return errors.New("timeout during node synchronization")
case <-ticker.C:
if n.PeerCount() == 0 {
n.log.Debug("No established connections with any peers, continue waiting for a sync")
continue
}
if downloader.Synchronising() {
n.log.Debug("Synchronization is in progress")
continue
}
progress = downloader.Progress()
if progress.CurrentBlock >= progress.HighestBlock {
n.log.Info("Synchronization completed", "current block", progress.CurrentBlock, "highest block", progress.HighestBlock)
return nil
}
n.log.Debug("Synchronization is not finished", "current", progress.CurrentBlock, "highest", progress.HighestBlock)
case <-progressTicker.C:
progress = downloader.Progress()
n.log.Warn("Synchronization is not finished", "current", progress.CurrentBlock, "highest", progress.HighestBlock)
}
}
}
// Discover sets up the discovery for a specific topic.
func (n *StatusNode) Discover(topic string, max, min int) (err error) {
if n.peerPool == nil {
@ -806,3 +627,11 @@ func (n *StatusNode) Discover(topic string, max, min int) (err error) {
Min: min,
})
}
func (n *StatusNode) SetAppDB(db *sql.DB) {
n.appDB = db
}
func (n *StatusNode) SetMultiaccountsDB(db *multiaccounts.Database) {
n.multiaccountsDB = db
}

View File

@ -6,17 +6,11 @@ import (
"fmt"
"os"
"path/filepath"
"time"
logging "github.com/ipfs/go-log"
"github.com/syndtr/goleveldb/leveldb"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/les"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
@ -24,22 +18,9 @@ import (
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/nat"
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/logutils"
"github.com/status-im/status-go/mailserver"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/services/ext"
"github.com/status-im/status-go/services/nodebridge"
"github.com/status-im/status-go/services/peer"
"github.com/status-im/status-go/services/personal"
"github.com/status-im/status-go/services/wakuext"
"github.com/status-im/status-go/services/wakuv2ext"
"github.com/status-im/status-go/static"
"github.com/status-im/status-go/timesource"
"github.com/status-im/status-go/waku"
wakucommon "github.com/status-im/status-go/waku/common"
"github.com/status-im/status-go/wakuv2"
)
// Errors related to node and services creation.
@ -83,77 +64,9 @@ func MakeNode(config *params.NodeConfig, accs *accounts.Manager, db *leveldb.DB)
return nil, fmt.Errorf(ErrNodeMakeFailureFormat, err.Error())
}
err = activateServices(stack, config, accs, db)
if err != nil {
return nil, err
}
return stack, nil
}
func activateServices(stack *node.Node, config *params.NodeConfig, accs *accounts.Manager, db *leveldb.DB) error {
if config.EnableNTPSync {
err := stack.Register(func(*node.ServiceContext) (node.Service, error) {
return timesource.Default(), nil
})
if err != nil {
return fmt.Errorf("failed to register NTP time source: %v", err)
}
}
// start Ethereum service if we are not expected to use an upstream server
if !config.UpstreamConfig.Enabled {
if err := activateLightEthService(stack, accs, config); err != nil {
return fmt.Errorf("%v: %v", ErrLightEthRegistrationFailure, err)
}
} else {
if config.LightEthConfig.Enabled {
return ErrLightEthRegistrationFailureUpstreamEnabled
}
logger.Info("LES protocol is disabled")
// `personal_sign` and `personal_ecRecover` methods are important to
// keep DApps working.
// Usually, they are provided by an ETH or a LES service, but when using
// upstream, we don't start any of these, so we need to start our own
// implementation.
if err := activatePersonalService(stack, accs, config); err != nil {
return fmt.Errorf("%v: %v", ErrPersonalServiceRegistrationFailure, err)
}
}
if err := activateNodeServices(stack, config, db); err != nil {
return err
}
return nil
}
func activateNodeServices(stack *node.Node, config *params.NodeConfig, db *leveldb.DB) error {
// Register eth-node node bridge
err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
return &nodebridge.NodeService{Node: gethbridge.NewNodeBridge(stack)}, nil
})
if err != nil {
return fmt.Errorf("failed to register NodeBridge: %v", err)
}
// start Waku service
if err := activateWakuService(stack, config, db); err != nil {
return fmt.Errorf("%v: %v", ErrWakuServiceRegistrationFailure, err)
}
if err := activateWakuV2Service(stack, config, db); err != nil {
return fmt.Errorf("%v: %v", ErrWakuV2ServiceRegistrationFailure, err)
}
// start peer service
if err := activatePeerService(stack); err != nil {
return fmt.Errorf("%v: %v", ErrPeerServiceRegistrationFailure, err)
}
return nil
}
// newGethNodeConfig returns default stack configuration for mobile client node
func newGethNodeConfig(config *params.NodeConfig) (*node.Config, error) {
nc := &node.Config{
@ -164,11 +77,12 @@ func newGethNodeConfig(config *params.NodeConfig) (*node.Config, error) {
Name: config.Name,
Version: config.Version,
P2P: p2p.Config{
NoDiscovery: true, // we always use only v5 server
ListenAddr: config.ListenAddr,
NAT: nat.Any(),
MaxPeers: config.MaxPeers,
MaxPendingPeers: config.MaxPendingPeers,
NoDiscovery: true, // we always use only v5 server
ListenAddr: config.ListenAddr,
NAT: nat.Any(),
// FIX ME: don't hardcode
MaxPeers: 200,
MaxPendingPeers: 200,
},
HTTPModules: config.FormatAPIModules(),
}
@ -214,7 +128,7 @@ func calculateGenesis(networkID uint64) (*core.Genesis, error) {
case params.MainNetworkID:
genesis = core.DefaultGenesisBlock()
case params.RopstenNetworkID:
genesis = core.DefaultTestnetGenesisBlock()
genesis = core.DefaultRopstenGenesisBlock()
case params.RinkebyNetworkID:
genesis = core.DefaultRinkebyGenesisBlock()
case params.GoerliNetworkID:
@ -246,191 +160,6 @@ func defaultStatusChainGenesisBlock() (*core.Genesis, error) {
return genesis, nil
}
// activateLightEthService configures and registers the eth.Ethereum service with a given node.
func activateLightEthService(stack *node.Node, accs *accounts.Manager, config *params.NodeConfig) error {
if !config.LightEthConfig.Enabled {
logger.Info("LES protocol is disabled")
return nil
}
genesis, err := calculateGenesis(config.NetworkID)
if err != nil {
return err
}
ethConf := eth.DefaultConfig
ethConf.Genesis = genesis
ethConf.SyncMode = downloader.LightSync
ethConf.NetworkId = config.NetworkID
ethConf.DatabaseCache = config.LightEthConfig.DatabaseCache
return stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
// NOTE(dshulyak) here we set our instance of the accounts manager.
// without sharing same instance selected account won't be visible for personal_* methods.
nctx := &node.ServiceContext{}
*nctx = *ctx
nctx.AccountManager = accs
return les.New(nctx, &ethConf)
})
}
func activatePersonalService(stack *node.Node, accs *accounts.Manager, config *params.NodeConfig) error {
return stack.Register(func(*node.ServiceContext) (node.Service, error) {
svc := personal.New(accs)
return svc, nil
})
}
func activatePeerService(stack *node.Node) error {
return stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
svc := peer.New()
return svc, nil
})
}
func registerWakuMailServer(wakuService *waku.Waku, config *params.WakuConfig) (err error) {
var mailServer mailserver.WakuMailServer
wakuService.RegisterMailServer(&mailServer)
return mailServer.Init(wakuService, config)
}
// activateWakuService configures Waku and adds it to the given node.
func activateWakuService(stack *node.Node, config *params.NodeConfig, db *leveldb.DB) (err error) {
if !config.WakuConfig.Enabled {
logger.Info("Waku protocol is disabled")
return nil
}
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
return createWakuService(ctx, &config.WakuConfig, &config.ClusterConfig)
})
if err != nil {
return
}
// Register Whisper eth-node bridge
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
var ethnode *nodebridge.NodeService
if err := ctx.Service(&ethnode); err != nil {
return nil, err
}
w, err := ethnode.Node.GetWaku(ctx)
if err != nil {
return nil, err
}
return &nodebridge.WakuService{Waku: w}, nil
})
if err != nil {
return
}
// TODO(dshulyak) add a config option to enable it by default, but disable if app is started from statusd
return stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
var ethnode *nodebridge.NodeService
if err := ctx.Service(&ethnode); err != nil {
return nil, err
}
return wakuext.New(config.ShhextConfig, ethnode.Node, ctx, ext.EnvelopeSignalHandler{}, db), nil
})
}
// activateWakuV2Service configures WakuV2 and adds it to the given node.
func activateWakuV2Service(stack *node.Node, config *params.NodeConfig, db *leveldb.DB) (err error) {
if !config.WakuV2Config.Enabled {
logger.Info("WakuV2 protocol is disabled")
return nil
}
err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
return createWakuV2Service(ctx, config.NodeKey, &config.WakuV2Config, &config.ClusterConfig)
})
if err != nil {
return
}
return stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
var ethnode *nodebridge.NodeService
if err := ctx.Service(&ethnode); err != nil {
return nil, err
}
return wakuv2ext.New(config.ShhextConfig, ethnode.Node, ctx, ext.EnvelopeSignalHandler{}, db), nil
})
}
func createWakuService(ctx *node.ServiceContext, wakuCfg *params.WakuConfig, clusterCfg *params.ClusterConfig) (*waku.Waku, error) {
cfg := &waku.Config{
MaxMessageSize: wakucommon.DefaultMaxMessageSize,
BloomFilterMode: wakuCfg.BloomFilterMode,
FullNode: wakuCfg.FullNode,
SoftBlacklistedPeerIDs: wakuCfg.SoftBlacklistedPeerIDs,
MinimumAcceptedPoW: params.WakuMinimumPoW,
EnableConfirmations: wakuCfg.EnableConfirmations,
}
if wakuCfg.MaxMessageSize > 0 {
cfg.MaxMessageSize = wakuCfg.MaxMessageSize
}
if wakuCfg.MinimumPoW > 0 {
cfg.MinimumAcceptedPoW = wakuCfg.MinimumPoW
}
w := waku.New(cfg, logutils.ZapLogger())
if wakuCfg.EnableRateLimiter {
r := wakuRateLimiter(wakuCfg, clusterCfg)
w.RegisterRateLimiter(r)
}
if timesource, err := timeSource(ctx); err == nil {
w.SetTimeSource(timesource)
}
// enable mail service
if wakuCfg.EnableMailServer {
if err := registerWakuMailServer(w, wakuCfg); err != nil {
return nil, fmt.Errorf("failed to register WakuMailServer: %v", err)
}
}
if wakuCfg.LightClient {
emptyBloomFilter := make([]byte, 64)
if err := w.SetBloomFilter(emptyBloomFilter); err != nil {
return nil, err
}
}
return w, nil
}
func createWakuV2Service(ctx *node.ServiceContext, nodeKey string, wakuCfg *params.WakuV2Config, clusterCfg *params.ClusterConfig) (*wakuv2.Waku, error) {
cfg := &wakuv2.Config{
MaxMessageSize: wakucommon.DefaultMaxMessageSize,
SoftBlacklistedPeerIDs: wakuCfg.SoftBlacklistedPeerIDs,
Host: wakuCfg.Host,
Port: wakuCfg.Port,
BootNodes: clusterCfg.WakuNodes,
StoreNodes: clusterCfg.WakuStoreNodes,
}
if wakuCfg.MaxMessageSize > 0 {
cfg.MaxMessageSize = wakuCfg.MaxMessageSize
}
lvl, err := logging.LevelFromString("info")
if err != nil {
panic(err)
}
logging.SetAllLoggers(lvl)
w, err := wakuv2.New(nodeKey, cfg, logutils.ZapLogger())
if err != nil {
return nil, err
}
return w, nil
}
// parseNodes creates list of enode.Node out of enode strings.
func parseNodes(enodes []string) []*enode.Node {
var nodes []*enode.Node
@ -469,6 +198,7 @@ func parseNodesToNodeID(enodes []string) []enode.ID {
return nodeIDs
}
/*
// timeSource get timeSource to be used by whisper
func timeSource(ctx *node.ServiceContext) (func() time.Time, error) {
var timeSource *timesource.NTPTimeSource
@ -476,33 +206,4 @@ func timeSource(ctx *node.ServiceContext) (func() time.Time, error) {
return nil, err
}
return timeSource.Now, nil
}
func wakuRateLimiter(wakuCfg *params.WakuConfig, clusterCfg *params.ClusterConfig) *wakucommon.PeerRateLimiter {
enodes := append(
parseNodes(clusterCfg.StaticNodes),
parseNodes(clusterCfg.TrustedMailServers)...,
)
var (
ips []string
peerIDs []enode.ID
)
for _, item := range enodes {
ips = append(ips, item.IP().String())
peerIDs = append(peerIDs, item.ID())
}
return wakucommon.NewPeerRateLimiter(
&wakucommon.PeerRateLimiterConfig{
PacketLimitPerSecIP: wakuCfg.PacketRateLimitIP,
PacketLimitPerSecPeerID: wakuCfg.PacketRateLimitPeerID,
BytesLimitPerSecIP: wakuCfg.BytesRateLimitIP,
BytesLimitPerSecPeerID: wakuCfg.BytesRateLimitPeerID,
WhitelistedIPs: ips,
WhitelistedPeerIDs: peerIDs,
},
&wakucommon.MetricsRateLimiterHandler{},
&wakucommon.DropPeerRateLimiterHandler{
Tolerance: wakuCfg.RateLimitTolerance,
},
)
}
}*/

View File

@ -0,0 +1,472 @@
package node
import (
"errors"
"fmt"
logging "github.com/ipfs/go-log"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/status-im/status-go/appmetrics"
"github.com/status-im/status-go/common"
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/logutils"
"github.com/status-im/status-go/mailserver"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc"
accountssvc "github.com/status-im/status-go/services/accounts"
appmetricsservice "github.com/status-im/status-go/services/appmetrics"
"github.com/status-im/status-go/services/browsers"
"github.com/status-im/status-go/services/ext"
localnotifications "github.com/status-im/status-go/services/local-notifications"
"github.com/status-im/status-go/services/mailservers"
"github.com/status-im/status-go/services/nodebridge"
"github.com/status-im/status-go/services/peer"
"github.com/status-im/status-go/services/permissions"
"github.com/status-im/status-go/services/personal"
"github.com/status-im/status-go/services/rpcfilters"
"github.com/status-im/status-go/services/rpcstats"
"github.com/status-im/status-go/services/subscriptions"
"github.com/status-im/status-go/services/wakuext"
"github.com/status-im/status-go/services/wakuv2ext"
"github.com/status-im/status-go/services/wallet"
"github.com/status-im/status-go/timesource"
"github.com/status-im/status-go/waku"
wakucommon "github.com/status-im/status-go/waku/common"
"github.com/status-im/status-go/wakuv2"
)
var (
// ErrWakuClearIdentitiesFailure clearing whisper identities has failed.
ErrWakuClearIdentitiesFailure = errors.New("failed to clear waku identities")
)
func (b *StatusNode) initServices(config *params.NodeConfig) error {
accountsFeed := &event.Feed{}
services := []common.StatusService{}
services = appendIf(config.UpstreamConfig.Enabled, services, b.rpcFiltersService())
services = append(services, b.subscriptionService())
services = append(services, b.rpcStatsService())
services = append(services, b.appmetricsService())
services = append(services, b.peerService())
services = append(services, b.personalService())
services = appendIf(config.EnableNTPSync, services, b.timeSource())
services = appendIf(b.appDB != nil && b.multiaccountsDB != nil, services, b.accountsService(accountsFeed))
services = appendIf(config.BrowsersConfig.Enabled, services, b.browsersService())
services = appendIf(config.PermissionsConfig.Enabled, services, b.permissionsService())
services = appendIf(config.MailserversConfig.Enabled, services, b.mailserversService())
if config.WakuConfig.Enabled {
wakuService, err := b.wakuService(&config.WakuConfig, &config.ClusterConfig)
if err != nil {
return err
}
services = append(services, wakuService)
wakuext, err := b.wakuExtService(config)
if err != nil {
return err
}
b.wakuExtSrvc = wakuext
services = append(services, wakuext)
}
if config.WakuV2Config.Enabled {
waku2Service, err := b.wakuV2Service(config.NodeKey, &config.WakuV2Config, &config.ClusterConfig)
if err != nil {
return err
}
services = append(services, waku2Service)
wakuext, err := b.wakuV2ExtService(config)
if err != nil {
return err
}
b.wakuV2ExtSrvc = wakuext
services = append(services, wakuext)
}
b.log.Info("WAKU ENABLED")
if config.WalletConfig.Enabled {
walletService := b.walletService(config.NetworkID, accountsFeed)
b.log.Info("SETTING REPC CLIETN")
b.walletSrvc.SetClient(b.rpcClient.Ethclient())
b.log.Info("SET REPC CLIETN")
services = append(services, walletService)
}
b.log.Info("WALLET ENABLED")
// We ignore for now local notifications flag as users who are upgrading have no mean to enable it
services = append(services, b.localNotificationsService(config.NetworkID))
b.log.Info("SET CLIENT")
b.peerSrvc.SetDiscoverer(b)
b.log.Info("SET DISCOVERER")
for i := range services {
b.gethNode.RegisterAPIs(services[i].APIs())
b.gethNode.RegisterProtocols(services[i].Protocols())
b.gethNode.RegisterLifecycle(services[i])
}
return nil
}
func (b *StatusNode) nodeBridge() types.Node {
return gethbridge.NewNodeBridge(b.gethNode, b.wakuSrvc, b.wakuV2Srvc)
}
func (b *StatusNode) nodeBridgeService() *nodebridge.NodeService {
if b.nodeBridgeSrvc == nil {
b.nodeBridgeSrvc = &nodebridge.NodeService{Node: b.nodeBridge()}
}
return b.nodeBridgeSrvc
}
func (b *StatusNode) wakuExtService(config *params.NodeConfig) (*wakuext.Service, error) {
if b.gethNode == nil {
return nil, errors.New("geth node not initialized")
}
if b.wakuExtSrvc == nil {
b.wakuExtSrvc = wakuext.New(config.ShhextConfig, b.nodeBridge(), ext.EnvelopeSignalHandler{}, b.db)
}
b.wakuExtSrvc.SetP2PServer(b.gethNode.Server())
return b.wakuExtSrvc, nil
}
func (b *StatusNode) wakuV2ExtService(config *params.NodeConfig) (*wakuv2ext.Service, error) {
if b.gethNode == nil {
return nil, errors.New("geth node not initialized")
}
if b.wakuV2ExtSrvc == nil {
b.wakuV2ExtSrvc = wakuv2ext.New(config.ShhextConfig, b.nodeBridge(), ext.EnvelopeSignalHandler{}, b.db)
}
b.wakuV2ExtSrvc.SetP2PServer(b.gethNode.Server())
return b.wakuV2ExtSrvc, nil
}
func (b *StatusNode) WakuService() *waku.Waku {
return b.wakuSrvc
}
func (b *StatusNode) WakuExtService() *wakuext.Service {
return b.wakuExtSrvc
}
func (b *StatusNode) WakuV2ExtService() *wakuv2ext.Service {
return b.wakuV2ExtSrvc
}
func (b *StatusNode) WakuV2Service() *wakuv2.Waku {
return b.wakuV2Srvc
}
func (b *StatusNode) wakuService(wakuCfg *params.WakuConfig, clusterCfg *params.ClusterConfig) (*waku.Waku, error) {
if b.wakuSrvc == nil {
cfg := &waku.Config{
MaxMessageSize: wakucommon.DefaultMaxMessageSize,
BloomFilterMode: wakuCfg.BloomFilterMode,
FullNode: wakuCfg.FullNode,
SoftBlacklistedPeerIDs: wakuCfg.SoftBlacklistedPeerIDs,
MinimumAcceptedPoW: params.WakuMinimumPoW,
EnableConfirmations: wakuCfg.EnableConfirmations,
}
if wakuCfg.MaxMessageSize > 0 {
cfg.MaxMessageSize = wakuCfg.MaxMessageSize
}
if wakuCfg.MinimumPoW > 0 {
cfg.MinimumAcceptedPoW = wakuCfg.MinimumPoW
}
w := waku.New(cfg, logutils.ZapLogger())
if wakuCfg.EnableRateLimiter {
r := wakuRateLimiter(wakuCfg, clusterCfg)
w.RegisterRateLimiter(r)
}
if timesource := b.timeSource(); timesource != nil {
w.SetTimeSource(timesource.Now)
}
// enable mail service
if wakuCfg.EnableMailServer {
if err := registerWakuMailServer(w, wakuCfg); err != nil {
return nil, fmt.Errorf("failed to register WakuMailServer: %v", err)
}
}
if wakuCfg.LightClient {
emptyBloomFilter := make([]byte, 64)
if err := w.SetBloomFilter(emptyBloomFilter); err != nil {
return nil, err
}
}
b.wakuSrvc = w
}
return b.wakuSrvc, nil
}
func (b *StatusNode) wakuV2Service(nodeKey string, wakuCfg *params.WakuV2Config, clusterCfg *params.ClusterConfig) (*wakuv2.Waku, error) {
if b.wakuV2Srvc == nil {
cfg := &wakuv2.Config{
MaxMessageSize: wakucommon.DefaultMaxMessageSize,
SoftBlacklistedPeerIDs: wakuCfg.SoftBlacklistedPeerIDs,
Host: wakuCfg.Host,
Port: wakuCfg.Port,
BootNodes: clusterCfg.WakuNodes,
StoreNodes: clusterCfg.WakuStoreNodes,
}
if wakuCfg.MaxMessageSize > 0 {
cfg.MaxMessageSize = wakuCfg.MaxMessageSize
}
lvl, err := logging.LevelFromString("info")
if err != nil {
panic(err)
}
logging.SetAllLoggers(lvl)
w, err := wakuv2.New(nodeKey, cfg, logutils.ZapLogger())
if err != nil {
return nil, err
}
b.wakuV2Srvc = w
}
return b.wakuV2Srvc, nil
}
func wakuRateLimiter(wakuCfg *params.WakuConfig, clusterCfg *params.ClusterConfig) *wakucommon.PeerRateLimiter {
enodes := append(
parseNodes(clusterCfg.StaticNodes),
parseNodes(clusterCfg.TrustedMailServers)...,
)
var (
ips []string
peerIDs []enode.ID
)
for _, item := range enodes {
ips = append(ips, item.IP().String())
peerIDs = append(peerIDs, item.ID())
}
return wakucommon.NewPeerRateLimiter(
&wakucommon.PeerRateLimiterConfig{
PacketLimitPerSecIP: wakuCfg.PacketRateLimitIP,
PacketLimitPerSecPeerID: wakuCfg.PacketRateLimitPeerID,
BytesLimitPerSecIP: wakuCfg.BytesRateLimitIP,
BytesLimitPerSecPeerID: wakuCfg.BytesRateLimitPeerID,
WhitelistedIPs: ips,
WhitelistedPeerIDs: peerIDs,
},
&wakucommon.MetricsRateLimiterHandler{},
&wakucommon.DropPeerRateLimiterHandler{
Tolerance: wakuCfg.RateLimitTolerance,
},
)
}
func (b *StatusNode) rpcFiltersService() *rpcfilters.Service {
if b.rpcFiltersSrvc == nil {
b.rpcFiltersSrvc = rpcfilters.New(b)
}
return b.rpcFiltersSrvc
}
func (b *StatusNode) subscriptionService() *subscriptions.Service {
if b.subscriptionsSrvc == nil {
b.subscriptionsSrvc = subscriptions.New(func() *rpc.Client { return b.RPCPrivateClient() })
}
return b.subscriptionsSrvc
}
func (b *StatusNode) rpcStatsService() *rpcstats.Service {
if b.rpcStatsSrvc == nil {
b.rpcStatsSrvc = rpcstats.New()
}
return b.rpcStatsSrvc
}
func (b *StatusNode) accountsService(accountsFeed *event.Feed) *accountssvc.Service {
if b.accountsSrvc == nil {
b.accountsSrvc = accountssvc.NewService(accounts.NewDB(b.appDB), b.multiaccountsDB, b.gethAccountManager.Manager, accountsFeed)
}
return b.accountsSrvc
}
func (b *StatusNode) browsersService() *browsers.Service {
if b.browsersSrvc == nil {
b.browsersSrvc = browsers.NewService(browsers.NewDB(b.appDB))
}
return b.browsersSrvc
}
func (b *StatusNode) permissionsService() *permissions.Service {
if b.permissionsSrvc == nil {
b.permissionsSrvc = permissions.NewService(permissions.NewDB(b.appDB))
}
return b.permissionsSrvc
}
func (b *StatusNode) mailserversService() *mailservers.Service {
if b.mailserversSrvc == nil {
b.mailserversSrvc = mailservers.NewService(mailservers.NewDB(b.appDB))
}
return b.mailserversSrvc
}
func (b *StatusNode) appmetricsService() common.StatusService {
if b.appMetricsSrvc == nil {
b.appMetricsSrvc = appmetricsservice.NewService(appmetrics.NewDB(b.appDB))
}
return b.appMetricsSrvc
}
func (b *StatusNode) walletService(network uint64, accountsFeed *event.Feed) common.StatusService {
if b.walletSrvc == nil {
b.walletSrvc = wallet.NewService(wallet.NewDB(b.appDB, network), accountsFeed)
}
return b.walletSrvc
}
func (b *StatusNode) localNotificationsService(network uint64) *localnotifications.Service {
if b.localNotificationsSrvc == nil {
b.localNotificationsSrvc = localnotifications.NewService(b.appDB, network)
}
return b.localNotificationsSrvc
}
func (b *StatusNode) peerService() *peer.Service {
if b.peerSrvc == nil {
b.peerSrvc = peer.New()
}
return b.peerSrvc
}
func registerWakuMailServer(wakuService *waku.Waku, config *params.WakuConfig) (err error) {
var mailServer mailserver.WakuMailServer
wakuService.RegisterMailServer(&mailServer)
return mailServer.Init(wakuService, config)
}
func appendIf(condition bool, services []common.StatusService, service common.StatusService) []common.StatusService {
if !condition {
return services
}
return append(services, service)
}
func (b *StatusNode) RPCFiltersService() *rpcfilters.Service {
return b.rpcFiltersSrvc
}
func (b *StatusNode) StopLocalNotifications() error {
if b.localNotificationsSrvc == nil {
return nil
}
if b.localNotificationsSrvc.IsStarted() {
err := b.localNotificationsSrvc.Stop()
if err != nil {
b.log.Error("LocalNotifications service stop failed on StopLocalNotifications", "error", err)
return nil
}
}
return nil
}
func (b *StatusNode) StartLocalNotifications() error {
if b.localNotificationsSrvc == nil {
return nil
}
if b.walletSrvc == nil {
return nil
}
if !b.localNotificationsSrvc.IsStarted() {
err := b.localNotificationsSrvc.Start()
if err != nil {
b.log.Error("LocalNotifications service start failed on StartLocalNotifications", "error", err)
return nil
}
}
err := b.localNotificationsSrvc.SubscribeWallet(b.walletSrvc.GetFeed())
if err != nil {
b.log.Error("LocalNotifications service could not subscribe to wallet on StartLocalNotifications", "error", err)
return nil
}
return nil
}
// `personal_sign` and `personal_ecRecover` methods are important to
// keep DApps working.
// Usually, they are provided by an ETH or a LES service, but when using
// upstream, we don't start any of these, so we need to start our own
// implementation.
func (b *StatusNode) personalService() *personal.Service {
if b.personalSrvc == nil {
b.personalSrvc = personal.New(b.accountsManager)
}
return b.personalSrvc
}
func (b *StatusNode) timeSource() *timesource.NTPTimeSource {
if b.timeSourceSrvc == nil {
b.timeSourceSrvc = timesource.Default()
}
return b.timeSourceSrvc
}
func (b *StatusNode) Cleanup() error {
if b.wakuSrvc != nil {
if err := b.wakuSrvc.DeleteKeyPairs(); err != nil {
return fmt.Errorf("%s: %v", ErrWakuClearIdentitiesFailure, err)
}
}
if b.Config().WalletConfig.Enabled {
if b.walletSrvc != nil {
if b.walletSrvc.IsStarted() {
err := b.walletSrvc.Stop()
if err != nil {
return err
}
}
}
}
return nil
}

View File

@ -17,14 +17,36 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/ethereum/go-ethereum/params"
"github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/pushnotificationserver"
"github.com/status-im/status-go/static"
wakucommon "github.com/status-im/status-go/waku/common"
wakuv2common "github.com/status-im/status-go/wakuv2/common"
)
// ----------
// LightEthConfig
// ----------
// LightEthConfig holds LES-related configuration
// Status nodes are always lightweight clients (due to mobile platform constraints)
type LightEthConfig struct {
// Enabled flag specifies whether protocol is enabled
Enabled bool
// DatabaseCache is memory (in MBs) allocated to internal caching (min 16MB / database forced)
DatabaseCache int
// TrustedNodes is a list of trusted servers
TrustedNodes []string
//MinTrustedFraction is minimum percentage of connected trusted servers to validate header(1-100)
MinTrustedFraction int
}
// ----------
// DatabaseConfig
// ----------
@ -412,6 +434,9 @@ type NodeConfig struct {
// ClusterConfig extra configuration for supporting cluster peers.
ClusterConfig ClusterConfig `json:"ClusterConfig," validate:"structonly"`
// LightEthConfig extra configuration for LES
LightEthConfig LightEthConfig `json:"LightEthConfig," validate:"structonly"`
// WakuConfig provides a configuration for Waku subprotocol.
WakuConfig WakuConfig `json:"WakuConfig" validate:"structonly"`
@ -705,6 +730,9 @@ func (c *NodeConfig) updatePeerLimits() {
if c.NoDiscovery && !c.Rendezvous {
return
}
if c.LightEthConfig.Enabled {
c.RequireTopics[discv5.Topic(LesTopic(int(c.NetworkID)))] = LesDiscoveryLimits
}
}
// NewNodeConfig creates new node configuration object with bare-minimum defaults.
@ -739,6 +767,9 @@ func NewNodeConfig(dataDir string, networkID uint64) (*NodeConfig, error) {
UpstreamConfig: UpstreamRPCConfig{
URL: getUpstreamURL(networkID),
},
LightEthConfig: LightEthConfig{
DatabaseCache: 16,
},
WakuConfig: WakuConfig{
DataDir: wakuDir,
MinimumPoW: WakuMinimumPoW,
@ -840,6 +871,10 @@ func (c *NodeConfig) Validate() error {
}
}
if c.UpstreamConfig.Enabled && c.LightEthConfig.Enabled {
return fmt.Errorf("both UpstreamConfig and LightEthConfig are enabled, but they are mutually exclusive")
}
if err := c.validateChildStructs(validate); err != nil {
return err
}
@ -889,6 +924,9 @@ func (c *NodeConfig) validateChildStructs(validate *validator.Validate) error {
if err := c.ClusterConfig.Validate(validate); err != nil {
return err
}
if err := c.LightEthConfig.Validate(validate); err != nil {
return err
}
if err := c.SwarmConfig.Validate(validate); err != nil {
return err
}
@ -928,6 +966,19 @@ func (c *ClusterConfig) Validate(validate *validator.Validate) error {
return nil
}
// Validate validates the LightEthConfig struct and returns an error if inconsistent values are found
func (c *LightEthConfig) Validate(validate *validator.Validate) error {
if !c.Enabled {
return nil
}
if err := validate.Struct(c); err != nil {
return err
}
return nil
}
// Validate validates the SwarmConfig struct and returns an error if inconsistent values are found
func (c *SwarmConfig) Validate(validate *validator.Validate) error {
if !c.Enabled {
@ -995,3 +1046,18 @@ func (c *NodeConfig) FormatAPIModules() []string {
func (c *NodeConfig) AddAPIModule(m string) {
c.APIModules = fmt.Sprintf("%s,%s", c.APIModules, m)
}
// LesTopic returns discovery v5 topic derived from genesis of the provided network.
// 1 - mainnet, 3 - ropsten, 4 - rinkeby
func LesTopic(netid int) string {
switch netid {
case 1:
return LESDiscoveryIdentifier + types.Bytes2Hex(params.MainnetGenesisHash.Bytes()[:8])
case 3:
return LESDiscoveryIdentifier + types.Bytes2Hex(params.RopstenGenesisHash.Bytes()[:8])
case 4:
return LESDiscoveryIdentifier + types.Bytes2Hex(params.RinkebyGenesisHash.Bytes()[:8])
default:
return ""
}
}

View File

@ -128,7 +128,7 @@ func (s *PeerPoolSimulationSuite) TestPeerPoolCacheEthV5() {
config := map[discv5.Topic]params.Limits{
topic: params.NewLimits(1, 1),
}
peerPoolOpts := &Options{100 * time.Millisecond, 100 * time.Millisecond, 0, true, 100 * time.Millisecond, nil, ""}
peerPoolOpts := &Options{100 * time.Millisecond, 100 * time.Millisecond, 0, true, 100 * time.Millisecond, nil}
cache, err := newInMemoryCache()
s.Require().NoError(err)
peerPool := NewPeerPool(s.discovery[1], config, cache, peerPoolOpts)
@ -177,7 +177,7 @@ func TestPeerPoolMaxPeersOverflow(t *testing.T) {
defer func() { assert.NoError(t, discovery.Stop()) }()
require.True(t, discovery.Running())
poolOpts := &Options{DefaultFastSync, DefaultSlowSync, 0, true, 100 * time.Millisecond, nil, ""}
poolOpts := &Options{DefaultFastSync, DefaultSlowSync, 0, true, 100 * time.Millisecond, nil}
pool := NewPeerPool(discovery, nil, nil, poolOpts)
require.NoError(t, pool.Start(peer, nil))
require.Equal(t, signal.EventDiscoveryStarted, <-signals)
@ -230,7 +230,7 @@ func TestPeerPoolDiscV5Timeout(t *testing.T) {
require.True(t, discovery.Running())
// start PeerPool
poolOpts := &Options{DefaultFastSync, DefaultSlowSync, time.Millisecond * 100, true, 100 * time.Millisecond, nil, ""}
poolOpts := &Options{DefaultFastSync, DefaultSlowSync, time.Millisecond * 100, true, 100 * time.Millisecond, nil}
pool := NewPeerPool(discovery, nil, nil, poolOpts)
require.NoError(t, pool.Start(server, nil))
require.Equal(t, signal.EventDiscoveryStarted, <-signals)
@ -277,7 +277,7 @@ func TestPeerPoolNotAllowedStopping(t *testing.T) {
require.True(t, discovery.Running())
// start PeerPool
poolOpts := &Options{DefaultFastSync, DefaultSlowSync, time.Millisecond * 100, false, 100 * time.Millisecond, nil, ""}
poolOpts := &Options{DefaultFastSync, DefaultSlowSync, time.Millisecond * 100, false, 100 * time.Millisecond, nil}
pool := NewPeerPool(discovery, nil, nil, poolOpts)
require.NoError(t, pool.Start(server, nil))
@ -294,7 +294,7 @@ func (s *PeerPoolSimulationSuite) TestUpdateTopicLimits() {
config := map[discv5.Topic]params.Limits{
topic: params.NewLimits(1, 1),
}
peerPoolOpts := &Options{100 * time.Millisecond, 100 * time.Millisecond, 0, true, 100 * time.Millisecond, nil, ""}
peerPoolOpts := &Options{100 * time.Millisecond, 100 * time.Millisecond, 0, true, 100 * time.Millisecond, nil}
cache, err := newInMemoryCache()
s.Require().NoError(err)
peerPool := NewPeerPool(s.discovery[1], config, cache, peerPoolOpts)
@ -373,7 +373,6 @@ func (s *PeerPoolSimulationSuite) TestMailServerPeersDiscovery() {
true,
100 * time.Millisecond,
[]enode.ID{s.peers[0].Self().ID()},
"",
}
peerPool := NewPeerPool(s.discovery[1], config, cache, peerPoolOpts)
s.Require().NoError(peerPool.Start(s.peers[1], nil))

View File

@ -206,8 +206,7 @@ func NewMessenger(
// Initialize transport layer.
var transp *transport.Transport
logger.Info("failed to find Whisper service; trying Waku", zap.Error(err))
logger.Info("TEST", zap.Any("node", node))
if waku, err := node.GetWaku(nil); err == nil && waku != nil {
transp, err = transport.NewTransport(
waku,

View File

@ -24,7 +24,7 @@ type Service struct {
}
// Start a service.
func (s *Service) Start(*p2p.Server) error {
func (s *Service) Start() error {
return nil
}

View File

@ -15,7 +15,7 @@ type Service struct {
db *appmetrics.Database
}
func (s *Service) Start(*p2p.Server) error {
func (s *Service) Start() error {
return nil
}

View File

@ -16,7 +16,7 @@ type Service struct {
}
// Start a service.
func (s *Service) Start(*p2p.Server) error {
func (s *Service) Start() error {
return nil
}

View File

@ -64,7 +64,7 @@ type Service struct {
}
// Make sure that Service implements node.Service interface.
var _ node.Service = (*Service)(nil)
var _ node.Lifecycle = (*Service)(nil)
func New(
config params.ShhextConfig,
@ -209,7 +209,7 @@ func (c *verifyTransactionClient) TransactionByHash(ctx context.Context, hash ty
return coretypes.Message{}, coretypes.TransactionStatusPending, err
}
message, err := transaction.AsMessage(signer)
message, err := transaction.AsMessage(signer, nil)
if err != nil {
return coretypes.Message{}, coretypes.TransactionStatusPending, err
}
@ -328,10 +328,15 @@ func (s *Service) APIs() []rpc.API {
panic("this is abstract service, use shhext or wakuext implementation")
}
func (s *Service) SetP2PServer(server *p2p.Server) {
s.server = server
}
// Start is run when a service is started.
// It does nothing in this case but is required by `node.Service` interface.
func (s *Service) Start(server *p2p.Server) error {
s.server = server
func (s *Service) Start() error {
// TODO: set server before start
// s.server = server
return nil
}

View File

@ -153,7 +153,7 @@ func pushMessage(notification *Notification) {
}
// Start Worker which processes all incoming messages
func (s *Service) Start(_ *p2p.Server) error {
func (s *Service) Start() error {
s.started = true
s.transmitter.quit = make(chan struct{})

View File

@ -13,7 +13,7 @@ type Service struct {
db *Database
}
func (s *Service) Start(*p2p.Server) error {
func (s *Service) Start() error {
return nil
}

View File

@ -15,7 +15,7 @@ type Service struct {
}
// Start a service.
func (s *Service) Start(*p2p.Server) error {
func (s *Service) Start() error {
return nil
}

View File

@ -9,7 +9,7 @@ import (
)
// Make sure that Service implements node.Service interface.
var _ node.Service = (*Service)(nil)
var _ node.Lifecycle = (*Service)(nil)
// Service represents out own implementation of personal sign operations.
type Service struct {
@ -40,7 +40,7 @@ func (s *Service) APIs() []rpc.API {
// Start is run when a service is started.
// It does nothing in this case but is required by `node.Service` interface.
func (s *Service) Start(server *p2p.Server) error {
func (s *Service) Start() error {
return nil
}

View File

@ -32,7 +32,7 @@ func (s *Service) Protocols() []p2p.Protocol {
// Start is run when a service is started.
// It does nothing in this case but is required by `node.Service` interface.
func (s *Service) Start(server *p2p.Server) error {
func (s *Service) Start() error {
resetStats()
return nil
}

View File

@ -1,124 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: services/status/service.go
// Package status is a generated GoMock package.
package status
import (
ecdsa "crypto/ecdsa"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
account "github.com/status-im/status-go/account"
"github.com/status-im/status-go/account/generator"
types "github.com/status-im/status-go/eth-node/types"
)
// MockWhisperService is a mock of WhisperService interface
type MockWhisperService struct {
ctrl *gomock.Controller
recorder *MockWhisperServiceMockRecorder
}
// MockWhisperServiceMockRecorder is the mock recorder for MockWhisperService
type MockWhisperServiceMockRecorder struct {
mock *MockWhisperService
}
// NewMockWhisperService creates a new mock instance
func NewMockWhisperService(ctrl *gomock.Controller) *MockWhisperService {
mock := &MockWhisperService{ctrl: ctrl}
mock.recorder = &MockWhisperServiceMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockWhisperService) EXPECT() *MockWhisperServiceMockRecorder {
return m.recorder
}
// AddKeyPair mocks base method
func (m *MockWhisperService) AddKeyPair(key *ecdsa.PrivateKey) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddKeyPair", key)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// AddKeyPair indicates an expected call of AddKeyPair
func (mr *MockWhisperServiceMockRecorder) AddKeyPair(key interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddKeyPair", reflect.TypeOf((*MockWhisperService)(nil).AddKeyPair), key)
}
// MockAccountManager is a mock of AccountManager interface
type MockAccountManager struct {
ctrl *gomock.Controller
recorder *MockAccountManagerMockRecorder
}
// MockAccountManagerMockRecorder is the mock recorder for MockAccountManager
type MockAccountManagerMockRecorder struct {
mock *MockAccountManager
}
// NewMockAccountManager creates a new mock instance
func NewMockAccountManager(ctrl *gomock.Controller) *MockAccountManager {
mock := &MockAccountManager{ctrl: ctrl}
mock.recorder = &MockAccountManagerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockAccountManager) EXPECT() *MockAccountManagerMockRecorder {
return m.recorder
}
// AddressToDecryptedAccount mocks base method
func (m *MockAccountManager) AddressToDecryptedAccount(arg0, arg1 string) (types.Account, *types.Key, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "AddressToDecryptedAccount", arg0, arg1)
ret0, _ := ret[0].(types.Account)
ret1, _ := ret[1].(*types.Key)
ret2, _ := ret[2].(error)
return ret0, ret1, ret2
}
// AddressToDecryptedAccount indicates an expected call of AddressToDecryptedAccount
func (mr *MockAccountManagerMockRecorder) AddressToDecryptedAccount(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AddressToDecryptedAccount", reflect.TypeOf((*MockAccountManager)(nil).AddressToDecryptedAccount), arg0, arg1)
}
// SelectAccount mocks base method
func (m *MockAccountManager) SelectAccount(arg0 account.LoginParams) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "SelectAccount", arg0)
ret0, _ := ret[0].(error)
return ret0
}
// SelectAccount indicates an expected call of SelectAccount
func (mr *MockAccountManagerMockRecorder) SelectAccount(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SelectAccount", reflect.TypeOf((*MockAccountManager)(nil).SelectAccount), arg0)
}
// CreateAccount mocks base method
func (m *MockAccountManager) CreateAccount(password string) (generator.GeneratedAccountInfo, account.Info, string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CreateAccount", password)
ret0, _ := ret[0].(generator.GeneratedAccountInfo)
ret1, _ := ret[1].(account.Info)
ret2, _ := ret[2].(string)
ret3, _ := ret[3].(error)
return ret0, ret1, ret2, ret3
}
// CreateAccount indicates an expected call of CreateAccount
func (mr *MockAccountManagerMockRecorder) CreateAccount(password interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateAccount", reflect.TypeOf((*MockAccountManager)(nil).CreateAccount), password)
}

View File

@ -1,106 +0,0 @@
package status
import (
"context"
"errors"
"fmt"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/eth-node/types"
)
// PublicAPI represents a set of APIs from the `web3.status` namespace.
type PublicAPI struct {
s *Service
}
// NewAPI creates an instance of the status API.
func NewAPI(s *Service) *PublicAPI {
return &PublicAPI{s: s}
}
// LoginRequest : json request for status_login.
type LoginRequest struct {
Addr string `json:"address"`
Password string `json:"password"`
}
// LoginResponse : json response returned by status_login.
type LoginResponse struct {
AddressKeyID string `json:"address_key_id"`
}
// Login is an implementation of `status_login` or `web3.status.login` API
func (api *PublicAPI) Login(context context.Context, req LoginRequest) (res LoginResponse, err error) {
_, accountKey, err := api.s.am.AddressToDecryptedAccount(req.Addr, req.Password)
if err != nil {
return
}
if res.AddressKeyID, err = api.s.w.AddKeyPair(accountKey.PrivateKey); err != nil {
return
}
loginParams := account.LoginParams{
ChatAddress: types.HexToAddress(req.Addr),
Password: req.Password,
MainAccount: types.HexToAddress(req.Addr),
}
if err = api.s.am.SelectAccount(loginParams); err != nil {
return
}
return
}
// SignupRequest : json request for status_signup.
type SignupRequest struct {
Password string `json:"password"`
}
// SignupResponse : json response returned by status_signup.
type SignupResponse struct {
Address string `json:"address"`
Pubkey string `json:"pubkey"`
WalletAddress string `json:"walletAddress"`
WalletPubkey string `json:"walletPubKey"`
ChatAddress string `json:"chatAddress"`
ChatPubkey string `json:"chatPubkey"`
Mnemonic string `json:"mnemonic"`
}
// Signup is an implementation of `status_signup` or `web3.status.signup` API
func (api *PublicAPI) Signup(context context.Context, req SignupRequest) (res SignupResponse, err error) {
_, accountInfo, mnemonic, err := api.s.am.CreateAccount(req.Password)
if err != nil {
err = errors.New("could not create the specified account : " + err.Error())
return
}
res.Address = accountInfo.WalletAddress
res.Pubkey = accountInfo.WalletPubKey
res.WalletAddress = accountInfo.WalletAddress
res.WalletPubkey = accountInfo.WalletPubKey
res.ChatAddress = accountInfo.ChatAddress
res.ChatPubkey = accountInfo.ChatPubKey
res.Mnemonic = mnemonic
return
}
// CreateAddressResponse : json response returned by status_createaccount
type CreateAddressResponse struct {
Address string `json:"address"`
Pubkey string `json:"pubkey"`
Privkey string `json:"privkey"`
}
// CreateAddress is an implementation of `status_createaccount` or `web3.status.createaccount` API
func (api *PublicAPI) CreateAddress(context context.Context) (res CreateAddressResponse, err error) {
if res.Address, res.Pubkey, res.Privkey, err = account.CreateAddress(); err != nil {
err = fmt.Errorf("could not create an address: %v", err)
}
return
}

View File

@ -1,178 +0,0 @@
package status
import (
"context"
"crypto/ecdsa"
"errors"
"testing"
"github.com/status-im/status-go/account/generator"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/suite"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/eth-node/types"
)
func TestStatusSuite(t *testing.T) {
suite.Run(t, new(StatusSuite))
}
type StatusSuite struct {
suite.Suite
am *MockAccountManager
w *MockWhisperService
api *PublicAPI
}
func (s *StatusSuite) SetupTest() {
ctrl := gomock.NewController(s.T())
s.am = NewMockAccountManager(ctrl)
s.w = NewMockWhisperService(ctrl)
service := New(s.w)
service.SetAccountManager(s.am)
s.api = NewAPI(service)
}
var logintests = []struct {
name string
expectedAddressKey string
expectedError error
prepareExpectations func(*StatusSuite)
}{
{
name: "success login",
expectedAddressKey: "addressKey",
expectedError: nil,
prepareExpectations: func(s *StatusSuite) {
key := types.Key{
PrivateKey: &ecdsa.PrivateKey{},
}
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(types.Account{}, &key, nil)
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("addressKey", nil)
loginParams := account.LoginParams{
MainAccount: types.HexToAddress("0x01"),
ChatAddress: types.HexToAddress("0x01"),
Password: "password",
}
s.am.EXPECT().SelectAccount(loginParams).Return(nil)
},
},
{
name: "error when decrypting account from address",
expectedAddressKey: "",
expectedError: errors.New("foo"),
prepareExpectations: func(s *StatusSuite) {
key := types.Key{
PrivateKey: &ecdsa.PrivateKey{},
}
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(types.Account{}, &key, errors.New("foo"))
},
},
{
name: "error when adding key pair to whisper",
expectedAddressKey: "",
expectedError: errors.New("foo"),
prepareExpectations: func(s *StatusSuite) {
key := types.Key{
PrivateKey: &ecdsa.PrivateKey{},
}
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(types.Account{}, &key, nil)
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("", errors.New("foo"))
},
},
{
name: "error when selecting account",
expectedAddressKey: "",
expectedError: errors.New("foo"),
prepareExpectations: func(s *StatusSuite) {
key := types.Key{
PrivateKey: &ecdsa.PrivateKey{},
}
s.am.EXPECT().AddressToDecryptedAccount("0x01", "password").Return(types.Account{}, &key, nil)
s.w.EXPECT().AddKeyPair(key.PrivateKey).Return("", nil)
loginParams := account.LoginParams{
MainAccount: types.HexToAddress("0x01"),
ChatAddress: types.HexToAddress("0x01"),
Password: "password",
}
s.am.EXPECT().SelectAccount(loginParams).Return(errors.New("foo"))
},
},
}
func (s *StatusSuite) TestLogin() {
for _, t := range logintests {
req := LoginRequest{Addr: "0x01", Password: "password"}
t.prepareExpectations(s)
var ctx context.Context
res, err := s.api.Login(ctx, req)
s.Equal(t.expectedAddressKey, res.AddressKeyID, "failed scenario : "+t.name)
s.Equal(t.expectedError, err, "failed scenario : "+t.name)
}
}
var signuptests = []struct {
name string
expectedResponse SignupResponse
expectedError error
prepareExpectations func(*StatusSuite)
}{
{
name: "success signup",
expectedResponse: SignupResponse{
WalletAddress: "addr",
WalletPubkey: "pubkey",
Mnemonic: "mnemonic",
},
expectedError: nil,
prepareExpectations: func(s *StatusSuite) {
mKInfo := generator.GeneratedAccountInfo{
IdentifiedAccountInfo: generator.IdentifiedAccountInfo{},
Mnemonic: "",
}
accountInfo := account.Info{
WalletAddress: "addr",
WalletPubKey: "pubkey",
ChatAddress: "addr",
ChatPubKey: "pubkey",
}
s.am.EXPECT().CreateAccount("password").Return(mKInfo, accountInfo, "mnemonic", nil)
},
},
{
name: "success signup",
expectedResponse: SignupResponse{
WalletAddress: "",
WalletPubkey: "",
Mnemonic: "",
},
expectedError: errors.New("could not create the specified account : foo"),
prepareExpectations: func(s *StatusSuite) {
mKInfo := generator.GeneratedAccountInfo{
IdentifiedAccountInfo: generator.IdentifiedAccountInfo{},
Mnemonic: "",
}
s.am.EXPECT().CreateAccount("password").Return(mKInfo, account.Info{}, "", errors.New("foo"))
},
},
}
func (s *StatusSuite) TestSignup() {
for _, t := range signuptests {
t.prepareExpectations(s)
var ctx context.Context
res, err := s.api.Signup(ctx, SignupRequest{Password: "password"})
s.Equal(t.expectedResponse.WalletAddress, res.WalletAddress, "failed scenario : "+t.name)
s.Equal(t.expectedResponse.WalletPubkey, res.WalletPubkey, "failed scenario : "+t.name)
s.Equal(t.expectedResponse.Mnemonic, res.Mnemonic, "failed scenario : "+t.name)
s.Equal(t.expectedError, err, "failed scenario : "+t.name)
}
}

View File

@ -1,74 +0,0 @@
package status
import (
"crypto/ecdsa"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/account/generator"
"github.com/status-im/status-go/eth-node/types"
)
// Make sure that Service implements node.Service interface.
var _ node.Service = (*Service)(nil)
// WhisperService whisper interface to add key pairs
type WhisperService interface {
AddKeyPair(key *ecdsa.PrivateKey) (string, error)
}
// AccountManager interface to manage account actions
type AccountManager interface {
AddressToDecryptedAccount(string, string) (types.Account, *types.Key, error)
SelectAccount(account.LoginParams) error
CreateAccount(password string) (mkInfo generator.GeneratedAccountInfo, accountInfo account.Info, mnemonic string, err error)
}
// Service represents our own implementation of status status operations.
type Service struct {
am AccountManager
w WhisperService
}
// New returns a new Service.
func New(w WhisperService) *Service {
return &Service{w: w}
}
// Protocols returns a new protocols list. In this case, there are none.
func (s *Service) Protocols() []p2p.Protocol {
return []p2p.Protocol{}
}
// APIs returns a list of new APIs.
func (s *Service) APIs() []rpc.API {
return []rpc.API{
{
Namespace: "status",
Version: "1.0",
Service: NewAPI(s),
Public: false,
},
}
}
// SetAccountManager sets account manager for the API calls.
func (s *Service) SetAccountManager(a AccountManager) {
s.am = a
}
// Start is run when a service is started.
// It does nothing in this case but is required by `node.Service` interface.
func (s *Service) Start(server *p2p.Server) error {
return nil
}
// Stop is run when a service is stopped.
// It does nothing in this case but is required by `node.Service` interface.
func (s *Service) Stop() error {
return nil
}

View File

@ -15,8 +15,8 @@ type Service struct {
w types.Waku
}
func New(config params.ShhextConfig, n types.Node, ctx interface{}, handler ext.EnvelopeEventsHandler, ldb *leveldb.DB) *Service {
w, err := n.GetWaku(ctx)
func New(config params.ShhextConfig, n types.Node, handler ext.EnvelopeEventsHandler, ldb *leveldb.DB) *Service {
w, err := n.GetWaku(nil)
if err != nil {
panic(err)
}

View File

@ -15,8 +15,8 @@ type Service struct {
w types.Waku
}
func New(config params.ShhextConfig, n types.Node, ctx interface{}, handler ext.EnvelopeEventsHandler, ldb *leveldb.DB) *Service {
w, err := n.GetWakuV2(ctx)
func New(config params.ShhextConfig, n types.Node, handler ext.EnvelopeEventsHandler, ldb *leveldb.DB) *Service {
w, err := n.GetWakuV2(nil)
if err != nil {
panic(err)
}

View File

@ -43,7 +43,7 @@ type Service struct {
}
// Start signals transmitter.
func (s *Service) Start(*p2p.Server) error {
func (s *Service) Start() error {
s.group = NewGroup(context.Background())
return s.signals.Start()
}

View File

@ -194,7 +194,7 @@ func (s *NTPTimeSource) StartService() error {
}
// Start runs a goroutine that updates local offset every updatePeriod.
func (s *NTPTimeSource) Start(*p2p.Server) error {
func (s *NTPTimeSource) Start() error {
return s.runPeriodically(s.updateOffset)
}

View File

@ -43,9 +43,7 @@ import (
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
)
@ -203,37 +201,13 @@ func (s *LightEthereum) VfluxRequest(n *enode.Node, reqs vflux.Requests) vflux.R
if !s.udpEnabled {
return nil
}
reqsEnc, _ := rlp.EncodeToBytes(&reqs)
repliesEnc, _ := s.p2pServer.DiscV5.TalkRequest(s.serverPool.DialNode(n), "vfx", reqsEnc)
var replies vflux.Replies
if len(repliesEnc) == 0 || rlp.DecodeBytes(repliesEnc, &replies) != nil {
return nil
}
return replies
return nil
}
// vfxVersion returns the version number of the "les" service subdomain of the vflux UDP
// service, as advertised in the ENR record
func (s *LightEthereum) vfxVersion(n *enode.Node) uint {
if n.Seq() == 0 {
var err error
if !s.udpEnabled {
return 0
}
if n, err = s.p2pServer.DiscV5.RequestENR(n); n != nil && err == nil && n.Seq() != 0 {
s.serverPool.Persist(n)
} else {
return 0
}
}
var les []rlp.RawValue
if err := n.Load(enr.WithEntry("les", &les)); err != nil || len(les) < 1 {
return 0
}
var version uint
rlp.DecodeBytes(les[0], &version) // Ignore additional fields (for forward compatibility).
return version
return 0
}
// prenegQuery sends a capacity query to the given server node to determine whether

View File

@ -54,11 +54,6 @@ func (eth *LightEthereum) setupDiscovery() (enode.Iterator, error) {
it.AddSource(dns)
}
// Enable DHT.
if eth.udpEnabled {
it.AddSource(eth.p2pServer.DiscV5.RandomNodes())
}
forkFilter := forkid.NewFilter(eth.blockchain)
iterator := enode.Filter(it, func(n *enode.Node) bool { return nodeIsServer(forkFilter, n) })
return iterator, nil

View File

@ -201,9 +201,10 @@ func (s *LesServer) Start() error {
s.handler.start()
s.wg.Add(1)
go s.capacityManagement()
if s.p2pSrv.DiscV5 != nil {
s.p2pSrv.DiscV5.RegisterTalkHandler("vfx", s.vfluxServer.ServeEncoded)
}
/*
if s.p2pSrv.DiscV5 != nil {
s.p2pSrv.DiscV5.RegisterTalkHandler("vfx", s.vfluxServer.ServeEncoded)
}*/
return nil
}

View File

@ -206,6 +206,7 @@ func (d *dialScheduler) removeStatic(n *enode.Node) {
// peerAdded updates the peer set.
func (d *dialScheduler) peerAdded(c *conn) {
log.Info("PEER added")
select {
case d.addPeerCh <- c:
case <-d.ctx.Done():
@ -214,6 +215,7 @@ func (d *dialScheduler) peerAdded(c *conn) {
// peerRemoved updates the peer set.
func (d *dialScheduler) peerRemoved(c *conn) {
log.Info("PEER removed")
select {
case d.remPeerCh <- c:
case <-d.ctx.Done():
@ -243,7 +245,7 @@ loop:
select {
case node := <-nodesCh:
if err := d.checkDial(node); err != nil {
d.log.Trace("Discarding dial candidate", "id", node.ID(), "ip", node.IP(), "reason", err)
d.log.Info("Discarding dial candidate", "id", node.ID(), "ip", node.IP(), "reason", err)
} else {
d.startDial(newDialTask(node, dynDialedConn))
}
@ -277,14 +279,22 @@ loop:
case node := <-d.addStaticCh:
id := node.ID()
_, exists := d.static[id]
d.log.Trace("Adding static node", "id", id, "ip", node.IP(), "added", !exists)
d.log.Info("Adding static node", "id", id, "ip", node.IP(), "added", !exists)
if exists {
d.log.Info("existing, continue")
continue loop
}
task := newDialTask(node, staticDialedConn)
d.log.Info("new dial task")
d.static[id] = task
if d.checkDial(node) == nil {
d.log.Info("checking dial")
err := d.checkDial(node)
d.log.Info("dial checked")
if err == nil {
d.log.Info("addign to static pool")
d.addToStaticPool(task)
} else {
d.log.Info("error", "err", err)
}
case node := <-d.remStaticCh:
@ -376,6 +386,7 @@ func (d *dialScheduler) expireHistory() {
// freeDialSlots returns the number of free dial slots. The result can be negative
// when peers are connected while their task is still running.
func (d *dialScheduler) freeDialSlots() int {
log.Info("checkign slots", "max dials", d.maxDialPeers, "dial peers", d.dialPeers)
slots := (d.maxDialPeers - d.dialPeers) * 2
if slots > d.maxActiveDials {
slots = d.maxActiveDials
@ -412,7 +423,9 @@ func (d *dialScheduler) checkDial(n *enode.Node) error {
// startStaticDials starts n static dial tasks.
func (d *dialScheduler) startStaticDials(n int) (started int) {
log.Info("starting", "n", n)
for started = 0; started < n && len(d.staticPool) > 0; started++ {
log.Info("starting static")
idx := d.rand.Intn(len(d.staticPool))
task := d.staticPool[idx]
d.startDial(task)

View File

@ -0,0 +1,4 @@
This package is an early prototype of Discovery v5. Do not use this code.
See https://github.com/ethereum/devp2p/blob/master/discv5/discv5.md for the
current Discovery v5 specification.

View File

@ -58,12 +58,11 @@ var (
nodeDBVersionKey = []byte("version") // Version of the database to flush if changes
nodeDBItemPrefix = []byte("n:") // Identifier to prefix node entries with
nodeDBDiscoverRoot = ":discover"
nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping"
nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong"
nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail"
nodeDBDiscoverLocalEndpoint = nodeDBDiscoverRoot + ":localendpoint"
nodeDBTopicRegTickets = ":tickets"
nodeDBDiscoverRoot = ":discover"
nodeDBDiscoverPing = nodeDBDiscoverRoot + ":lastping"
nodeDBDiscoverPong = nodeDBDiscoverRoot + ":lastpong"
nodeDBDiscoverFindFails = nodeDBDiscoverRoot + ":findfail"
nodeDBTopicRegTickets = ":tickets"
)
// newNodeDB creates a new node database for storing and retrieving infos about
@ -311,20 +310,6 @@ func (db *nodeDB) updateFindFails(id NodeID, fails int) error {
return db.storeInt64(makeKey(id, nodeDBDiscoverFindFails), int64(fails))
}
// localEndpoint returns the last local endpoint communicated to the
// given remote node.
func (db *nodeDB) localEndpoint(id NodeID) *rpcEndpoint {
var ep rpcEndpoint
if err := db.fetchRLP(makeKey(id, nodeDBDiscoverLocalEndpoint), &ep); err != nil {
return nil
}
return &ep
}
func (db *nodeDB) updateLocalEndpoint(id NodeID, ep rpcEndpoint) error {
return db.storeRLP(makeKey(id, nodeDBDiscoverLocalEndpoint), &ep)
}
// querySeeds retrieves random nodes to be used as potential seed nodes
// for bootstrapping.
func (db *nodeDB) querySeeds(n int, maxAge time.Duration) []*Node {

View File

@ -77,14 +77,6 @@ type Network struct {
nursery []*Node
nodes map[NodeID]*Node // tracks active nodes with state != known
timeoutTimers map[timeoutEvent]*time.Timer
// Revalidation queues.
// Nodes put on these queues will be pinged eventually.
slowRevalidateQueue []*Node
fastRevalidateQueue []*Node
// Buffers for state transition.
sendBuf []*ingressPacket
}
// transport is implemented by the UDP transport.
@ -104,10 +96,9 @@ type transport interface {
}
type findnodeQuery struct {
remote *Node
target common.Hash
reply chan<- []*Node
nresults int // counter for received nodes
remote *Node
target common.Hash
reply chan<- []*Node
}
type topicRegisterReq struct {
@ -366,6 +357,8 @@ func (net *Network) loop() {
bucketRefreshTimer = time.NewTimer(bucketRefreshInterval)
refreshDone chan struct{} // closed when the 'refresh' lookup has ended
)
defer refreshTimer.Stop()
defer bucketRefreshTimer.Stop()
// Tracking the next ticket to register.
var (
@ -402,11 +395,13 @@ func (net *Network) loop() {
searchInfo = make(map[Topic]topicSearchInfo)
activeSearchCount int
)
defer topicRegisterLookupTick.Stop()
topicSearchLookupDone := make(chan topicSearchResult, 100)
topicSearch := make(chan Topic, 100)
<-topicRegisterLookupTick.C
statsDump := time.NewTicker(10 * time.Second)
defer statsDump.Stop()
loop:
for {
@ -646,14 +641,14 @@ loop:
}
log.Trace("loop stopped")
log.Debug(fmt.Sprintf("shutting down"))
log.Debug("shutting down")
if net.conn != nil {
net.conn.Close()
}
if refreshDone != nil {
// TODO: wait for pending refresh.
//<-refreshResults
}
// TODO: wait for pending refresh.
// if refreshDone != nil {
// <-refreshResults
// }
// Cancel all pending timeouts.
for _, timer := range net.timeoutTimers {
timer.Stop()
@ -1042,6 +1037,9 @@ func (net *Network) handle(n *Node, ev nodeEvent, pkt *ingressPacket) error {
net.db.ensureExpirer()
}
}
if ev == pongTimeout {
n.pingEcho = nil // clean up if pongtimeout
}
if n.state == nil {
n.state = unknown //???
}

View File

@ -66,23 +66,6 @@ func (n *Node) addr() *net.UDPAddr {
return &net.UDPAddr{IP: n.IP, Port: int(n.UDP)}
}
func (n *Node) setAddr(a *net.UDPAddr) {
n.IP = a.IP
if ipv4 := a.IP.To4(); ipv4 != nil {
n.IP = ipv4
}
n.UDP = uint16(a.Port)
}
// compares the given address against the stored values.
func (n *Node) addrEqual(a *net.UDPAddr) bool {
ip := a.IP
if ipv4 := a.IP.To4(); ipv4 != nil {
ip = ipv4
}
return n.UDP == uint16(a.Port) && n.IP.Equal(ip)
}
// Incomplete returns true for nodes with no IP address.
func (n *Node) Incomplete() bool {
return n.IP == nil
@ -326,14 +309,6 @@ func (n NodeID) Pubkey() (*ecdsa.PublicKey, error) {
return p, nil
}
func (id NodeID) mustPubkey() ecdsa.PublicKey {
pk, err := id.Pubkey()
if err != nil {
panic(err)
}
return *pk
}
// recoverNodeID computes the public key used to sign the
// given hash from the signature.
func recoverNodeID(hash, sig []byte) (id NodeID, err error) {

View File

@ -1,126 +0,0 @@
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Contains the NTP time drift detection via the SNTP protocol:
// https://tools.ietf.org/html/rfc4330
package discv5
import (
"fmt"
"net"
"sort"
"strings"
"time"
"github.com/ethereum/go-ethereum/log"
)
const (
ntpPool = "pool.ntp.org" // ntpPool is the NTP server to query for the current time
ntpChecks = 3 // Number of measurements to do against the NTP server
)
// durationSlice attaches the methods of sort.Interface to []time.Duration,
// sorting in increasing order.
type durationSlice []time.Duration
func (s durationSlice) Len() int { return len(s) }
func (s durationSlice) Less(i, j int) bool { return s[i] < s[j] }
func (s durationSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// checkClockDrift queries an NTP server for clock drifts and warns the user if
// one large enough is detected.
func checkClockDrift() {
drift, err := sntpDrift(ntpChecks)
if err != nil {
return
}
if drift < -driftThreshold || drift > driftThreshold {
warning := fmt.Sprintf("System clock seems off by %v, which can prevent network connectivity", drift)
howtofix := fmt.Sprintf("Please enable network time synchronisation in system settings")
separator := strings.Repeat("-", len(warning))
log.Warn(separator)
log.Warn(warning)
log.Warn(howtofix)
log.Warn(separator)
} else {
log.Debug(fmt.Sprintf("Sanity NTP check reported %v drift, all ok", drift))
}
}
// sntpDrift does a naive time resolution against an NTP server and returns the
// measured drift. This method uses the simple version of NTP. It's not precise
// but should be fine for these purposes.
//
// Note, it executes two extra measurements compared to the number of requested
// ones to be able to discard the two extremes as outliers.
func sntpDrift(measurements int) (time.Duration, error) {
// Resolve the address of the NTP server
addr, err := net.ResolveUDPAddr("udp", ntpPool+":123")
if err != nil {
return 0, err
}
// Construct the time request (empty package with only 2 fields set):
// Bits 3-5: Protocol version, 3
// Bits 6-8: Mode of operation, client, 3
request := make([]byte, 48)
request[0] = 3<<3 | 3
// Execute each of the measurements
drifts := []time.Duration{}
for i := 0; i < measurements+2; i++ {
// Dial the NTP server and send the time retrieval request
conn, err := net.DialUDP("udp", nil, addr)
if err != nil {
return 0, err
}
defer conn.Close()
sent := time.Now()
if _, err = conn.Write(request); err != nil {
return 0, err
}
// Retrieve the reply and calculate the elapsed time
conn.SetDeadline(time.Now().Add(5 * time.Second))
reply := make([]byte, 48)
if _, err = conn.Read(reply); err != nil {
return 0, err
}
elapsed := time.Since(sent)
// Reconstruct the time from the reply data
sec := uint64(reply[43]) | uint64(reply[42])<<8 | uint64(reply[41])<<16 | uint64(reply[40])<<24
frac := uint64(reply[47]) | uint64(reply[46])<<8 | uint64(reply[45])<<16 | uint64(reply[44])<<24
nanosec := sec*1e9 + (frac*1e9)>>32
t := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nanosec)).Local()
// Calculate the drift based on an assumed answer time of RRT/2
drifts = append(drifts, sent.Sub(t)+elapsed/2)
}
// Calculate average drif (drop two extremities to avoid outliers)
sort.Sort(durationSlice(drifts))
drift := time.Duration(0)
for i := 1; i < len(drifts)-1; i++ {
drift += drifts[i]
}
return drift / time.Duration(measurements), nil
}

View File

@ -14,12 +14,8 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
// Package discv5 implements the RLPx v5 Topic Discovery Protocol.
//
// The Topic Discovery protocol provides a way to find RLPx nodes that
// can be connected to. It uses a Kademlia-like protocol to maintain a
// distributed database of the IDs and endpoints of all listening
// nodes.
// Package discv5 is a prototype implementation of Discvery v5.
// Deprecated: do not use this package.
package discv5
import (

View File

@ -22,7 +22,6 @@ import (
"fmt"
"math"
"math/rand"
"sort"
"time"
"github.com/ethereum/go-ethereum/common"
@ -33,8 +32,6 @@ import (
const (
ticketTimeBucketLen = time.Minute
timeWindow = 10 // * ticketTimeBucketLen
wantTicketsInWindow = 10
collectFrequency = time.Second * 30
registerFrequency = time.Second * 60
maxCollectDebt = 10
@ -139,7 +136,6 @@ type ticketStore struct {
lastBucketFetched timeBucket
nextTicketCached *ticketRef
nextTicketReg mclock.AbsTime
searchTopicMap map[Topic]searchTopic
nextTopicQueryCleanup mclock.AbsTime
@ -268,57 +264,6 @@ func (s *ticketStore) nextSearchLookup(topic Topic) lookupInfo {
return target
}
// ticketsInWindow returns the tickets of a given topic in the registration window.
func (s *ticketStore) ticketsInWindow(topic Topic) []ticketRef {
// Sanity check that the topic still exists before operating on it
if s.tickets[topic] == nil {
log.Warn("Listing non-existing discovery tickets", "topic", topic)
return nil
}
// Gather all the tickers in the next time window
var tickets []ticketRef
buckets := s.tickets[topic].buckets
for idx := timeBucket(0); idx < timeWindow; idx++ {
tickets = append(tickets, buckets[s.lastBucketFetched+idx]...)
}
log.Trace("Retrieved discovery registration tickets", "topic", topic, "from", s.lastBucketFetched, "tickets", len(tickets))
return tickets
}
func (s *ticketStore) removeExcessTickets(t Topic) {
tickets := s.ticketsInWindow(t)
if len(tickets) <= wantTicketsInWindow {
return
}
sort.Sort(ticketRefByWaitTime(tickets))
for _, r := range tickets[wantTicketsInWindow:] {
s.removeTicketRef(r)
}
}
type ticketRefByWaitTime []ticketRef
// Len is the number of elements in the collection.
func (s ticketRefByWaitTime) Len() int {
return len(s)
}
func (ref ticketRef) waitTime() mclock.AbsTime {
return ref.t.regTime[ref.idx] - ref.t.issueTime
}
// Less reports whether the element with
// index i should sort before the element with index j.
func (s ticketRefByWaitTime) Less(i, j int) bool {
return s[i].waitTime() < s[j].waitTime()
}
// Swap swaps the elements with indexes i and j.
func (s ticketRefByWaitTime) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s *ticketStore) addTicketRef(r ticketRef) {
topic := r.t.topics[r.idx]
tickets := s.tickets[topic]
@ -565,15 +510,6 @@ func (s *ticketStore) addTicket(localTime mclock.AbsTime, pingHash []byte, ticke
}
}
func (s *ticketStore) getNodeTicket(node *Node) *ticket {
if s.nodes[node] == nil {
log.Trace("Retrieving node ticket", "node", node.ID, "serial", nil)
} else {
log.Trace("Retrieving node ticket", "node", node.ID, "serial", s.nodes[node].serial)
}
return s.nodes[node]
}
func (s *ticketStore) canQueryTopic(node *Node, topic Topic) bool {
qq := s.queriesSent[node]
if qq != nil {
@ -770,12 +706,6 @@ func globalRandRead(b []byte) {
}
}
func (r *topicRadius) isInRadius(addrHash common.Hash) bool {
nodePrefix := binary.BigEndian.Uint64(addrHash[0:8])
dist := nodePrefix ^ r.topicHashPrefix
return dist < r.radius
}
func (r *topicRadius) chooseLookupBucket(a, b int) int {
if a < 0 {
a = 0

View File

@ -27,7 +27,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/p2p/netutil"
"github.com/ethereum/go-ethereum/rlp"
)
@ -38,15 +37,12 @@ const Version = 4
var (
errPacketTooSmall = errors.New("too small")
errBadPrefix = errors.New("bad prefix")
errTimeout = errors.New("RPC timeout")
)
// Timeouts
const (
respTimeout = 500 * time.Millisecond
expiration = 20 * time.Second
driftThreshold = 10 * time.Second // Allowed clock drift before warning user
)
// RPC request structures
@ -187,10 +183,6 @@ func makeEndpoint(addr *net.UDPAddr, tcpPort uint16) rpcEndpoint {
return rpcEndpoint{IP: ip, UDP: uint16(addr.Port), TCP: tcpPort}
}
func (e1 rpcEndpoint) equal(e2 rpcEndpoint) bool {
return e1.UDP == e2.UDP && e1.TCP == e2.TCP && e1.IP.Equal(e2.IP)
}
func nodeFromRPC(sender *net.UDPAddr, rn rpcNode) (*Node, error) {
if err := netutil.CheckRelayIP(sender.IP, rn.IP); err != nil {
return nil, err
@ -225,7 +217,6 @@ type udp struct {
conn conn
priv *ecdsa.PrivateKey
ourEndpoint rpcEndpoint
nat nat.Interface
net *Network
}
@ -274,13 +265,6 @@ func (t *udp) sendPing(remote *Node, toaddr *net.UDPAddr, topics []Topic) (hash
return hash
}
func (t *udp) sendFindnode(remote *Node, target NodeID) {
t.sendPacket(remote.ID, remote.addr(), byte(findnodePacket), findnode{
Target: target,
Expiration: uint64(time.Now().Add(expiration).Unix()),
})
}
func (t *udp) sendNeighbours(remote *Node, results []*Node) {
// Send neighbors in chunks with at most maxNeighbors per packet
// to stay below the 1280 byte limit.

View File

@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/discv5"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/p2p/enr"
"github.com/ethereum/go-ethereum/p2p/nat"
@ -104,7 +105,7 @@ type Config struct {
// BootstrapNodesV5 are used to establish connectivity
// with the rest of the network using the V5 discovery
// protocol.
BootstrapNodesV5 []*enode.Node `toml:",omitempty"`
BootstrapNodesV5 []*discv5.Node `toml:",omitempty"`
// Static nodes are used as pre-configured connections which are always
// maintained and re-connected on disconnects.
@ -181,7 +182,7 @@ type Server struct {
nodedb *enode.DB
localnode *enode.LocalNode
ntab *discover.UDPv4
DiscV5 *discover.UDPv5
DiscV5 *discv5.Network
discmix *enode.FairMix
dialsched *dialScheduler
@ -442,7 +443,7 @@ type sharedUDPConn struct {
unhandled chan discover.ReadPacket
}
// ReadFromUDP implements discover.UDPConn
// ReadFromUDP implements discv5.conn
func (s *sharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) {
packet, ok := <-s.unhandled
if !ok {
@ -456,7 +457,7 @@ func (s *sharedUDPConn) ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err err
return l, packet.Addr, nil
}
// Close implements discover.UDPConn
// Close implements discv5.conn
func (s *sharedUDPConn) Close() error {
return nil
}
@ -615,7 +616,7 @@ func (srv *Server) setupDiscovery() error {
Unhandled: unhandled,
Log: srv.log,
}
ntab, err := discover.ListenV4(conn, srv.localnode, cfg)
ntab, err := discover.ListenUDP(conn, srv.localnode, cfg)
if err != nil {
return err
}
@ -625,21 +626,20 @@ func (srv *Server) setupDiscovery() error {
// Discovery V5
if srv.DiscoveryV5 {
cfg := discover.Config{
PrivateKey: srv.PrivateKey,
NetRestrict: srv.NetRestrict,
Bootnodes: srv.BootstrapNodesV5,
Log: srv.log,
}
var ntab *discv5.Network
var err error
if sconn != nil {
srv.DiscV5, err = discover.ListenV5(sconn, srv.localnode, cfg)
ntab, err = discv5.ListenUDP(srv.PrivateKey, sconn, "", srv.NetRestrict)
} else {
srv.DiscV5, err = discover.ListenV5(conn, srv.localnode, cfg)
ntab, err = discv5.ListenUDP(srv.PrivateKey, conn, "", srv.NetRestrict)
}
if err != nil {
return err
}
if err := ntab.SetFallbackNodes(srv.BootstrapNodesV5); err != nil {
return err
}
srv.DiscV5 = ntab
}
return nil
}
@ -671,6 +671,7 @@ func (srv *Server) maxInboundConns() int {
}
func (srv *Server) maxDialedConns() (limit int) {
log.Info("max dialled", "no-dial", srv.NoDial, "max-peers", srv.MaxPeers)
if srv.NoDial || srv.MaxPeers == 0 {
return 0
}
@ -780,6 +781,7 @@ running:
c.cont <- srv.postHandshakeChecks(peers, inboundCount, c)
case c := <-srv.checkpointAddPeer:
log.Info("checkpoing add peer")
// At this point the connection is past the protocol handshake.
// Its capabilities are known and the remote identity is verified.
err := srv.addPeerChecks(peers, inboundCount, c)
@ -1043,6 +1045,7 @@ func (srv *Server) checkpoint(c *conn, stage chan<- *conn) error {
}
func (srv *Server) launchPeer(c *conn) *Peer {
log.Info("launching peer")
p := newPeer(srv.log, c, srv.Protocols)
if srv.EnableMsgEvents {
// If message events are enabled, pass the peerFeed
@ -1055,6 +1058,7 @@ func (srv *Server) launchPeer(c *conn) *Peer {
// runPeer runs in its own goroutine for each peer.
func (srv *Server) runPeer(p *Peer) {
log.Info("Running peer", "peer", p)
if srv.newPeerHook != nil {
srv.newPeerHook(p)
}

View File

@ -73,24 +73,6 @@ var CalaverasBootnodes = []string{
"enode://9e1096aa59862a6f164994cb5cb16f5124d6c992cdbf4535ff7dea43ea1512afe5448dca9df1b7ab0726129603f1a3336b631e4d7a1a44c94daddd03241587f9@3.9.20.133:30303",
}
var V5Bootnodes = []string{
// Teku team's bootnode
"enr:-KG4QOtcP9X1FbIMOe17QNMKqDxCpm14jcX5tiOE4_TyMrFqbmhPZHK_ZPG2Gxb1GE2xdtodOfx9-cgvNtxnRyHEmC0ghGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQDE8KdiXNlY3AyNTZrMaEDhpehBDbZjM_L9ek699Y7vhUJ-eAdMyQW_Fil522Y0fODdGNwgiMog3VkcIIjKA",
"enr:-KG4QDyytgmE4f7AnvW-ZaUOIi9i79qX4JwjRAiXBZCU65wOfBu-3Nb5I7b_Rmg3KCOcZM_C3y5pg7EBU5XGrcLTduQEhGV0aDKQ9aX9QgAAAAD__________4JpZIJ2NIJpcIQ2_DUbiXNlY3AyNTZrMaEDKnz_-ps3UUOfHWVYaskI5kWYO_vtYMGYCQRAR3gHDouDdGNwgiMog3VkcIIjKA",
// Prylab team's bootnodes
"enr:-Ku4QImhMc1z8yCiNJ1TyUxdcfNucje3BGwEHzodEZUan8PherEo4sF7pPHPSIB1NNuSg5fZy7qFsjmUKs2ea1Whi0EBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQOVphkDqal4QzPMksc5wnpuC3gvSC8AfbFOnZY_On34wIN1ZHCCIyg",
"enr:-Ku4QP2xDnEtUXIjzJ_DhlCRN9SN99RYQPJL92TMlSv7U5C1YnYLjwOQHgZIUXw6c-BvRg2Yc2QsZxxoS_pPRVe0yK8Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMeFF5GrS7UZpAH2Ly84aLK-TyvH-dRo0JM1i8yygH50YN1ZHCCJxA",
"enr:-Ku4QPp9z1W4tAO8Ber_NQierYaOStqhDqQdOPY3bB3jDgkjcbk6YrEnVYIiCBbTxuar3CzS528d2iE7TdJsrL-dEKoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpD1pf1CAAAAAP__________gmlkgnY0gmlwhBLf22SJc2VjcDI1NmsxoQMw5fqqkw2hHC4F5HZZDPsNmPdB1Gi8JPQK7pRc9XHh-oN1ZHCCKvg",
// Lighthouse team's bootnodes
"enr:-IS4QLkKqDMy_ExrpOEWa59NiClemOnor-krjp4qoeZwIw2QduPC-q7Kz4u1IOWf3DDbdxqQIgC4fejavBOuUPy-HE4BgmlkgnY0gmlwhCLzAHqJc2VjcDI1NmsxoQLQSJfEAHZApkm5edTCZ_4qps_1k_ub2CxHFxi-gr2JMIN1ZHCCIyg",
"enr:-IS4QDAyibHCzYZmIYZCjXwU9BqpotWmv2BsFlIq1V31BwDDMJPFEbox1ijT5c2Ou3kvieOKejxuaCqIcjxBjJ_3j_cBgmlkgnY0gmlwhAMaHiCJc2VjcDI1NmsxoQJIdpj_foZ02MXz4It8xKD7yUHTBx7lVFn3oeRP21KRV4N1ZHCCIyg",
// EF bootnodes
"enr:-Ku4QHqVeJ8PPICcWk1vSn_XcSkjOkNiTg6Fmii5j6vUQgvzMc9L1goFnLKgXqBJspJjIsB91LTOleFmyWWrFVATGngBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhAMRHkWJc2VjcDI1NmsxoQKLVXFOhp2uX6jeT0DvvDpPcU8FWMjQdR4wMuORMhpX24N1ZHCCIyg",
"enr:-Ku4QG-2_Md3sZIAUebGYT6g0SMskIml77l6yR-M_JXc-UdNHCmHQeOiMLbylPejyJsdAPsTHJyjJB2sYGDLe0dn8uYBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhBLY-NyJc2VjcDI1NmsxoQORcM6e19T1T9gi7jxEZjk_sjVLGFscUNqAY9obgZaxbIN1ZHCCIyg",
"enr:-Ku4QPn5eVhcoF1opaFEvg1b6JNFD2rqVkHQ8HApOKK61OIcIXD127bKWgAtbwI7pnxx6cDyk_nI88TrZKQaGMZj0q0Bh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDayLMaJc2VjcDI1NmsxoQK2sBOLGcUb4AwuYzFuAVCaNHA-dy24UuEKkeFNgCVCsIN1ZHCCIyg",
"enr:-Ku4QEWzdnVtXc2Q0ZVigfCGggOVB2Vc1ZCPEc6j21NIFLODSJbvNaef1g4PxhPwl_3kax86YPheFUSLXPRs98vvYsoBh2F0dG5ldHOIAAAAAAAAAACEZXRoMpC1MD8qAAAAAP__________gmlkgnY0gmlwhDZBrP2Jc2VjcDI1NmsxoQM6jr8Rb1ktLEsVcKAPa08wCsKUmvoQ8khiOl_SLozf9IN1ZHCCIyg",
}
const dnsPrefix = "enrtree://AKA3AM6LPBYEUDMVNU3BSVQJ5AD45Y7YPOHJLEF6W26QOE4VTUDPE@"
// KnownDNSNetwork returns the address of a public DNS-based node list for the given

View File

@ -1056,7 +1056,7 @@ func (w *Waku) Send(envelope *common.Envelope) error {
// Start implements node.Service, starting the background data propagation thread
// of the Waku protocol.
func (w *Waku) Start(*p2p.Server) error {
func (w *Waku) Start() error {
go w.update()
numCPU := runtime.NumCPU()
@ -1519,6 +1519,10 @@ func (w *Waku) GetEnvelope(hash gethcommon.Hash) *common.Envelope {
return w.envelopes[hash]
}
func (w *Waku) Version() uint {
return 1
}
// isEnvelopeCached checks if envelope with specific hash has already been received and cached.
func (w *Waku) IsEnvelopeCached(hash gethcommon.Hash) bool {
w.poolMu.Lock()

View File

@ -548,7 +548,7 @@ func (w *Waku) Query(topics []types.TopicType, from uint64, to uint64, opts []st
// Start implements node.Service, starting the background data propagation thread
// of the Waku protocol.
func (w *Waku) Start(*p2p.Server) error {
func (w *Waku) Start() error {
numCPU := runtime.NumCPU()
for i := 0; i < numCPU; i++ {
go w.processQueue()