Improve statusd CLI usage (#441)
This PR refactors CLI API, removes obsolete commands and splits status code into smaller pieces: * get rid of subcommands API (no ./status <command>) * get rid of custom cli app package * use stdlib flag package for handling command line flags * move cross-compilation / mobile related code to lib/ package * move wnode command into separate binary (cmd/node-status, name is subject to discuss) * remove faucet command as obsolete * update/add docs/READMES/wikis for new command line flags It makes statusd code much simpler and smaller, separates concerns (lib, wnode and statusd are different things).
This commit is contained in:
parent
987cdd6221
commit
4536e99275
|
@ -5,6 +5,12 @@
|
||||||
# git config --global core.excludesfile ~/.gitignore_global
|
# git config --global core.excludesfile ~/.gitignore_global
|
||||||
|
|
||||||
/statusd-data
|
/statusd-data
|
||||||
|
/cmd/statusd/statusd-data
|
||||||
|
/cmd/statusd/statusd
|
||||||
|
|
||||||
|
/wnode-status-data
|
||||||
|
/cmd/wnode-status/wnode-status-data
|
||||||
|
/cmd/wnode-status/wnode-status
|
||||||
|
|
||||||
/tmp
|
/tmp
|
||||||
*/**/*un~
|
*/**/*un~
|
||||||
|
|
25
Makefile
25
Makefile
|
@ -1,6 +1,9 @@
|
||||||
.PHONY: statusgo all test xgo clean help
|
.PHONY: statusgo all test xgo clean help
|
||||||
.PHONY: statusgo-android statusgo-ios
|
.PHONY: statusgo-android statusgo-ios
|
||||||
|
|
||||||
|
help: ##@other Show this help
|
||||||
|
@perl -e '$(HELP_FUN)' $(MAKEFILE_LIST)
|
||||||
|
|
||||||
include ./static/tools/mk/lint.mk
|
include ./static/tools/mk/lint.mk
|
||||||
|
|
||||||
GOBIN = build/bin
|
GOBIN = build/bin
|
||||||
|
@ -28,32 +31,32 @@ HELP_FUN = \
|
||||||
print "\n"; \
|
print "\n"; \
|
||||||
}
|
}
|
||||||
|
|
||||||
help: ##@other Show this help
|
|
||||||
@perl -e '$(HELP_FUN)' $(MAKEFILE_LIST)
|
|
||||||
|
|
||||||
# Main targets
|
# Main targets
|
||||||
|
|
||||||
UNIT_TEST_PACKAGES := $(shell go list ./... | grep -v /vendor | grep -v /e2e | grep -v /cmd)
|
UNIT_TEST_PACKAGES := $(shell go list ./... | grep -v /vendor | grep -v /e2e | grep -v /cmd)
|
||||||
|
|
||||||
statusgo: ##@build Build status-go as statusd server
|
statusgo: ##@build Build status-go as statusd server
|
||||||
build/env.sh go build -i -o $(GOBIN)/statusd -v $(shell build/testnet-flags.sh) ./cmd/statusd
|
build/env.sh go build -i -o $(GOBIN)/statusd -v $(shell build/testnet-flags.sh) ./cmd/statusd
|
||||||
@echo "\nCompilation done.\nRun \"build/bin/statusd help\" to view available commands."
|
@echo "\nCompilation done.\nRun \"build/bin/statusd -h\" to view available commands."
|
||||||
|
|
||||||
|
wnode-status: ##@build Build wnode-status (Whisper 5 debug tool)
|
||||||
|
build/env.sh go build -i -o $(GOBIN)/wnode-status -v $(shell build/testnet-flags.sh) ./cmd/wnode-status
|
||||||
|
|
||||||
statusgo-cross: statusgo-android statusgo-ios
|
statusgo-cross: statusgo-android statusgo-ios
|
||||||
@echo "Full cross compilation done."
|
@echo "Full cross compilation done."
|
||||||
@ls -ld $(GOBIN)/statusgo-*
|
@ls -ld $(GOBIN)/statusgo-*
|
||||||
|
|
||||||
statusgo-android: xgo ##@cross-compile Build status-go for Android
|
statusgo-android: xgo ##@cross-compile Build status-go for Android
|
||||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=android-16/aar -v $(shell build/testnet-flags.sh) ./cmd/statusd
|
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=android-16/aar -v $(shell build/testnet-flags.sh) ./lib
|
||||||
@echo "Android cross compilation done."
|
@echo "Android cross compilation done."
|
||||||
|
|
||||||
statusgo-ios: xgo ##@cross-compile Build status-go for iOS
|
statusgo-ios: xgo ##@cross-compile Build status-go for iOS
|
||||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/testnet-flags.sh) ./cmd/statusd
|
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/testnet-flags.sh) ./lib
|
||||||
@echo "iOS framework cross compilation done."
|
@echo "iOS framework cross compilation done."
|
||||||
|
|
||||||
statusgo-ios-simulator: xgo ##@cross-compile Build status-go for iOS Simulator
|
statusgo-ios-simulator: xgo ##@cross-compile Build status-go for iOS Simulator
|
||||||
@build/env.sh docker pull farazdagi/xgo-ios-simulator
|
@build/env.sh docker pull farazdagi/xgo-ios-simulator
|
||||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo-ios-simulator --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/testnet-flags.sh) ./cmd/statusd
|
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo-ios-simulator --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/testnet-flags.sh) ./lib
|
||||||
@echo "iOS framework cross compilation done."
|
@echo "iOS framework cross compilation done."
|
||||||
|
|
||||||
xgo:
|
xgo:
|
||||||
|
@ -66,15 +69,15 @@ statusgo-mainnet:
|
||||||
@echo "Run \"build/bin/statusgo\" to view available commands"
|
@echo "Run \"build/bin/statusgo\" to view available commands"
|
||||||
|
|
||||||
statusgo-android-mainnet: xgo
|
statusgo-android-mainnet: xgo
|
||||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=android-16/aar -v $(shell build/mainnet-flags.sh) ./cmd/statusd
|
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=android-16/aar -v $(shell build/mainnet-flags.sh) ./lib
|
||||||
@echo "Android cross compilation done (mainnet)."
|
@echo "Android cross compilation done (mainnet)."
|
||||||
|
|
||||||
statusgo-ios-mainnet: xgo
|
statusgo-ios-mainnet: xgo
|
||||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/mainnet-flags.sh) ./cmd/statusd
|
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/mainnet-flags.sh) ./lib
|
||||||
@echo "iOS framework cross compilation done (mainnet)."
|
@echo "iOS framework cross compilation done (mainnet)."
|
||||||
|
|
||||||
statusgo-ios-simulator-mainnet: xgo
|
statusgo-ios-simulator-mainnet: xgo
|
||||||
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo-ios-simulator --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/mainnet-flags.sh) ./cmd/statusd
|
build/env.sh $(GOBIN)/xgo --image farazdagi/xgo-ios-simulator --go=$(GO) -out statusgo --dest=$(GOBIN) --targets=ios-9.3/framework -v $(shell build/mainnet-flags.sh) ./lib
|
||||||
@echo "iOS framework cross compilation done (mainnet)."
|
@echo "iOS framework cross compilation done (mainnet)."
|
||||||
|
|
||||||
generate: ##@other Regenerate assets and other auto-generated stuff
|
generate: ##@other Regenerate assets and other auto-generated stuff
|
||||||
|
@ -109,7 +112,7 @@ test-e2e: ##@tests Run e2e tests
|
||||||
build/env.sh go test -timeout 20m ./e2e/rpc/... -network=$(networkid)
|
build/env.sh go test -timeout 20m ./e2e/rpc/... -network=$(networkid)
|
||||||
build/env.sh go test -timeout 20m ./e2e/whisper/... -network=$(networkid)
|
build/env.sh go test -timeout 20m ./e2e/whisper/... -network=$(networkid)
|
||||||
build/env.sh go test -timeout 10m ./e2e/transactions/... -network=$(networkid)
|
build/env.sh go test -timeout 10m ./e2e/transactions/... -network=$(networkid)
|
||||||
build/env.sh go test -timeout 40m ./cmd/statusd -network=$(networkid)
|
build/env.sh go test -timeout 40m ./lib -network=$(networkid)
|
||||||
|
|
||||||
ci: lint mock-install mock test-unit test-e2e ##@tests Run all linters and tests at once
|
ci: lint mock-install mock test-unit test-e2e ##@tests Run all linters and tests at once
|
||||||
|
|
||||||
|
|
|
@ -1,66 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/status-im/status-go/geth/params"
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
faucetCommand = cli.Command{
|
|
||||||
Action: faucetCommandHandler,
|
|
||||||
Name: "faucet",
|
|
||||||
Usage: "Starts faucet node (light node used by faucet service to request Ether)",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
HTTPPortFlag,
|
|
||||||
HTTPEnabledFlag,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// faucetCommandHandler handles `statusd faucet` command
|
|
||||||
func faucetCommandHandler(ctx *cli.Context) error {
|
|
||||||
config, err := parseFaucetCommandConfig(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can not parse config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Starting Status Faucet node..")
|
|
||||||
if err = statusAPI.StartNode(config); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait till node has been stopped
|
|
||||||
node, err := statusAPI.NodeManager().Node()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
node.Wait()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseFaucetCommandConfig parses incoming CLI options and returns node configuration object
|
|
||||||
func parseFaucetCommandConfig(ctx *cli.Context) (*params.NodeConfig, error) {
|
|
||||||
nodeConfig, err := makeNodeConfig(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// select sub-protocols
|
|
||||||
nodeConfig.LightEthConfig.Enabled = true
|
|
||||||
nodeConfig.WhisperConfig.Enabled = false
|
|
||||||
nodeConfig.SwarmConfig.Enabled = false
|
|
||||||
|
|
||||||
// RPC configuration
|
|
||||||
nodeConfig.APIModules = "eth"
|
|
||||||
nodeConfig.HTTPHost = "0.0.0.0" // allow to connect from anywhere
|
|
||||||
nodeConfig.HTTPPort = ctx.Int(HTTPPortFlag.Name)
|
|
||||||
nodeConfig.RPCEnabled = ctx.Bool(HTTPEnabledFlag.Name)
|
|
||||||
|
|
||||||
// extra options
|
|
||||||
nodeConfig.BootClusterConfig.Enabled = true
|
|
||||||
|
|
||||||
return nodeConfig, nil
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/status-im/status-go/geth/params"
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
lesCommand = cli.Command{
|
|
||||||
Action: lesCommandHandler,
|
|
||||||
Name: "les",
|
|
||||||
Usage: "Starts Light Ethereum node",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
WhisperEnabledFlag,
|
|
||||||
SwarmEnabledFlag,
|
|
||||||
HTTPEnabledFlag,
|
|
||||||
HTTPPortFlag,
|
|
||||||
IPCEnabledFlag,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// lesCommandHandler handles `statusd les` command
|
|
||||||
func lesCommandHandler(ctx *cli.Context) error {
|
|
||||||
config, err := parseLESCommandConfig(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can not parse config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Starting Light Status node..")
|
|
||||||
if err = statusAPI.StartNode(config); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait till node has been stopped
|
|
||||||
node, err := statusAPI.NodeManager().Node()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
node.Wait()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// parseLESCommandConfig parses incoming CLI options and returns node configuration object
|
|
||||||
func parseLESCommandConfig(ctx *cli.Context) (*params.NodeConfig, error) {
|
|
||||||
nodeConfig, err := makeNodeConfig(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Enabled sub-protocols
|
|
||||||
nodeConfig.LightEthConfig.Enabled = true
|
|
||||||
nodeConfig.RPCEnabled = ctx.Bool(HTTPEnabledFlag.Name)
|
|
||||||
nodeConfig.WhisperConfig.Enabled = ctx.Bool(WhisperEnabledFlag.Name)
|
|
||||||
nodeConfig.SwarmConfig.Enabled = ctx.Bool(SwarmEnabledFlag.Name)
|
|
||||||
|
|
||||||
// RPC configuration
|
|
||||||
if !ctx.Bool(HTTPEnabledFlag.Name) {
|
|
||||||
nodeConfig.HTTPHost = "" // HTTP RPC is disabled
|
|
||||||
}
|
|
||||||
nodeConfig.HTTPPort = ctx.Int(HTTPPortFlag.Name)
|
|
||||||
nodeConfig.IPCEnabled = ctx.Bool(IPCEnabledFlag.Name)
|
|
||||||
|
|
||||||
return nodeConfig, nil
|
|
||||||
}
|
|
|
@ -1,174 +1,142 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"log"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/status-im/status-go/geth/api"
|
"github.com/status-im/status-go/geth/api"
|
||||||
"github.com/status-im/status-go/geth/params"
|
"github.com/status-im/status-go/geth/params"
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
gitCommit = "rely on linker: -ldflags -X main.GitCommit"
|
gitCommit = "N/A" // rely on linker: -ldflags -X main.GitCommit"
|
||||||
buildStamp = "rely on linker: -ldflags -X main.buildStamp"
|
buildStamp = "N/A" // rely on linker: -ldflags -X main.buildStamp"
|
||||||
app = makeApp(gitCommit)
|
|
||||||
statusAPI = api.NewStatusAPI()
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// ProdModeFlag is whether we need dev or production settings
|
prodMode = flag.Bool("production", false, "Whether production settings should be loaded")
|
||||||
ProdModeFlag = cli.BoolFlag{
|
nodeKeyFile = flag.String("nodekey", "", "P2P node key file (private key)")
|
||||||
Name: "production",
|
dataDir = flag.String("datadir", params.DataDir, "Data directory for the databases and keystore")
|
||||||
Usage: "Whether production settings should be loaded",
|
networkID = flag.Int("networkid", params.RopstenNetworkID, "Network identifier (integer, 1=Homestead, 3=Ropsten, 4=Rinkeby, 777=StatusChain)")
|
||||||
}
|
whisperEnabled = flag.Bool("shh", false, "SHH protocol enabled")
|
||||||
|
swarmEnabled = flag.Bool("swarm", false, "Swarm protocol enabled")
|
||||||
// NodeKeyFileFlag is a node key file to be used as node's private key
|
httpEnabled = flag.Bool("http", false, "HTTP RPC endpoint enabled (default: false)")
|
||||||
NodeKeyFileFlag = cli.StringFlag{
|
httpPort = flag.Int("httpport", params.HTTPPort, "HTTP RPC server's listening port")
|
||||||
Name: "nodekey",
|
ipcEnabled = flag.Bool("ipc", false, "IPC RPC endpoint enabled")
|
||||||
Usage: "P2P node key file (private key)",
|
logLevel = flag.String("log", "INFO", `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`)
|
||||||
}
|
logFile = flag.String("logfile", "", "Path to the log file")
|
||||||
|
version = flag.Bool("version", false, "Print version")
|
||||||
// DataDirFlag defines data directory for the node
|
|
||||||
DataDirFlag = cli.StringFlag{
|
|
||||||
Name: "datadir",
|
|
||||||
Usage: "Data directory for the databases and keystore",
|
|
||||||
Value: params.DataDir,
|
|
||||||
}
|
|
||||||
|
|
||||||
// NetworkIDFlag defines network ID
|
|
||||||
NetworkIDFlag = cli.IntFlag{
|
|
||||||
Name: "networkid",
|
|
||||||
Usage: "Network identifier (integer, 1=Homestead, 3=Ropsten, 4=Rinkeby)",
|
|
||||||
Value: params.RopstenNetworkID,
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhisperEnabledFlag flags whether Whisper is enabled or not
|
|
||||||
WhisperEnabledFlag = cli.BoolFlag{
|
|
||||||
Name: "shh",
|
|
||||||
Usage: "SHH protocol enabled",
|
|
||||||
}
|
|
||||||
|
|
||||||
// SwarmEnabledFlag flags whether Swarm is enabled or not
|
|
||||||
SwarmEnabledFlag = cli.BoolFlag{
|
|
||||||
Name: "swarm",
|
|
||||||
Usage: "Swarm protocol enabled",
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPEnabledFlag defines whether HTTP RPC endpoint should be opened or not
|
|
||||||
HTTPEnabledFlag = cli.BoolFlag{
|
|
||||||
Name: "http",
|
|
||||||
Usage: "HTTP RPC enpoint enabled (default: false)",
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPPortFlag defines HTTP RPC port to use (if HTTP RPC is enabled)
|
|
||||||
HTTPPortFlag = cli.IntFlag{
|
|
||||||
Name: "httpport",
|
|
||||||
Usage: "HTTP RPC server's listening port",
|
|
||||||
Value: params.HTTPPort,
|
|
||||||
}
|
|
||||||
|
|
||||||
// IPCEnabledFlag flags whether IPC is enabled or not
|
|
||||||
IPCEnabledFlag = cli.BoolFlag{
|
|
||||||
Name: "ipc",
|
|
||||||
Usage: "IPC RPC enpoint enabled",
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogLevelFlag defines a log reporting level
|
|
||||||
LogLevelFlag = cli.StringFlag{
|
|
||||||
Name: "log",
|
|
||||||
Usage: `Log level, one of: "ERROR", "WARN", "INFO", "DEBUG", and "TRACE"`,
|
|
||||||
Value: "",
|
|
||||||
}
|
|
||||||
|
|
||||||
// LogFileFlag defines a log filename
|
|
||||||
LogFileFlag = cli.StringFlag{
|
|
||||||
Name: "logfile",
|
|
||||||
Usage: `Path to the log file`,
|
|
||||||
Value: "",
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
|
||||||
// setup the app
|
|
||||||
app.Action = cli.ShowAppHelp
|
|
||||||
app.HideVersion = true // separate command prints version
|
|
||||||
app.Commands = []cli.Command{
|
|
||||||
versionCommand,
|
|
||||||
faucetCommand,
|
|
||||||
lesCommand,
|
|
||||||
wnodeCommand,
|
|
||||||
}
|
|
||||||
app.Flags = []cli.Flag{
|
|
||||||
ProdModeFlag,
|
|
||||||
NodeKeyFileFlag,
|
|
||||||
DataDirFlag,
|
|
||||||
NetworkIDFlag,
|
|
||||||
LogLevelFlag,
|
|
||||||
LogFileFlag,
|
|
||||||
}
|
|
||||||
app.Before = func(ctx *cli.Context) error {
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
app.After = func(ctx *cli.Context) error {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := app.Run(os.Args); err != nil {
|
flag.Usage = printUsage
|
||||||
fmt.Fprintln(os.Stderr, err)
|
flag.Parse()
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeApp creates an app with sane defaults.
|
config, err := makeNodeConfig()
|
||||||
func makeApp(gitCommit string) *cli.App {
|
if err != nil {
|
||||||
app := cli.NewApp()
|
log.Fatalf("Making config failed: %v", err)
|
||||||
app.Name = filepath.Base(os.Args[0])
|
return
|
||||||
app.Author = ""
|
|
||||||
//app.Authors = nil
|
|
||||||
app.Email = ""
|
|
||||||
app.Version = params.Version
|
|
||||||
if gitCommit != "" {
|
|
||||||
app.Version += "-" + gitCommit[:8]
|
|
||||||
}
|
}
|
||||||
app.Usage = "CLI for Status nodes management"
|
|
||||||
return app
|
if *version {
|
||||||
|
printVersion(config, gitCommit, buildStamp)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
backend := api.NewStatusBackend()
|
||||||
|
started, err := backend.StartNode(config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Node start failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait till node is started
|
||||||
|
<-started
|
||||||
|
|
||||||
|
// wait till node has been stopped
|
||||||
|
node, err := backend.NodeManager().Node()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Getting node failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// makeNodeConfig parses incoming CLI options and returns node configuration object
|
// makeNodeConfig parses incoming CLI options and returns node configuration object
|
||||||
func makeNodeConfig(ctx *cli.Context) (*params.NodeConfig, error) {
|
func makeNodeConfig() (*params.NodeConfig, error) {
|
||||||
nodeConfig, err := params.NewNodeConfig(
|
devMode := !*prodMode
|
||||||
ctx.GlobalString(DataDirFlag.Name),
|
nodeConfig, err := params.NewNodeConfig(*dataDir, uint64(*networkID), devMode)
|
||||||
ctx.GlobalUint64(NetworkIDFlag.Name),
|
|
||||||
!ctx.GlobalBool(ProdModeFlag.Name))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
nodeConfig.NodeKeyFile = ctx.GlobalString(NodeKeyFileFlag.Name)
|
// TODO(divan): move this logic into params package
|
||||||
|
if *nodeKeyFile != "" {
|
||||||
|
nodeConfig.NodeKeyFile = *nodeKeyFile
|
||||||
|
}
|
||||||
|
|
||||||
if logLevel := ctx.GlobalString(LogLevelFlag.Name); logLevel != "" {
|
if *logLevel != "" {
|
||||||
nodeConfig.LogLevel = logLevel
|
nodeConfig.LogLevel = *logLevel
|
||||||
}
|
}
|
||||||
if logFile := ctx.GlobalString(LogFileFlag.Name); logFile != "" {
|
if *logFile != "" {
|
||||||
nodeConfig.LogFile = logFile
|
nodeConfig.LogFile = *logFile
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nodeConfig.LightEthConfig.Enabled = true
|
||||||
|
nodeConfig.RPCEnabled = *httpEnabled
|
||||||
|
nodeConfig.WhisperConfig.Enabled = *whisperEnabled
|
||||||
|
nodeConfig.SwarmConfig.Enabled = *swarmEnabled
|
||||||
|
|
||||||
|
// RPC configuration
|
||||||
|
if !*httpEnabled {
|
||||||
|
nodeConfig.HTTPHost = "" // HTTP RPC is disabled
|
||||||
|
}
|
||||||
|
nodeConfig.HTTPPort = *httpPort
|
||||||
|
nodeConfig.IPCEnabled = *ipcEnabled
|
||||||
|
|
||||||
return nodeConfig, nil
|
return nodeConfig, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// printNodeConfig prints node config
|
// printVersion prints verbose output about version and config.
|
||||||
func printNodeConfig(ctx *cli.Context) {
|
func printVersion(config *params.NodeConfig, gitCommit, buildStamp string) {
|
||||||
nodeConfig, err := makeNodeConfig(ctx)
|
if gitCommit != "" && len(gitCommit) > 8 {
|
||||||
if err != nil {
|
params.Version += "-" + gitCommit[:8]
|
||||||
fmt.Printf("Loaded Config: failed (err: %v)", err)
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
nodeConfig.LightEthConfig.Genesis = "SKIP"
|
|
||||||
fmt.Println("Loaded Config: ", nodeConfig)
|
fmt.Println(strings.Title(params.ClientIdentifier))
|
||||||
|
fmt.Println("Version:", params.Version)
|
||||||
|
if gitCommit != "" {
|
||||||
|
fmt.Println("Git Commit:", gitCommit)
|
||||||
|
}
|
||||||
|
if buildStamp != "" {
|
||||||
|
fmt.Println("Build Stamp:", buildStamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Network Id:", config.NetworkID)
|
||||||
|
fmt.Println("Go Version:", runtime.Version())
|
||||||
|
fmt.Println("OS:", runtime.GOOS)
|
||||||
|
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
|
||||||
|
fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
|
||||||
|
|
||||||
|
config.LightEthConfig.Genesis = "SKIP"
|
||||||
|
fmt.Println("Loaded Config: ", config)
|
||||||
|
}
|
||||||
|
|
||||||
|
func printUsage() {
|
||||||
|
fmt.Fprintln(os.Stderr, "Usage: statusd [options]")
|
||||||
|
fmt.Fprintf(os.Stderr, `
|
||||||
|
Examples:
|
||||||
|
statusd # run status node with defaults
|
||||||
|
statusd -networkid 4 # run node on Rinkeby network
|
||||||
|
statusd -datadir /dir # specify different dir for data
|
||||||
|
statusd -ipc # enable IPC for usage with "geth attach"
|
||||||
|
|
||||||
|
Options:
|
||||||
|
`)
|
||||||
|
flag.PrintDefaults()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"runtime"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/status-im/status-go/geth/params"
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
versionCommand = cli.Command{
|
|
||||||
Action: versionCommandHandler,
|
|
||||||
Name: "version",
|
|
||||||
Usage: "Print app version",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// versionCommandHandler displays app version
|
|
||||||
func versionCommandHandler(ctx *cli.Context) error {
|
|
||||||
fmt.Println(strings.Title(params.ClientIdentifier))
|
|
||||||
fmt.Println("Version:", params.Version)
|
|
||||||
if gitCommit != "" {
|
|
||||||
fmt.Println("Git Commit:", gitCommit)
|
|
||||||
}
|
|
||||||
if buildStamp != "" {
|
|
||||||
fmt.Println("Build Stamp:", buildStamp)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Network Id:", ctx.GlobalInt(NetworkIDFlag.Name))
|
|
||||||
fmt.Println("Go Version:", runtime.Version())
|
|
||||||
fmt.Println("OS:", runtime.GOOS)
|
|
||||||
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
|
|
||||||
fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
|
|
||||||
|
|
||||||
printNodeConfig(ctx)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -1,269 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/status-im/status-go/geth/account"
|
|
||||||
"github.com/status-im/status-go/geth/common"
|
|
||||||
"github.com/status-im/status-go/geth/params"
|
|
||||||
"gopkg.in/urfave/cli.v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
// WhisperEchoModeFlag enables/disables Echo mode (arguments are printed for diagnostics)
|
|
||||||
WhisperEchoModeFlag = cli.BoolTFlag{
|
|
||||||
Name: "echo",
|
|
||||||
Usage: "Echo mode, prints some arguments for diagnostics (default: true)",
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhisperBootstrapNodeFlag marks node as not actively listening for incoming connections
|
|
||||||
WhisperBootstrapNodeFlag = cli.BoolTFlag{
|
|
||||||
Name: "bootstrap",
|
|
||||||
Usage: "Don't actively connect to peers, wait for incoming connections (default: true)",
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhisperNotificationServerNodeFlag enables/disables Push Notifications services
|
|
||||||
WhisperNotificationServerNodeFlag = cli.BoolFlag{
|
|
||||||
Name: "notify",
|
|
||||||
Usage: "Node is capable of sending Push Notifications",
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhisperForwarderNodeFlag enables/disables message forwarding
|
|
||||||
// (when neither sends nor decrypts envelopes, just forwards them)
|
|
||||||
WhisperForwarderNodeFlag = cli.BoolFlag{
|
|
||||||
Name: "forward",
|
|
||||||
Usage: "Only forward messages, neither send nor decrypt messages",
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhisperMailserverNodeFlag enables/disables Inboxing services
|
|
||||||
WhisperMailserverNodeFlag = cli.BoolFlag{
|
|
||||||
Name: "mailserver",
|
|
||||||
Usage: "Delivers expired messages on demand",
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhisperIdentityFile is path to file containing private key of the node (for asymmetric encryption)
|
|
||||||
WhisperIdentityFile = cli.StringFlag{
|
|
||||||
Name: "identity",
|
|
||||||
Usage: "Protocol identity file (private key used for asymmetric encryption)",
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhisperPasswordFile is password used to do a symmetric encryption
|
|
||||||
WhisperPasswordFile = cli.StringFlag{
|
|
||||||
Name: "password",
|
|
||||||
Usage: "Password file (password is used for symmetric encryption)",
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhisperPortFlag defines port on which Whisper protocol is listening
|
|
||||||
WhisperPortFlag = cli.IntFlag{
|
|
||||||
Name: "port",
|
|
||||||
Usage: "Whisper node's listening port",
|
|
||||||
Value: params.WhisperPort,
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhisperPoWFlag is the minimum PoW required by the node
|
|
||||||
WhisperPoWFlag = cli.Float64Flag{
|
|
||||||
Name: "pow",
|
|
||||||
Usage: "PoW for messages to be added to queue, in float format",
|
|
||||||
Value: params.WhisperMinimumPoW,
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhisperTTLFlag defines node's default TTL for envelopes
|
|
||||||
WhisperTTLFlag = cli.IntFlag{
|
|
||||||
Name: "ttl",
|
|
||||||
Usage: "Time to live for messages, in seconds",
|
|
||||||
Value: params.WhisperTTL,
|
|
||||||
}
|
|
||||||
|
|
||||||
// WhisperInjectTestAccounts if set, then test accounts will be imported
|
|
||||||
// into node's key store, and then will be injected as key pairs (identities)
|
|
||||||
// into the Whisper as well.
|
|
||||||
WhisperInjectTestAccounts = cli.BoolTFlag{
|
|
||||||
Name: "injectaccounts",
|
|
||||||
Usage: "Whether test account should be injected or not (default: true)",
|
|
||||||
}
|
|
||||||
|
|
||||||
// FirebaseAuthorizationKey path to file containing FCM password
|
|
||||||
FirebaseAuthorizationKey = cli.StringFlag{
|
|
||||||
Name: "firebaseauth",
|
|
||||||
Usage: "FCM Authorization Key used for sending Push Notifications",
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
wnodeCommand = cli.Command{
|
|
||||||
Action: wnode,
|
|
||||||
Name: "wnode",
|
|
||||||
Usage: "Starts Whisper/5 node",
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
WhisperEchoModeFlag,
|
|
||||||
WhisperBootstrapNodeFlag,
|
|
||||||
WhisperNotificationServerNodeFlag,
|
|
||||||
WhisperForwarderNodeFlag,
|
|
||||||
WhisperMailserverNodeFlag,
|
|
||||||
WhisperIdentityFile,
|
|
||||||
WhisperPasswordFile,
|
|
||||||
WhisperPoWFlag,
|
|
||||||
WhisperPortFlag,
|
|
||||||
WhisperTTLFlag,
|
|
||||||
WhisperInjectTestAccounts,
|
|
||||||
FirebaseAuthorizationKey,
|
|
||||||
HTTPEnabledFlag,
|
|
||||||
HTTPPortFlag,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// version displays app version
|
|
||||||
func wnode(ctx *cli.Context) error {
|
|
||||||
config, err := makeWhisperNodeConfig(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("can not parse config: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
wnodePrintHeader(config)
|
|
||||||
|
|
||||||
// import test accounts
|
|
||||||
if ctx.BoolT(WhisperInjectTestAccounts.Name) {
|
|
||||||
if err = common.ImportTestAccount(filepath.Join(config.DataDir, "keystore"), "test-account1.pk"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = common.ImportTestAccount(filepath.Join(config.DataDir, "keystore"), "test-account2.pk"); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = statusAPI.StartNode(config); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// inject test accounts into Whisper
|
|
||||||
if ctx.BoolT(WhisperInjectTestAccounts.Name) {
|
|
||||||
testConfig, _ := common.LoadTestConfig()
|
|
||||||
if err = injectAccountIntoWhisper(testConfig.Account1.Address, testConfig.Account1.Password); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err = injectAccountIntoWhisper(testConfig.Account2.Address, testConfig.Account2.Password); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// wait till node has been stopped
|
|
||||||
node, err := statusAPI.NodeManager().Node()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
node.Wait()
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// wnodePrintHeader prints command header
|
|
||||||
func wnodePrintHeader(nodeConfig *params.NodeConfig) {
|
|
||||||
fmt.Println("Starting Whisper/5 node..")
|
|
||||||
|
|
||||||
whisperConfig := nodeConfig.WhisperConfig
|
|
||||||
|
|
||||||
if whisperConfig.EchoMode {
|
|
||||||
fmt.Printf("Whisper Config: %s\n", whisperConfig)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// makeWhisperNodeConfig parses incoming CLI options and returns node configuration object
|
|
||||||
func makeWhisperNodeConfig(ctx *cli.Context) (*params.NodeConfig, error) {
|
|
||||||
nodeConfig, err := makeNodeConfig(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
nodeConfig.LightEthConfig.Enabled = false
|
|
||||||
|
|
||||||
whisperConfig := nodeConfig.WhisperConfig
|
|
||||||
|
|
||||||
whisperConfig.Enabled = true
|
|
||||||
whisperConfig.IdentityFile = ctx.String(WhisperIdentityFile.Name)
|
|
||||||
whisperConfig.PasswordFile = ctx.String(WhisperPasswordFile.Name)
|
|
||||||
whisperConfig.EchoMode = ctx.BoolT(WhisperEchoModeFlag.Name)
|
|
||||||
whisperConfig.BootstrapNode = ctx.BoolT(WhisperBootstrapNodeFlag.Name)
|
|
||||||
whisperConfig.ForwarderNode = ctx.Bool(WhisperForwarderNodeFlag.Name)
|
|
||||||
whisperConfig.NotificationServerNode = ctx.Bool(WhisperNotificationServerNodeFlag.Name)
|
|
||||||
whisperConfig.MailServerNode = ctx.Bool(WhisperMailserverNodeFlag.Name)
|
|
||||||
whisperConfig.Port = ctx.Int(WhisperPortFlag.Name)
|
|
||||||
whisperConfig.TTL = ctx.Int(WhisperTTLFlag.Name)
|
|
||||||
whisperConfig.MinimumPoW = ctx.Float64(WhisperPoWFlag.Name)
|
|
||||||
|
|
||||||
if whisperConfig.MailServerNode && len(whisperConfig.PasswordFile) == 0 {
|
|
||||||
return nil, errors.New("mail server requires --password to be specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
if whisperConfig.NotificationServerNode && len(whisperConfig.IdentityFile) == 0 {
|
|
||||||
return nil, errors.New("notification server requires either --identity file to be specified")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(whisperConfig.PasswordFile) > 0 { // make sure that we can load password file
|
|
||||||
if whisperConfig.PasswordFile, err = filepath.Abs(whisperConfig.PasswordFile); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err = whisperConfig.ReadPasswordFile(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(whisperConfig.IdentityFile) > 0 { // make sure that we can load identity file
|
|
||||||
if whisperConfig.IdentityFile, err = filepath.Abs(whisperConfig.IdentityFile); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err = whisperConfig.ReadIdentityFile(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
firebaseConfig := whisperConfig.FirebaseConfig
|
|
||||||
firebaseConfig.AuthorizationKeyFile = ctx.String(FirebaseAuthorizationKey.Name)
|
|
||||||
if len(firebaseConfig.AuthorizationKeyFile) > 0 { // make sure authorization key can be loaded
|
|
||||||
if firebaseConfig.AuthorizationKeyFile, err = filepath.Abs(firebaseConfig.AuthorizationKeyFile); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if _, err := firebaseConfig.ReadAuthorizationKeyFile(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// RPC configuration
|
|
||||||
if !ctx.Bool(HTTPEnabledFlag.Name) {
|
|
||||||
nodeConfig.HTTPHost = "" // HTTP RPC is disabled
|
|
||||||
}
|
|
||||||
nodeConfig.HTTPPort = ctx.Int(HTTPPortFlag.Name)
|
|
||||||
|
|
||||||
return nodeConfig, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// injectAccountIntoWhisper adds key pair into Whisper. Similar to Select/Login,
|
|
||||||
// but allows multiple accounts to be injected.
|
|
||||||
func injectAccountIntoWhisper(address, password string) error {
|
|
||||||
nodeManager := statusAPI.NodeManager()
|
|
||||||
keyStore, err := nodeManager.AccountKeyStore()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
acct, err := common.ParseAccountString(address)
|
|
||||||
if err != nil {
|
|
||||||
return account.ErrAddressToAccountMappingFailure
|
|
||||||
}
|
|
||||||
|
|
||||||
_, accountKey, err := keyStore.AccountDecryptedKey(acct, password)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("%s: %v", account.ErrAccountToKeyMappingFailure.Error(), err)
|
|
||||||
}
|
|
||||||
|
|
||||||
whisperService, err := nodeManager.WhisperService()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if _, err = whisperService.AddKeyPair(accountKey.PrivateKey); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
|
@ -0,0 +1,113 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/geth/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
// makeNodeConfig creates node configuration object from flags
|
||||||
|
func makeNodeConfig() (*params.NodeConfig, error) {
|
||||||
|
devMode := !*prodMode
|
||||||
|
nodeConfig, err := params.NewNodeConfig(*dataDir, uint64(*networkID), devMode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO(divan): move this logic into params package?
|
||||||
|
if *nodeKeyFile != "" {
|
||||||
|
nodeConfig.NodeKeyFile = *nodeKeyFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// disable log
|
||||||
|
nodeConfig.LogLevel = "CRIT"
|
||||||
|
nodeConfig.LogFile = ""
|
||||||
|
|
||||||
|
// disable les and swarm for wnode
|
||||||
|
nodeConfig.LightEthConfig.Enabled = false
|
||||||
|
nodeConfig.SwarmConfig.Enabled = false
|
||||||
|
|
||||||
|
nodeConfig.RPCEnabled = *httpEnabled
|
||||||
|
|
||||||
|
// whisper configuration
|
||||||
|
whisperConfig := nodeConfig.WhisperConfig
|
||||||
|
|
||||||
|
whisperConfig.Enabled = true
|
||||||
|
whisperConfig.IdentityFile = *identity
|
||||||
|
whisperConfig.PasswordFile = *password
|
||||||
|
whisperConfig.EchoMode = *echo
|
||||||
|
whisperConfig.BootstrapNode = *bootstrap
|
||||||
|
whisperConfig.ForwarderNode = *forward
|
||||||
|
whisperConfig.NotificationServerNode = *notify
|
||||||
|
whisperConfig.MailServerNode = *mailserver
|
||||||
|
whisperConfig.Port = *port
|
||||||
|
whisperConfig.TTL = *ttl
|
||||||
|
whisperConfig.MinimumPoW = *pow
|
||||||
|
|
||||||
|
if whisperConfig.MailServerNode && whisperConfig.PasswordFile == "" {
|
||||||
|
return nil, errors.New("mail server requires -password to be specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
if whisperConfig.NotificationServerNode && whisperConfig.IdentityFile == "" {
|
||||||
|
return nil, errors.New("notification server requires either -identity file to be specified")
|
||||||
|
}
|
||||||
|
|
||||||
|
if whisperConfig.PasswordFile != "" {
|
||||||
|
if err := verifyPasswordFile(whisperConfig); err != nil {
|
||||||
|
return nil, fmt.Errorf("read password file: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if whisperConfig.IdentityFile != "" {
|
||||||
|
if err := verifyIdentityFile(whisperConfig); err != nil {
|
||||||
|
return nil, fmt.Errorf("read identity file: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// firebase configuration
|
||||||
|
firebaseConfig := whisperConfig.FirebaseConfig
|
||||||
|
firebaseConfig.AuthorizationKeyFile = *firebaseAuth
|
||||||
|
if len(firebaseConfig.AuthorizationKeyFile) > 0 { // make sure authorization key can be loaded
|
||||||
|
if firebaseConfig.AuthorizationKeyFile, err = filepath.Abs(firebaseConfig.AuthorizationKeyFile); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if _, err := firebaseConfig.ReadAuthorizationKeyFile(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RPC configuration
|
||||||
|
if !*httpEnabled {
|
||||||
|
nodeConfig.HTTPHost = "" // HTTP RPC is disabled
|
||||||
|
}
|
||||||
|
nodeConfig.HTTPPort = *httpPort
|
||||||
|
nodeConfig.IPCEnabled = *ipcEnabled
|
||||||
|
|
||||||
|
return nodeConfig, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyPasswordFile verifies that we can load password file
|
||||||
|
func verifyPasswordFile(config *params.WhisperConfig) error {
|
||||||
|
// TODO(divan): why do we need it here?
|
||||||
|
absPath, err := filepath.Abs(config.PasswordFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config.PasswordFile = absPath
|
||||||
|
_, err = config.ReadPasswordFile()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyIdentityFile verifies that we can load identity file
|
||||||
|
func verifyIdentityFile(config *params.WhisperConfig) error {
|
||||||
|
// TODO(divan): why do we need it here?
|
||||||
|
absPath, err := filepath.Abs(config.IdentityFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config.IdentityFile = absPath
|
||||||
|
_, err = config.ReadIdentityFile()
|
||||||
|
return err
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/geth/api"
|
||||||
|
"github.com/status-im/status-go/geth/params"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
prodMode = flag.Bool("production", false, "Whether production settings should be loaded")
|
||||||
|
nodeKeyFile = flag.String("nodekey", "", "P2P node key file (private key)")
|
||||||
|
dataDir = flag.String("datadir", "wnode-status-data", "Data directory for the databases and keystore")
|
||||||
|
networkID = flag.Int("networkid", params.RopstenNetworkID, "Network identifier (integer, 1=Homestead, 3=Ropsten, 4=Rinkeby)")
|
||||||
|
httpEnabled = flag.Bool("http", false, "HTTP RPC enpoint enabled (default: false)")
|
||||||
|
httpPort = flag.Int("httpport", params.HTTPPort, "HTTP RPC server's listening port")
|
||||||
|
ipcEnabled = flag.Bool("ipc", false, "IPC RPC enpoint enabled")
|
||||||
|
|
||||||
|
// wnode specific flags
|
||||||
|
echo = flag.Bool("echo", true, "Echo mode, prints some arguments for diagnostics")
|
||||||
|
bootstrap = flag.Bool("bootstrap", true, "Don't actively connect to peers, wait for incoming connections")
|
||||||
|
notify = flag.Bool("notify", false, "Node is capable of sending Push Notifications")
|
||||||
|
forward = flag.Bool("forward", false, "Only forward messages, neither send nor decrypt messages")
|
||||||
|
mailserver = flag.Bool("mailserver", false, "Delivers expired messages on demand")
|
||||||
|
identity = flag.String("identity", "", "Protocol identity file (private key used for asymmetric encryption)")
|
||||||
|
password = flag.String("password", "", "Password file (password is used for symmetric encryption)")
|
||||||
|
port = flag.Int("port", params.WhisperPort, "Whisper node's listening port")
|
||||||
|
pow = flag.Float64("pow", params.WhisperMinimumPoW, "PoW for messages to be added to queue, in float format")
|
||||||
|
ttl = flag.Int("ttl", params.WhisperTTL, "Time to live for messages, in seconds")
|
||||||
|
injectAccounts = flag.Bool("injectaccounts", true, "Whether test account should be injected or not")
|
||||||
|
firebaseAuth = flag.String("firebaseauth", "", "FCM Authorization Key used for sending Push Notifications")
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
config, err := makeNodeConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Making config failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
printHeader(config)
|
||||||
|
|
||||||
|
if *injectAccounts {
|
||||||
|
if err := LoadTestAccounts(config.DataDir); err != nil {
|
||||||
|
log.Fatalf("Failed to load test accounts: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backend := api.NewStatusBackend()
|
||||||
|
started, err := backend.StartNode(config)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Node start failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait till node is started
|
||||||
|
<-started
|
||||||
|
|
||||||
|
if *injectAccounts {
|
||||||
|
if err := InjectTestAccounts(backend.NodeManager()); err != nil {
|
||||||
|
log.Fatalf("Failed to inject accounts: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// wait till node has been stopped
|
||||||
|
node, err := backend.NodeManager().Node()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Getting node failed: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
node.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
// printHeader prints command header
|
||||||
|
func printHeader(config *params.NodeConfig) {
|
||||||
|
fmt.Println("Starting Whisper/5 node..")
|
||||||
|
if config.WhisperConfig.EchoMode {
|
||||||
|
fmt.Printf("Whisper Config: %s\n", config.WhisperConfig)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/status-im/status-go/geth/account"
|
||||||
|
"github.com/status-im/status-go/geth/common"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LoadTestAccounts loads public key files for test accounts
|
||||||
|
func LoadTestAccounts(dataDir string) error {
|
||||||
|
files := []string{"test-account1.pk", "test-account2.pk"}
|
||||||
|
dir := filepath.Join(dataDir, "keystore")
|
||||||
|
for _, filename := range files {
|
||||||
|
if err := common.ImportTestAccount(dir, filename); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InjectTestAccounts injects test accounts into running node
|
||||||
|
func InjectTestAccounts(node common.NodeManager) error {
|
||||||
|
testConfig, err := common.LoadTestConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = injectAccountIntoWhisper(node, testConfig.Account1.Address,
|
||||||
|
testConfig.Account1.Password); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = injectAccountIntoWhisper(node, testConfig.Account2.Address,
|
||||||
|
testConfig.Account2.Password); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// injectAccountIntoWhisper adds key pair into Whisper. Similar to Select/Login,
|
||||||
|
// but allows multiple accounts to be injected.
|
||||||
|
func injectAccountIntoWhisper(node common.NodeManager, address, password string) error {
|
||||||
|
keyStore, err := node.AccountKeyStore()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
acct, err := common.ParseAccountString(address)
|
||||||
|
if err != nil {
|
||||||
|
return account.ErrAddressToAccountMappingFailure
|
||||||
|
}
|
||||||
|
|
||||||
|
_, accountKey, err := keyStore.AccountDecryptedKey(acct, password)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%s: %v", account.ErrAccountToKeyMappingFailure.Error(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
whisperService, err := node.WhisperService()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if _, err = whisperService.AddKeyPair(accountKey.PrivateKey); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/status-im/status-go/geth/api"
|
||||||
|
|
||||||
|
var statusAPI = api.NewStatusAPI()
|
||||||
|
|
||||||
|
// Technically this package supposed to be a lib for
|
||||||
|
// cross-compilation and usage with Android/iOS, but
|
||||||
|
// without main it produces cryptic errors.
|
||||||
|
// TODO(divan): investigate the cause of the errors
|
||||||
|
// and change this package to be a library if possible.
|
||||||
|
func main() {
|
||||||
|
}
|
|
@ -10,9 +10,9 @@ describe('Whisper Tests', function () {
|
||||||
node1.setProvider(new web3.providers.HttpProvider('http://localhost:8645'));
|
node1.setProvider(new web3.providers.HttpProvider('http://localhost:8645'));
|
||||||
node2.setProvider(new web3.providers.HttpProvider('http://localhost:8745'));
|
node2.setProvider(new web3.providers.HttpProvider('http://localhost:8745'));
|
||||||
|
|
||||||
console.log('Node is expected: statusd --datadir app1 wnode --http --httpport 8645');
|
console.log('Node is expected: wnode-status -datadir app1 wnode -http -httpport 8645');
|
||||||
console.log('Node is expected: statusd --datadir app2 wnode --http --httpport 8745');
|
console.log('Node is expected: wnode-status -datadir app2 wnode -http -httpport 8745');
|
||||||
console.log('Node is expected: statusd --datadir wnode1 wnode --notify --injectaccounts=false --identity ./static/keys/wnodekey --firebaseauth ./static/keys/firebaseauthkey');
|
console.log('Node is expected: wnode-status -datadir wnode1 wnode -notify -injectaccounts=false -identity ./static/keys/wnodekey -firebaseauth ./static/keys/firebaseauthkey');
|
||||||
|
|
||||||
// some common vars
|
// some common vars
|
||||||
var topic1 = '0xdeadbeef'; // each topic 4 bytes, as hex
|
var topic1 = '0xdeadbeef'; // each topic 4 bytes, as hex
|
||||||
|
|
Loading…
Reference in New Issue