Merge pull request #158 from farazdagi/feature/split-cluster

Bootcluster (Rinkeby, Homestead) + Dev/Prod Mode
This commit is contained in:
Roman Volosovskyi 2017-05-16 13:05:16 +03:00 committed by GitHub
commit 697baa93f2
42 changed files with 1453 additions and 397 deletions

1
.dockerignore Normal file
View File

@ -0,0 +1 @@
*

15
Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM alpine:3.5
RUN \
apk add --update go git make gcc musl-dev linux-headers ca-certificates && \
git clone --depth 1 --branch feature/statusd-replaces-geth-on-cluster https://github.com/farazdagi/status-go && \
(cd status-go && make) && \
cp status-go/build/bin/statusd /statusd && \
apk del go git make gcc musl-dev linux-headers && \
rm -rf /status-go && rm -rf /var/cache/apk/*
EXPOSE 8545
EXPOSE 30303
EXPOSE 3001
ENTRYPOINT ["/statusd"]

61
cmd/statusd/faucetcmd.go Normal file
View File

@ -0,0 +1,61 @@
package main
import (
"fmt"
"github.com/status-im/status-go/geth"
"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,
},
}
)
// 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 = geth.CreateAndRunNode(config); err != nil {
return err
}
// wait till node has been stopped
geth.NodeManagerInstance().Node().GethStack().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)
// extra options
nodeConfig.BootClusterConfig.Enabled = true
return nodeConfig, nil
}

64
cmd/statusd/lescmd.go Normal file
View File

@ -0,0 +1,64 @@
package main
import (
"fmt"
"github.com/status-im/status-go/geth"
"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 = geth.CreateAndRunNode(config); err != nil {
return err
}
// wait till node has been stopped
geth.NodeManagerInstance().Node().GethStack().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.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
}

View File

@ -179,8 +179,8 @@ func DiscardTransactions(ids *C.char) *C.char {
}
//export GenerateConfig
func GenerateConfig(datadir *C.char, networkID C.int) *C.char {
config, err := params.NewNodeConfig(C.GoString(datadir), uint64(networkID))
func GenerateConfig(datadir *C.char, networkID C.int, devMode C.int) *C.char {
config, err := params.NewNodeConfig(C.GoString(datadir), uint64(networkID), devMode == 1)
if err != nil {
return makeJSONErrorResponse(err)
}

View File

@ -6,7 +6,6 @@ import (
"path/filepath"
"runtime"
"github.com/status-im/status-go/geth"
"github.com/status-im/status-go/geth/params"
"gopkg.in/urfave/cli.v1"
)
@ -18,6 +17,12 @@ var (
)
var (
// ProdModeFlag is whether we need dev or production settings
ProdModeFlag = cli.BoolFlag{
Name: "production",
Usage: "Whether production settings should be loaded",
}
// NodeKeyFileFlag is a node key file to be used as node's private key
NodeKeyFileFlag = cli.StringFlag{
Name: "nodekey",
@ -34,8 +39,8 @@ var (
// NetworkIDFlag defines network ID
NetworkIDFlag = cli.IntFlag{
Name: "networkid",
Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten)",
Value: params.TestNetworkID,
Usage: "Network identifier (integer, 1=Homestead, 3=Ropsten, 4=Rinkeby)",
Value: params.RopstenNetworkID,
}
// LightEthEnabledFlag flags whether LES is enabled or not
@ -85,23 +90,19 @@ var (
func init() {
// setup the app
app.Action = statusd
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,
LightEthEnabledFlag,
WhisperEnabledFlag,
SwarmEnabledFlag,
HTTPEnabledFlag,
HTTPPortFlag,
IPCEnabledFlag,
LogLevelFlag,
}
app.Before = func(ctx *cli.Context) error {
@ -120,49 +121,6 @@ func main() {
}
}
// statusd runs Status node
func statusd(ctx *cli.Context) error {
config, err := makeNodeConfig(ctx)
if err != nil {
fmt.Fprintf(os.Stderr, "can not parse config: %v", err)
return err
}
if err := geth.CreateAndRunNode(config); err != nil {
return err
}
// wait till node has been stopped
geth.NodeManagerInstance().Node().GethStack().Wait()
return nil
}
// makeNodeConfig parses incoming CLI options and returns node configuration object
func makeNodeConfig(ctx *cli.Context) (*params.NodeConfig, error) {
nodeConfig, err := params.NewNodeConfig(ctx.GlobalString(DataDirFlag.Name), ctx.GlobalUint64(NetworkIDFlag.Name))
if err != nil {
return nil, err
}
nodeConfig.NodeKeyFile = ctx.GlobalString(NodeKeyFileFlag.Name)
if !ctx.GlobalBool(HTTPEnabledFlag.Name) {
nodeConfig.HTTPHost = "" // HTTP RPC is disabled
}
nodeConfig.IPCEnabled = ctx.GlobalBool(IPCEnabledFlag.Name)
nodeConfig.LightEthConfig.Enabled = ctx.GlobalBool(LightEthEnabledFlag.Name)
nodeConfig.WhisperConfig.Enabled = ctx.GlobalBool(WhisperEnabledFlag.Name)
nodeConfig.SwarmConfig.Enabled = ctx.GlobalBool(SwarmEnabledFlag.Name)
nodeConfig.HTTPPort = ctx.GlobalInt(HTTPPortFlag.Name)
if logLevel := ctx.GlobalString(LogLevelFlag.Name); len(logLevel) > 0 {
nodeConfig.LogEnabled = true
nodeConfig.LogLevel = logLevel
}
return nodeConfig, nil
}
// makeApp creates an app with sane defaults.
func makeApp(gitCommit string) *cli.App {
app := cli.NewApp()
@ -174,6 +132,37 @@ func makeApp(gitCommit string) *cli.App {
if gitCommit != "" {
app.Version += "-" + gitCommit[:8]
}
app.Usage = "Status CLI"
app.Usage = "CLI for Status nodes management"
return app
}
// makeNodeConfig parses incoming CLI options and returns node configuration object
func makeNodeConfig(ctx *cli.Context) (*params.NodeConfig, error) {
nodeConfig, err := params.NewNodeConfig(
ctx.GlobalString(DataDirFlag.Name),
ctx.GlobalUint64(NetworkIDFlag.Name),
!ctx.GlobalBool(ProdModeFlag.Name))
if err != nil {
return nil, err
}
nodeConfig.NodeKeyFile = ctx.GlobalString(NodeKeyFileFlag.Name)
if logLevel := ctx.GlobalString(LogLevelFlag.Name); len(logLevel) > 0 {
nodeConfig.LogEnabled = true
nodeConfig.LogLevel = logLevel
}
return nodeConfig, nil
}
// printNodeConfig prints node config
func printNodeConfig(ctx *cli.Context) {
nodeConfig, err := makeNodeConfig(ctx)
if err != nil {
fmt.Printf("Loaded Config: failed (err: %v)", err)
return
}
nodeConfig.LightEthConfig.Genesis = "SKIP"
fmt.Println("Loaded Config: ", nodeConfig)
}

View File

@ -12,14 +12,14 @@ import (
var (
versionCommand = cli.Command{
Action: version,
Action: versionCommandHandler,
Name: "version",
Usage: "Print app version",
}
)
// version displays app version
func version(ctx *cli.Context) error {
// versionCommandHandler displays app version
func versionCommandHandler(ctx *cli.Context) error {
fmt.Println(strings.Title(params.ClientIdentifier))
fmt.Println("Version:", params.Version)
if gitCommit != "" {
@ -35,5 +35,7 @@ func version(ctx *cli.Context) error {
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
printNodeConfig(ctx)
return nil
}

View File

@ -153,7 +153,7 @@ func testGetDefaultConfig(t *testing.T) bool {
// test Mainnet config
nodeConfig := params.NodeConfig{}
rawResponse := GenerateConfig(C.CString("/tmp/data-folder"), 1)
rawResponse := GenerateConfig(C.CString("/tmp/data-folder"), 1, 1)
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &nodeConfig); err != nil {
t.Errorf("cannot decode response (%s): %v", C.GoString(rawResponse), err)
return false
@ -200,7 +200,7 @@ func testGetDefaultConfig(t *testing.T) bool {
// test Testnet
nodeConfig = params.NodeConfig{}
rawResponse = GenerateConfig(C.CString("/tmp/data-folder"), 3)
rawResponse = GenerateConfig(C.CString("/tmp/data-folder"), 3, 1)
if err := json.Unmarshal([]byte(C.GoString(rawResponse)), &nodeConfig); err != nil {
t.Errorf("cannot decode response (%s): %v", C.GoString(rawResponse), err)
return false
@ -1430,7 +1430,7 @@ func startTestNode(t *testing.T) <-chan struct{} {
go func() {
configJSON := `{
"NetworkId": ` + strconv.Itoa(params.TestNetworkID) + `,
"NetworkId": ` + strconv.Itoa(params.RopstenNetworkID) + `,
"DataDir": "` + geth.TestDataDir + `",
"HTTPPort": ` + strconv.Itoa(testConfig.Node.HTTPPort) + `,
"WSPort": ` + strconv.Itoa(testConfig.Node.WSPort) + `,

View File

@ -108,6 +108,8 @@ var (
WhisperTTLFlag,
WhisperInjectTestAccounts,
FirebaseAuthorizationKey,
HTTPEnabledFlag,
HTTPPortFlag,
},
}
)
@ -170,6 +172,8 @@ func makeWhisperNodeConfig(ctx *cli.Context) (*params.NodeConfig, error) {
return nil, err
}
nodeConfig.LightEthConfig.Enabled = false
whisperConfig := nodeConfig.WhisperConfig
whisperConfig.Enabled = true
@ -221,6 +225,12 @@ func makeWhisperNodeConfig(ctx *cli.Context) (*params.NodeConfig, error) {
}
}
// RPC configuration
if !ctx.Bool(HTTPEnabledFlag.Name) {
nodeConfig.HTTPHost = "" // HTTP RPC is disabled
}
nodeConfig.HTTPPort = ctx.Int(HTTPPortFlag.Name)
return nodeConfig, nil
}

View File

@ -1110,7 +1110,7 @@ func TestJailWhisper(t *testing.T) {
for testKey, filter := range installedFilters {
if filter != "" {
t.Logf("filter found: %v", filter)
for _, message := range whisperAPI.GetSubscriptionMessages(filter) {
for _, message := range whisperAPI.GetNewSubscriptionMessages(filter) {
t.Logf("message found: %s", common.FromHex(message.Payload))
passedTests[testKey] = true
}

View File

@ -80,33 +80,7 @@ func MakeNode(config *params.NodeConfig) *Node {
}
// configure required node (should you need to update node's config, e.g. add bootstrap nodes, see node.Config)
stackConfig := &node.Config{
DataDir: config.DataDir,
KeyStoreDir: config.KeyStoreDir,
UseLightweightKDF: true,
Name: config.Name,
Version: config.Version,
P2P: p2p.Config{
NoDiscovery: true,
DiscoveryV5: false,
DiscoveryV5Addr: ":0",
BootstrapNodes: makeBootstrapNodes(),
BootstrapNodesV5: makeBootstrapNodesV5(),
ListenAddr: ":0",
NAT: nat.Any(),
MaxPeers: config.MaxPeers,
MaxPendingPeers: config.MaxPendingPeers,
},
IPCPath: makeIPCPath(config),
HTTPHost: config.HTTPHost,
HTTPPort: config.HTTPPort,
HTTPCors: []string{"*"},
HTTPModules: strings.Split(config.APIModules, ","),
WSHost: makeWSHost(config),
WSPort: config.WSPort,
WSOrigins: []string{"*"},
WSModules: strings.Split(config.APIModules, ","),
}
stackConfig := defaultEmbeddedNodeConfig(config)
if len(config.NodeKeyFile) > 0 {
log.Info("Loading private key file", "file", config.NodeKeyFile)
@ -153,6 +127,37 @@ func MakeNode(config *params.NodeConfig) *Node {
}
}
// defaultEmbeddedNodeConfig returns default stack configuration for mobile client node
func defaultEmbeddedNodeConfig(config *params.NodeConfig) *node.Config {
return &node.Config{
DataDir: config.DataDir,
KeyStoreDir: config.KeyStoreDir,
UseLightweightKDF: true,
Name: config.Name,
Version: config.Version,
P2P: p2p.Config{
NoDiscovery: true,
DiscoveryV5: false,
DiscoveryV5Addr: ":0",
BootstrapNodes: makeBootstrapNodes(),
BootstrapNodesV5: makeBootstrapNodesV5(),
ListenAddr: ":0",
NAT: nat.Any(),
MaxPeers: config.MaxPeers,
MaxPendingPeers: config.MaxPendingPeers,
},
IPCPath: makeIPCPath(config),
HTTPHost: config.HTTPHost,
HTTPPort: config.HTTPPort,
HTTPCors: []string{"*"},
HTTPModules: strings.Split(config.APIModules, ","),
WSHost: makeWSHost(config),
WSPort: config.WSPort,
WSOrigins: []string{"*"},
WSModules: strings.Split(config.APIModules, ","),
}
}
// activateEthService configures and registers the eth.Ethereum service with a given node.
func activateEthService(stack *node.Node, config *params.NodeConfig) error {
if !config.LightEthConfig.Enabled {

View File

@ -413,8 +413,18 @@ func (m *NodeManager) onNodeStarted() {
// PopulateStaticPeers connects current node with our publicly available LES/SHH/Swarm cluster
func (m *NodeManager) PopulateStaticPeers() {
for _, enode := range params.TestnetBootnodes {
if !m.node.config.BootClusterConfig.Enabled {
log.Info("Boot cluster is disabled")
return
}
enodes, err := m.node.config.LoadBootClusterNodes()
if err != nil {
log.Warn("Can not load boot nodes", "error", err)
}
for _, enode := range enodes {
m.AddPeer(enode) // nolint: errcheck
log.Info("Boot node added", "enode", enode)
}
}

View File

@ -1,51 +0,0 @@
package params
// MainnetBootnodes are the enode URLs of the P2P bootstrap nodes running on the main Ethereum network.
var MainnetBootnodes = []string{}
// TestnetBootnodes are the enode URLs of the P2P bootstrap nodes running on the Ropsten test network.
var TestnetBootnodes = []string{
"enode://bc4a130219ae94c2a66e3ec3377c2a460e006d56b877d5e8edb0d0f8064cf400f117a53a5389f552c9e1a122b9a07eeaa41e7ed885268ee825b6a788188fb52e@51.15.55.219:30379",
//"enode://bc4a130219ae94c2a66e3ec3377c2a460e006d56b877d5e8edb0d0f8064cf400f117a53a5389f552c9e1a122b9a07eeaa41e7ed885268ee825b6a788188fb52e@51.15.55.219:30399",
"enode://fbddff478e18292dc32b90f139bf773a08da89ffe29208e4de0091f6c589e60fccfaf16d4f4a76be49f57782c061ec8ea97078601c6f367feabda740f5ce8246@51.15.55.219:30303",
"enode://df20352d07924ffe93e67ee7e81105d7b250f7932ff4e0351da2cfa17282e53f765895c32cda36b70dfb98def0b472b29d79321737035641bf3bcf595dcc1041@51.15.35.110:30379",
//"enode://df20352d07924ffe93e67ee7e81105d7b250f7932ff4e0351da2cfa17282e53f765895c32cda36b70dfb98def0b472b29d79321737035641bf3bcf595dcc1041@51.15.35.110:30399",
"enode://4e5ee0487a4d8349ab9a9925b00eed0f976d98972c5a22f43fd50d1424897757032c36f273b434a4d3e013a2544eca74a9d1a0419f9f07f7bb43182a73df3690@51.15.35.110:30303",
"enode://2cbcc7b3d067581072066143e0fade0d007e80ecc8f86eb475200c3da3a6d81dd4e1e7051fc3dfaee337f110ceec61594a901b09e36eb367629ddff6e1dfd955@51.15.34.3:30379",
//"enode://2cbcc7b3d067581072066143e0fade0d007e80ecc8f86eb475200c3da3a6d81dd4e1e7051fc3dfaee337f110ceec61594a901b09e36eb367629ddff6e1dfd955@51.15.34.3:30399",
"enode://18efd9afb60443e00fed602cc0df526cd1d8543d2f6037df9380eb973d30b5fd04ac9f221053f82034581051bfd6e54356a99af2255f1a674d71d17440a6c95b@51.15.34.3:30303",
"enode://cdb95f3d866472a74195342979ffea4ed7f9b68cd1e8c6f9a25b3197c221f01bc076ccba760341d8b69bb6bfbc9bf4fdeabd0caa99ee0bf4e79917fa1f42423c@51.15.56.154:30379",
//"enode://cdb95f3d866472a74195342979ffea4ed7f9b68cd1e8c6f9a25b3197c221f01bc076ccba760341d8b69bb6bfbc9bf4fdeabd0caa99ee0bf4e79917fa1f42423c@51.15.56.154:30399",
"enode://5b99c0cb372299fd3f2d94612a682990722eb7c3a252dacefc8270eb7f172fc699c1ddfad826fbfc979270538e8d89bd6919703eb9ef526eac0a45e9fb455123@51.15.56.154:30303",
"enode://5ce8e96d9589671767a7b1c6b9a34bcf532587387eb062de712a9f716a66f05f412126121ce4d97330bc5dc7a4938ff1ecc22306b0b8b97a7f748c6f5f59c620@51.15.60.23:30379",
//"enode://5ce8e96d9589671767a7b1c6b9a34bcf532587387eb062de712a9f716a66f05f412126121ce4d97330bc5dc7a4938ff1ecc22306b0b8b97a7f748c6f5f59c620@51.15.60.23:30399",
"enode://0e1d4d0fcfe888bf8a478b0fd89760a47733a5c04cd47de353295a6eb8dde8f54821b31196527d0c5c73a7024dc9ff34127692d237840fc09c312b3a19cd28fe@51.15.60.23:30303",
"enode://6853f434735e540f0fcd85ffebcaa75280d1171ca9a205e8c41d87428d71b07ad14ab266236b64268467ccc462679edc888f76326418d18d7bcfe8d1159391aa@51.15.61.194:30379",
//"enode://6853f434735e540f0fcd85ffebcaa75280d1171ca9a205e8c41d87428d71b07ad14ab266236b64268467ccc462679edc888f76326418d18d7bcfe8d1159391aa@51.15.61.194:30399",
"enode://1fa2dfe6b925ca753496ea197c973b66ef889ef4de2bf52acd5b8665c0cc2e8b95fbd192e764f10735f589297f1ae533f350f004e403063e8d4ad979aae44c12@51.15.61.194:30303",
"enode://a8f1dae49f665c566734e002f89c1feb9b01e3ed09bdea6199aa6093f25085c4777fd553d2d1d14457286c24aaa48eaf6db99315e0caf62d97ea8bce801ae7c1@51.15.35.2:30379",
//"enode://a8f1dae49f665c566734e002f89c1feb9b01e3ed09bdea6199aa6093f25085c4777fd553d2d1d14457286c24aaa48eaf6db99315e0caf62d97ea8bce801ae7c1@51.15.35.2:30399",
"enode://44b91c043bcd96bc5279524f1bfe32df0670374135435ebacb29ba5d0e18192623e63ead711c9c363afdf2500fac423116ac28bdd2d700bd70e096326f95c63f@51.15.35.2:30303",
"enode://64278f1e4224a5ff4608da54b7b045ae0b875a332c57e6f9b4cbb3e9ac1e56a1d5b91ff2def2662c767146b3f7f08924c15f66d41352a18ebe71832c35f6a0cf@51.15.54.229:30379",
//"enode://64278f1e4224a5ff4608da54b7b045ae0b875a332c57e6f9b4cbb3e9ac1e56a1d5b91ff2def2662c767146b3f7f08924c15f66d41352a18ebe71832c35f6a0cf@51.15.54.229:30399",
"enode://fb7622d3a50dc603f5c76919dd99c4112e5925cb891a67086b9dce581166fbdad361fd0bfb7ff128ab8f5e24e209e0b923668fbddb7e8b99edb82c1e3d782a80@51.15.54.229:30303",
"enode://e1fcf9e7a47ab43a546d1c1633c511d98d13646bbf5c82d74ff98a1c88e54567b0be6574e977164e1b4c997ef8e79b19f1e12e85a6230c746dd74206fe37cfa0@51.15.35.70:30379",
//"enode://e1fcf9e7a47ab43a546d1c1633c511d98d13646bbf5c82d74ff98a1c88e54567b0be6574e977164e1b4c997ef8e79b19f1e12e85a6230c746dd74206fe37cfa0@51.15.35.70:30399",
"enode://14c2960f57f6d63ed541cf64226aafbc7a21c40c6e4935a2e58fd2466fa7d06ec32082734c64d32f7c4692f4b90f26d019f472ba55cdda6d624ef4d7d8441285@51.15.35.70:30303",
"enode://a8512bcaae1245fda71d400291dd22937d89947b6fc31283945557abe1281c5a9325ffc11e363cfed6362e4d2d9b941c5b325270662ba43ac8c424168e6567a6@51.15.39.57:30379",
//"enode://a8512bcaae1245fda71d400291dd22937d89947b6fc31283945557abe1281c5a9325ffc11e363cfed6362e4d2d9b941c5b325270662ba43ac8c424168e6567a6@51.15.39.57:30399",
"enode://02cfa2b02b5431bfdc1bad0f575de8ea151029fe9a9c689074793d704d1b428255bd111bf578f0b4fcaa18267da7c335db9557e1012434f4a8ab2c25f4b3da4d@51.15.39.57:30303",
}
// DiscoveryV5Bootnodes are the enode URLs of the P2P bootstrap nodes for the
// experimental RLPx v5 topic-discovery network.
var DiscoveryV5Bootnodes = []string{}

View File

@ -14,6 +14,7 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/static"
)
// default node configuration options
@ -40,7 +41,7 @@ var (
// 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 flag specifies whether protocol is enabled
Enabled bool
// Genesis is JSON to seed the chain database with
@ -61,7 +62,7 @@ type FirebaseConfig struct {
// WhisperConfig holds SHH-related configuration
type WhisperConfig struct {
// Enabled flag specifies whether protocol is enabled
// Enabled flag specifies whether protocol is enabled
Enabled bool
// IdentityFile path to private key, that will be loaded as identity into Whisper
@ -104,14 +105,31 @@ type WhisperConfig struct {
// SwarmConfig holds Swarm-related configuration
type SwarmConfig struct {
// Enabled flag specifies whether protocol is enabled
// Enabled flag specifies whether protocol is enabled
Enabled bool
}
// BootCluster holds configuration for supporting boot cluster, which is a temporary
// means for mobile devices to get connected to Ethereum network (UDP-based discovery
// may not be available, so we need means to discover the network manually).
type BootClusterConfig struct {
// Enabled flag specifies whether feature is enabled
Enabled bool
// ConfigFile is a path to JSON file containing array of boot nodes
// See `static/bootcluster/*.json` for cluster configurations provided
// out of box. You can pass absolute path, and if file at that path can be
// loaded, it will be used. Otherwise, file is supposed to be relative to
// `static/bootcluster` folder.
ConfigFile string
}
// NodeConfig stores configuration options for a node
type NodeConfig struct {
// TestNet flag whether given configuration describes a test or mainnet
TestNet bool
// DevMode is true when given configuration is to be used during development.
// For production, this flag should be turned off, so that more strict requirements
// are applied to node's configuration
DevMode bool
// NetworkID sets network to use for selecting peers to connect to
NetworkID uint64 `json:"NetworkId,"`
@ -183,6 +201,9 @@ type NodeConfig struct {
// LogToStderr defines whether logged info should also be output to os.Stderr
LogToStderr bool
// BootClusterConfig extra configuration for supporting cluster
BootClusterConfig *BootClusterConfig `json:"BootClusterConfig,"`
// LightEthConfig extra configuration for LES
LightEthConfig *LightEthConfig `json:"LightEthConfig,"`
@ -194,15 +215,15 @@ type NodeConfig struct {
}
// NewNodeConfig creates new node configuration object
func NewNodeConfig(dataDir string, networkID uint64) (*NodeConfig, error) {
func NewNodeConfig(dataDir string, networkID uint64, devMode bool) (*NodeConfig, error) {
nodeConfig := &NodeConfig{
DevMode: devMode,
NetworkID: networkID,
DataDir: dataDir,
Name: ClientIdentifier,
Version: Version,
HTTPHost: HTTPHost,
HTTPPort: HTTPPort,
APIModules: APIModules,
WSHost: WSHost,
WSPort: WSPort,
MaxPeers: MaxPeers,
@ -215,6 +236,10 @@ func NewNodeConfig(dataDir string, networkID uint64) (*NodeConfig, error) {
Enabled: true,
DatabaseCache: DatabaseCache,
},
BootClusterConfig: &BootClusterConfig{
Enabled: true,
ConfigFile: BootClusterConfigFile,
},
WhisperConfig: &WhisperConfig{
Enabled: true,
Port: WhisperPort,
@ -227,64 +252,17 @@ func NewNodeConfig(dataDir string, networkID uint64) (*NodeConfig, error) {
SwarmConfig: &SwarmConfig{},
}
// auto-populate some dependent values
if err := nodeConfig.populateGenesis(); err != nil {
return nil, err
}
if err := nodeConfig.populateDirs(); err != nil {
// adjust dependent values
if err := nodeConfig.updateConfig(); err != nil {
return nil, err
}
return nodeConfig, nil
}
// populateDirs updates directories that should be wrt to DataDir
func (c *NodeConfig) populateDirs() error {
makeSubDirPath := func(baseDir, subDir string) string {
if len(baseDir) == 0 {
return ""
}
return filepath.Join(baseDir, subDir)
}
if len(c.KeyStoreDir) == 0 {
c.KeyStoreDir = makeSubDirPath(c.DataDir, KeyStoreDir)
}
if len(c.WhisperConfig.DataDir) == 0 {
c.WhisperConfig.DataDir = makeSubDirPath(c.DataDir, WhisperDataDir)
}
return nil
}
// populateChainConfig does necessary adjustments to config object (depending on network node will be runnin on)
func (c *NodeConfig) populateGenesis() error {
c.TestNet = false
if c.NetworkID == TestNetworkID {
c.TestNet = true
}
var genesis *core.Genesis
if c.TestNet {
genesis = core.DefaultTestnetGenesisBlock()
} else {
genesis = core.DefaultGenesisBlock()
}
// encode the genesis into JSON
enc, err := json.Marshal(genesis)
if err != nil {
return err
}
c.LightEthConfig.Genesis = string(enc)
return nil
}
// LoadNodeConfig parses incoming JSON and returned it as Config
func LoadNodeConfig(configJSON string) (*NodeConfig, error) {
nodeConfig, err := NewNodeConfig("", 0)
nodeConfig, err := NewNodeConfig("", 0, true)
if err != nil {
return nil, err
}
@ -298,10 +276,7 @@ func LoadNodeConfig(configJSON string) (*NodeConfig, error) {
}
// repopulate
if err := nodeConfig.populateGenesis(); err != nil {
return nil, err
}
if err := nodeConfig.populateDirs(); err != nil {
if err := nodeConfig.updateConfig(); err != nil {
return nil, err
}
@ -336,6 +311,125 @@ func (c *NodeConfig) Save() error {
return nil
}
// LoadBootClusterNodes loads boot nodes from a config file provided in BootClusterConfig
func (c *NodeConfig) LoadBootClusterNodes() ([]string, error) {
var bootnodes []string
var configData []byte
var err error
filename := c.BootClusterConfig.ConfigFile
log.Info("Loading boot nodes config file", "source", filename)
if _, err = os.Stat(filename); os.IsNotExist(err) { // load from static resources
configData, err = static.Asset("bootcluster/" + filename)
} else {
configData, err = ioutil.ReadFile(filename)
}
if err != nil {
return nil, err
}
// parse JSON
if err := json.Unmarshal([]byte(configData), &bootnodes); err != nil {
return nil, err
}
return bootnodes, nil
}
// updateConfig traverses configuration and adjusts dependent fields
// (we have a development/production and mobile/full node dependent configurations)
func (c *NodeConfig) updateConfig() error {
if err := c.updateGenesisConfig(); err != nil {
return err
}
if err := c.updateRPCConfig(); err != nil {
return err
}
if err := c.updateBootClusterConfig(); err != nil {
return err
}
if err := c.updateRelativeDirsConfig(); err != nil {
return err
}
return nil
}
// updateGenesisConfig does necessary adjustments to config object (depending on network node will be running on)
func (c *NodeConfig) updateGenesisConfig() error {
var genesis *core.Genesis
switch c.NetworkID {
case MainNetworkID:
genesis = core.DefaultGenesisBlock()
case RopstenNetworkID:
genesis = core.DefaultTestnetGenesisBlock()
case RinkebyNetworkID:
genesis = core.DefaultRinkebyGenesisBlock()
default:
return nil
}
// encode the genesis into JSON
enc, err := json.Marshal(genesis)
if err != nil {
return err
}
c.LightEthConfig.Genesis = string(enc)
return nil
}
// updateBootClusterConfig populates cluster config file, depending on dev/prod and mobile/full settings
func (c *NodeConfig) updateBootClusterConfig() error {
var configFile string
switch c.NetworkID {
case MainNetworkID:
configFile = "homestead.prod.json"
case RopstenNetworkID:
configFile = "ropsten.prod.json"
case RinkebyNetworkID:
configFile = "rinkeby.prod.json"
}
if c.DevMode {
configFile = strings.Replace(configFile, "prod", "dev", 1)
}
if len(configFile) > 0 {
c.BootClusterConfig.ConfigFile = configFile
}
return nil
}
// updateRPCConfig transforms RPC settings to meet requirements of a given configuration
func (c *NodeConfig) updateRPCConfig() error {
c.APIModules = ProdAPIModules
if c.DevMode {
c.APIModules = DevAPIModules
}
return nil
}
// updateRelativeDirsConfig updates directories that should be wrt to DataDir
func (c *NodeConfig) updateRelativeDirsConfig() error {
makeSubDirPath := func(baseDir, subDir string) string {
if len(baseDir) == 0 {
return ""
}
return filepath.Join(baseDir, subDir)
}
if len(c.KeyStoreDir) == 0 {
c.KeyStoreDir = makeSubDirPath(c.DataDir, KeyStoreDir)
}
if len(c.WhisperConfig.DataDir) == 0 {
c.WhisperConfig.DataDir = makeSubDirPath(c.DataDir, WhisperDataDir)
}
return nil
}
// String dumps config object as nicely indented JSON
func (c *NodeConfig) String() string {
data, _ := json.MarshalIndent(c, "", " ")
@ -354,6 +448,12 @@ func (c *SwarmConfig) String() string {
return string(data)
}
// String dumps config object as nicely indented JSON
func (c *BootClusterConfig) String() string {
data, _ := json.MarshalIndent(c, "", " ")
return string(data)
}
// ReadPasswordFile reads and returns content of the password file
func (c *WhisperConfig) ReadPasswordFile() ([]byte, error) {
if len(c.PasswordFile) <= 0 {

View File

@ -5,6 +5,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"reflect"
"strings"
"testing"
@ -285,8 +286,6 @@ var loadConfigTestCases = []struct {
"WSEnabled": false
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
//nodeConfig.LightEthConfig.Genesis = nodeConfig.LightEthConfig.Genesis[:125]
//fmt.Println(nodeConfig)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
@ -297,6 +296,509 @@ var loadConfigTestCases = []struct {
}
},
},
{
`default boot cluster (Ropsten Dev)`,
`{
"NetworkId": 3,
"DataDir": "$TMPDIR"
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodeConfig.BootClusterConfig.ConfigFile != params.BootClusterConfigFile {
t.Fatalf("unexpected BootClusterConfigFile, expected: %v, got: %v",
params.BootClusterConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
if nodeConfig.BootClusterConfig.Enabled != true {
t.Fatal("boot cluster is expected to be enabled by default")
}
enodes, err := nodeConfig.LoadBootClusterNodes()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedEnodes := []string{
"enode://bc4a130219ae94c2a66e3ec3377c2a460e006d56b877d5e8edb0d0f8064cf400f117a53a5389f552c9e1a122b9a07eeaa41e7ed885268ee825b6a788188fb52e@51.15.55.219:30379",
"enode://fbddff478e18292dc32b90f139bf773a08da89ffe29208e4de0091f6c589e60fccfaf16d4f4a76be49f57782c061ec8ea97078601c6f367feabda740f5ce8246@51.15.55.219:30303",
"enode://df20352d07924ffe93e67ee7e81105d7b250f7932ff4e0351da2cfa17282e53f765895c32cda36b70dfb98def0b472b29d79321737035641bf3bcf595dcc1041@51.15.35.110:30379",
"enode://4e5ee0487a4d8349ab9a9925b00eed0f976d98972c5a22f43fd50d1424897757032c36f273b434a4d3e013a2544eca74a9d1a0419f9f07f7bb43182a73df3690@51.15.35.110:30303",
"enode://2cbcc7b3d067581072066143e0fade0d007e80ecc8f86eb475200c3da3a6d81dd4e1e7051fc3dfaee337f110ceec61594a901b09e36eb367629ddff6e1dfd955@51.15.34.3:30379",
"enode://18efd9afb60443e00fed602cc0df526cd1d8543d2f6037df9380eb973d30b5fd04ac9f221053f82034581051bfd6e54356a99af2255f1a674d71d17440a6c95b@51.15.34.3:30303",
"enode://cdb95f3d866472a74195342979ffea4ed7f9b68cd1e8c6f9a25b3197c221f01bc076ccba760341d8b69bb6bfbc9bf4fdeabd0caa99ee0bf4e79917fa1f42423c@51.15.56.154:30379",
"enode://5b99c0cb372299fd3f2d94612a682990722eb7c3a252dacefc8270eb7f172fc699c1ddfad826fbfc979270538e8d89bd6919703eb9ef526eac0a45e9fb455123@51.15.56.154:30303",
"enode://5ce8e96d9589671767a7b1c6b9a34bcf532587387eb062de712a9f716a66f05f412126121ce4d97330bc5dc7a4938ff1ecc22306b0b8b97a7f748c6f5f59c620@51.15.60.23:30379",
"enode://0e1d4d0fcfe888bf8a478b0fd89760a47733a5c04cd47de353295a6eb8dde8f54821b31196527d0c5c73a7024dc9ff34127692d237840fc09c312b3a19cd28fe@51.15.60.23:30303",
}
if len(enodes) != len(expectedEnodes) {
t.Fatalf("wrong number of enodes, expected: %d, got: %d", len(expectedEnodes), len(enodes))
}
if !reflect.DeepEqual(enodes, expectedEnodes) {
t.Fatalf("wrong list of enodes, expected: \n%v,\n\ngot:\n%v", expectedEnodes, enodes)
}
},
},
{
`disabled boot cluster`,
`{
"NetworkId": 311,
"DataDir": "$TMPDIR",
"BootClusterConfig": {
"Enabled": false
}
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodeConfig.BootClusterConfig.Enabled != false {
t.Fatal("boot cluster is expected to be disabled")
}
},
},
{
`select boot cluster (Ropsten Prod)`,
`{
"NetworkId": 311,
"DataDir": "$TMPDIR",
"BootClusterConfig": {
"ConfigFile": "ropsten.prod.json"
}
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedConfigFile := "ropsten.prod.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedConfigFile {
t.Fatalf("unexpected BootClusterConfigFile, expected: %v, got: %v",
expectedConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
enodes, err := nodeConfig.LoadBootClusterNodes()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedEnodes := []string{
"enode://6853f434735e540f0fcd85ffebcaa75280d1171ca9a205e8c41d87428d71b07ad14ab266236b64268467ccc462679edc888f76326418d18d7bcfe8d1159391aa@51.15.61.194:30379",
"enode://1fa2dfe6b925ca753496ea197c973b66ef889ef4de2bf52acd5b8665c0cc2e8b95fbd192e764f10735f589297f1ae533f350f004e403063e8d4ad979aae44c12@51.15.61.194:30303",
"enode://a8f1dae49f665c566734e002f89c1feb9b01e3ed09bdea6199aa6093f25085c4777fd553d2d1d14457286c24aaa48eaf6db99315e0caf62d97ea8bce801ae7c1@51.15.35.2:30379",
"enode://44b91c043bcd96bc5279524f1bfe32df0670374135435ebacb29ba5d0e18192623e63ead711c9c363afdf2500fac423116ac28bdd2d700bd70e096326f95c63f@51.15.35.2:30303",
"enode://64278f1e4224a5ff4608da54b7b045ae0b875a332c57e6f9b4cbb3e9ac1e56a1d5b91ff2def2662c767146b3f7f08924c15f66d41352a18ebe71832c35f6a0cf@51.15.54.229:30379",
"enode://fb7622d3a50dc603f5c76919dd99c4112e5925cb891a67086b9dce581166fbdad361fd0bfb7ff128ab8f5e24e209e0b923668fbddb7e8b99edb82c1e3d782a80@51.15.54.229:30303",
"enode://e1fcf9e7a47ab43a546d1c1633c511d98d13646bbf5c82d74ff98a1c88e54567b0be6574e977164e1b4c997ef8e79b19f1e12e85a6230c746dd74206fe37cfa0@51.15.35.70:30379",
"enode://14c2960f57f6d63ed541cf64226aafbc7a21c40c6e4935a2e58fd2466fa7d06ec32082734c64d32f7c4692f4b90f26d019f472ba55cdda6d624ef4d7d8441285@51.15.35.70:30303",
"enode://a8512bcaae1245fda71d400291dd22937d89947b6fc31283945557abe1281c5a9325ffc11e363cfed6362e4d2d9b941c5b325270662ba43ac8c424168e6567a6@51.15.39.57:30379",
"enode://02cfa2b02b5431bfdc1bad0f575de8ea151029fe9a9c689074793d704d1b428255bd111bf578f0b4fcaa18267da7c335db9557e1012434f4a8ab2c25f4b3da4d@51.15.39.57:30303",
}
if len(enodes) != len(expectedEnodes) {
t.Fatalf("wrong number of enodes, expected: %d, got: %d", len(expectedEnodes), len(enodes))
}
if !reflect.DeepEqual(enodes, expectedEnodes) {
t.Fatalf("wrong list of enodes, expected: \n%v,\n\ngot:\n%v", expectedEnodes, enodes)
}
},
},
{
`select boot cluster (Rinkeby Dev)`,
`{
"NetworkId": 311,
"DataDir": "$TMPDIR",
"BootClusterConfig": {
"ConfigFile": "rinkeby.dev.json"
}
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedConfigFile := "rinkeby.dev.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedConfigFile {
t.Fatalf("unexpected BootClusterConfigFile, expected: %v, got: %v",
expectedConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
enodes, err := nodeConfig.LoadBootClusterNodes()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedEnodes := []string{
"enode://7512c8f6e7ffdcc723cf77e602a1de9d8cc2e8ad35db309464819122cd773857131aee390fec33894db13da730c8432bb248eed64039e3810e156e979b2847cb@51.15.78.243:30303",
"enode://1cc27a5a41130a5c8b90db5b2273dc28f7b56f3edfc0dcc57b665d451274b26541e8de49ea7a074281906a82209b9600239c981163b6ff85c3038a8e2bc5d8b8@51.15.68.93:30303",
"enode://798d17064141b8f88df718028a8272b943d1cb8e696b3dab56519c70b77b1d3469b56b6f4ce3788457646808f5c7299e9116626f2281f30b959527b969a71e4f@51.15.75.244:30303",
}
if len(enodes) != len(expectedEnodes) {
t.Fatalf("wrong number of enodes, expected: %d, got: %d", len(expectedEnodes), len(enodes))
}
if !reflect.DeepEqual(enodes, expectedEnodes) {
t.Fatalf("wrong list of enodes, expected: \n%v,\n\ngot:\n%v", expectedEnodes, enodes)
}
},
},
{
`select boot cluster (Rinkeby Prod)`,
`{
"NetworkId": 311,
"DataDir": "$TMPDIR",
"BootClusterConfig": {
"ConfigFile": "rinkeby.prod.json"
}
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedConfigFile := "rinkeby.prod.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedConfigFile {
t.Fatalf("unexpected BootClusterConfigFile, expected: %v, got: %v",
expectedConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
enodes, err := nodeConfig.LoadBootClusterNodes()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedEnodes := []string{
"enode://fda3f6273a0f2da4ac5858d1f52e5afaf9def281121be3d37558c67d4d9ca26c6ad7a0520b2cd7454120fb770e86d5760487c9924b2166e65485f606e56d60fc@51.15.69.144:30303",
"enode://ba41aa829287a0a9076d9bffed97c8ce2e491b99873288c9e886f16fd575306ac6c656db4fbf814f5a9021aec004ffa9c0ae8650f92fd10c12eeb7c364593eb3@51.15.69.147:30303",
"enode://28ecf5272b560ca951f4cd7f1eb8bd62da5853b026b46db432c4b01797f5b0114819a090a72acd7f32685365ecd8e00450074fa0673039aefe10f3fb666e0f3f@51.15.76.249:30303",
}
if len(enodes) != len(expectedEnodes) {
t.Fatalf("wrong number of enodes, expected: %d, got: %d", len(expectedEnodes), len(enodes))
}
if !reflect.DeepEqual(enodes, expectedEnodes) {
t.Fatalf("wrong list of enodes, expected: \n%v,\n\ngot:\n%v", expectedEnodes, enodes)
}
},
},
{
`select boot cluster (Homestead Dev)`,
`{
"NetworkId": 311,
"DataDir": "$TMPDIR",
"BootClusterConfig": {
"ConfigFile": "homestead.dev.json"
}
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedConfigFile := "homestead.dev.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedConfigFile {
t.Fatalf("unexpected BootClusterConfigFile, expected: %v, got: %v",
expectedConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
enodes, err := nodeConfig.LoadBootClusterNodes()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedEnodes := []string{
"enode://93833be81c3d1bdb2ae5cde258c8f82ad1011a1bea8eb49fe50b0af394d4f7f7e45974356870552f36744efd732692a64865d1e8b64114eaf89a1bad0a1903a2@51.15.64.29:30303",
"enode://d76854bc54144b2269c5316d5f00f0a194efee2fb8d31e7b1939effd7e17f25773f8dc7fda8c4eb469450799da7f39b4e364e2a278d91b53539dcbb10b139635@51.15.73.37:30303",
"enode://57874205931df976079e4ff8ebb5756461030fb00f73486bd5ec4ae6ed6ba98e27d09f58e59bd85281d24084a6062bc8ab514dbcdaa9678fc3001d47772e626e@51.15.75.213:30303",
}
if len(enodes) != len(expectedEnodes) {
t.Fatalf("wrong number of enodes, expected: %d, got: %d", len(expectedEnodes), len(enodes))
}
if !reflect.DeepEqual(enodes, expectedEnodes) {
t.Fatalf("wrong list of enodes, expected: \n%v,\n\ngot:\n%v", expectedEnodes, enodes)
}
},
},
{
`select boot cluster (Homestead Prod)`,
`{
"NetworkId": 311,
"DataDir": "$TMPDIR",
"BootClusterConfig": {
"ConfigFile": "homestead.prod.json"
}
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedConfigFile := "homestead.prod.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedConfigFile {
t.Fatalf("unexpected BootClusterConfigFile, expected: %v, got: %v",
expectedConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
enodes, err := nodeConfig.LoadBootClusterNodes()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedEnodes := []string{
"enode://f3b0e5dca730962bae814f3402b8f8a296644c33e8d7a95bd1ab313143a752c77076a03bcb76263570f2f34d4eb530f1daf5054c0990921a872a34eb505dcedf@51.15.73.129:30303",
"enode://fce0d1c2292829b0eccce444f8943f88087ce00a5e910b157972ee1658a948d23c7a046f26567f73b2b18d126811509d7ef1de5be9b1decfcbb14738a590c477@51.15.75.187:30303",
"enode://3b4b9fa02ae8d54c2db51a674bc93d85649b4775f22400f74ae25e9f1c665baa3bcdd33cadd2c1a93cd08a6af984cb605fbb61ec0d750a11d48d4080298af008@51.15.77.193:30303",
}
if len(enodes) != len(expectedEnodes) {
t.Fatalf("wrong number of enodes, expected: %d, got: %d", len(expectedEnodes), len(enodes))
}
if !reflect.DeepEqual(enodes, expectedEnodes) {
t.Fatalf("wrong list of enodes, expected: \n%v,\n\ngot:\n%v", expectedEnodes, enodes)
}
},
},
{
`select boot cluster (custom JSON, via absolute path)`,
`{
"NetworkId": 311,
"DataDir": "$TMPDIR",
"BootClusterConfig": {
"ConfigFile": "$TMPDIR/bootstrap-cluster.json"
}
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedConfigFile := filepath.Join(dataDir, "bootstrap-cluster.json")
if nodeConfig.BootClusterConfig.ConfigFile != expectedConfigFile {
t.Fatalf("unexpected BootClusterConfigFile, expected: %v, got: %v",
expectedConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
enodes, err := nodeConfig.LoadBootClusterNodes()
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expectedEnodes := []string{
"enode://foobar@41.41.41.41:30300",
"enode://foobaz@42.42.42.42:30302",
}
if len(enodes) != len(expectedEnodes) {
t.Fatalf("wrong number of enodes, expected: %d, got: %d", len(expectedEnodes), len(enodes))
}
if !reflect.DeepEqual(enodes, expectedEnodes) {
t.Fatalf("wrong list of enodes, expected: \n%v,\n\ngot:\n%v", expectedEnodes, enodes)
}
},
},
{
`default DevMode (true)`,
`{
"NetworkId": 311,
"DataDir": "$TMPDIR"
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodeConfig.DevMode != true {
t.Fatalf("unexpected dev mode: expected: %v, got: %v", true, nodeConfig.DevMode)
}
if nodeConfig.BootClusterConfig.Enabled != true {
t.Fatal("expected boot cluster to be enabled")
}
if nodeConfig.BootClusterConfig.ConfigFile != params.BootClusterConfigFile {
t.Fatalf("unexpected bootcluster config file, expected: %v, got: %v",
params.BootClusterConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
},
},
{
`explicit DevMode = false`,
`{
"NetworkId": 3,
"DataDir": "$TMPDIR",
"DevMode": false
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodeConfig.DevMode != false {
t.Fatalf("unexpected dev mode: expected: %v, got: %v", false, nodeConfig.DevMode)
}
if nodeConfig.BootClusterConfig.Enabled != true {
t.Fatal("expected boot cluster to be enabled")
}
expectedBootClusterConfigFile := "ropsten.prod.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedBootClusterConfigFile {
t.Fatalf("unexpected bootcluster config file, expected: %v, got: %v",
expectedBootClusterConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
},
},
{
`populate bootstrap config (Homestead/Dev)`,
`{
"NetworkId": 1,
"DataDir": "$TMPDIR",
"DevMode": true
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodeConfig.DevMode != true {
t.Fatalf("unexpected dev mode: expected: %v, got: %v", true, nodeConfig.DevMode)
}
if nodeConfig.BootClusterConfig.Enabled != true {
t.Fatal("expected boot cluster to be enabled")
}
expectedBootClusterConfigFile := "homestead.dev.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedBootClusterConfigFile {
t.Fatalf("unexpected bootcluster config file, expected: %v, got: %v",
expectedBootClusterConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
},
},
{
`populate bootstrap config (Homestead/Prod)`,
`{
"NetworkId": 1,
"DataDir": "$TMPDIR",
"DevMode": false
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodeConfig.DevMode != false {
t.Fatalf("unexpected dev mode: expected: %v, got: %v", false, nodeConfig.DevMode)
}
if nodeConfig.BootClusterConfig.Enabled != true {
t.Fatal("expected boot cluster to be enabled")
}
expectedBootClusterConfigFile := "homestead.prod.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedBootClusterConfigFile {
t.Fatalf("unexpected bootcluster config file, expected: %v, got: %v",
expectedBootClusterConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
},
},
{
`populate bootstrap config (Ropsten/Dev)`,
`{
"NetworkId": 3,
"DataDir": "$TMPDIR"
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodeConfig.DevMode != true {
t.Fatalf("unexpected dev mode: expected: %v, got: %v", true, nodeConfig.DevMode)
}
if nodeConfig.BootClusterConfig.Enabled != true {
t.Fatal("expected boot cluster to be enabled")
}
expectedBootClusterConfigFile := "ropsten.dev.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedBootClusterConfigFile {
t.Fatalf("unexpected bootcluster config file, expected: %v, got: %v",
expectedBootClusterConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
},
},
{
`populate bootstrap config (Ropsten/Prod)`,
`{
"NetworkId": 3,
"DataDir": "$TMPDIR",
"DevMode": false
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodeConfig.DevMode != false {
t.Fatalf("unexpected dev mode: expected: %v, got: %v", false, nodeConfig.DevMode)
}
if nodeConfig.BootClusterConfig.Enabled != true {
t.Fatal("expected boot cluster to be enabled")
}
expectedBootClusterConfigFile := "ropsten.prod.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedBootClusterConfigFile {
t.Fatalf("unexpected bootcluster config file, expected: %v, got: %v",
expectedBootClusterConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
},
},
{
`populate bootstrap config (Rinkeby/Dev)`,
`{
"NetworkId": 4,
"DataDir": "$TMPDIR"
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodeConfig.DevMode != true {
t.Fatalf("unexpected dev mode: expected: %v, got: %v", true, nodeConfig.DevMode)
}
if nodeConfig.BootClusterConfig.Enabled != true {
t.Fatal("expected boot cluster to be enabled")
}
expectedBootClusterConfigFile := "rinkeby.dev.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedBootClusterConfigFile {
t.Fatalf("unexpected bootcluster config file, expected: %v, got: %v",
expectedBootClusterConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
},
},
{
`populate bootstrap config (Rinkeby/Prod)`,
`{
"NetworkId": 4,
"DataDir": "$TMPDIR",
"DevMode": false
}`,
func(t *testing.T, dataDir string, nodeConfig *params.NodeConfig, err error) {
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if nodeConfig.DevMode != false {
t.Fatalf("unexpected dev mode: expected: %v, got: %v", false, nodeConfig.DevMode)
}
if nodeConfig.BootClusterConfig.Enabled != true {
t.Fatal("expected boot cluster to be enabled")
}
expectedBootClusterConfigFile := "rinkeby.prod.json"
if nodeConfig.BootClusterConfig.ConfigFile != expectedBootClusterConfigFile {
t.Fatalf("unexpected bootcluster config file, expected: %v, got: %v",
expectedBootClusterConfigFile, nodeConfig.BootClusterConfig.ConfigFile)
}
},
},
}
func TestLoadNodeConfig(t *testing.T) {
@ -306,6 +808,13 @@ func TestLoadNodeConfig(t *testing.T) {
}
defer os.RemoveAll(tmpDir) // nolint: errcheck
// create sample Bootstrap Cluster Config
bootstrapConfig := []byte(`["enode://foobar@41.41.41.41:30300", "enode://foobaz@42.42.42.42:30302"]`)
if err = ioutil.WriteFile(filepath.Join(tmpDir, "bootstrap-cluster.json"), bootstrapConfig, os.ModePerm); err != nil {
t.Fatal(err)
}
t.Log(tmpDir)
for _, testCase := range loadConfigTestCases {
t.Log("test: " + testCase.name)
testCase.configJSON = strings.Replace(testCase.configJSON, "$TMPDIR", tmpDir, -1)
@ -322,7 +831,7 @@ func TestConfigWriteRead(t *testing.T) {
}
defer os.RemoveAll(tmpDir) // nolint: errcheck
nodeConfig, err := params.NewNodeConfig(tmpDir, networkId)
nodeConfig, err := params.NewNodeConfig(tmpDir, networkId, true)
if err != nil {
t.Fatalf("cannot create new config object: %v", err)
}
@ -345,6 +854,7 @@ func TestConfigWriteRead(t *testing.T) {
}
}
configReadWrite(params.TestNetworkID, "testdata/config.testnet.json")
configReadWrite(params.RinkebyNetworkID, "testdata/config.rinkeby.json")
configReadWrite(params.RopstenNetworkID, "testdata/config.ropsten.json")
configReadWrite(params.MainNetworkID, "testdata/config.mainnet.json")
}

View File

@ -19,9 +19,11 @@ const (
// HTTPPort is HTTP-RPC port (replaced in unit tests)
HTTPPort = 8545
// APIModules is a list of modules to expose vie HTTP RPC
// TODO remove "admin" on main net
APIModules = "db,eth,net,web3,shh,personal,admin"
// DevAPIModules is a list of modules to expose via any type of RPC (HTTP, IPC) during development
DevAPIModules = "db,eth,net,web3,shh,personal,admin"
// ProdAPIModules is a list of modules to expose via any type of RPC (HTTP, IPC) in production
ProdAPIModules = "eth,net,web3,shh,personal"
// WSHost is a host interface for the websocket RPC server
WSHost = "localhost"
@ -72,6 +74,12 @@ const (
// MainNetworkID is id of the main network
MainNetworkID = 1
// TestNetworkID is id of a test network
TestNetworkID = 3
// RopstenNetworkID is id of a test network (on PoW)
RopstenNetworkID = 3
// RinkebyNetworkID is id of a test network (on PoA)
RinkebyNetworkID = 4
// BootClusterConfigFile is default config file containing boot node list (as JSON array)
BootClusterConfigFile = "ropsten.dev.json"
)

View File

@ -18,7 +18,7 @@ func TestLogger(t *testing.T) {
}
//defer os.RemoveAll(tmpDir)
nodeConfig, err := params.NewNodeConfig(tmpDir, params.TestNetworkID)
nodeConfig, err := params.NewNodeConfig(tmpDir, params.RopstenNetworkID, true)
if err != nil {
t.Fatal("cannot create config object")
}

File diff suppressed because one or more lines are too long

54
geth/params/testdata/config.rinkeby.json vendored Executable file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -165,7 +165,7 @@ func PrepareTestNode() (err error) {
}
// start geth node and wait for it to initialize
config, err := params.NewNodeConfig(filepath.Join(TestDataDir, "data"), params.TestNetworkID)
config, err := params.NewNodeConfig(filepath.Join(TestDataDir, "data"), params.RopstenNetworkID, true)
if err != nil {
return err
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,5 @@
[
"enode://93833be81c3d1bdb2ae5cde258c8f82ad1011a1bea8eb49fe50b0af394d4f7f7e45974356870552f36744efd732692a64865d1e8b64114eaf89a1bad0a1903a2@51.15.64.29:30303",
"enode://d76854bc54144b2269c5316d5f00f0a194efee2fb8d31e7b1939effd7e17f25773f8dc7fda8c4eb469450799da7f39b4e364e2a278d91b53539dcbb10b139635@51.15.73.37:30303",
"enode://57874205931df976079e4ff8ebb5756461030fb00f73486bd5ec4ae6ed6ba98e27d09f58e59bd85281d24084a6062bc8ab514dbcdaa9678fc3001d47772e626e@51.15.75.213:30303"
]

View File

@ -0,0 +1,5 @@
[
"enode://f3b0e5dca730962bae814f3402b8f8a296644c33e8d7a95bd1ab313143a752c77076a03bcb76263570f2f34d4eb530f1daf5054c0990921a872a34eb505dcedf@51.15.73.129:30303",
"enode://fce0d1c2292829b0eccce444f8943f88087ce00a5e910b157972ee1658a948d23c7a046f26567f73b2b18d126811509d7ef1de5be9b1decfcbb14738a590c477@51.15.75.187:30303",
"enode://3b4b9fa02ae8d54c2db51a674bc93d85649b4775f22400f74ae25e9f1c665baa3bcdd33cadd2c1a93cd08a6af984cb605fbb61ec0d750a11d48d4080298af008@51.15.77.193:30303"
]

View File

@ -0,0 +1,5 @@
[
"enode://7512c8f6e7ffdcc723cf77e602a1de9d8cc2e8ad35db309464819122cd773857131aee390fec33894db13da730c8432bb248eed64039e3810e156e979b2847cb@51.15.78.243:30303",
"enode://1cc27a5a41130a5c8b90db5b2273dc28f7b56f3edfc0dcc57b665d451274b26541e8de49ea7a074281906a82209b9600239c981163b6ff85c3038a8e2bc5d8b8@51.15.68.93:30303",
"enode://798d17064141b8f88df718028a8272b943d1cb8e696b3dab56519c70b77b1d3469b56b6f4ce3788457646808f5c7299e9116626f2281f30b959527b969a71e4f@51.15.75.244:30303"
]

View File

@ -0,0 +1,5 @@
[
"enode://fda3f6273a0f2da4ac5858d1f52e5afaf9def281121be3d37558c67d4d9ca26c6ad7a0520b2cd7454120fb770e86d5760487c9924b2166e65485f606e56d60fc@51.15.69.144:30303",
"enode://ba41aa829287a0a9076d9bffed97c8ce2e491b99873288c9e886f16fd575306ac6c656db4fbf814f5a9021aec004ffa9c0ae8650f92fd10c12eeb7c364593eb3@51.15.69.147:30303",
"enode://28ecf5272b560ca951f4cd7f1eb8bd62da5853b026b46db432c4b01797f5b0114819a090a72acd7f32685365ecd8e00450074fa0673039aefe10f3fb666e0f3f@51.15.76.249:30303"
]

View File

@ -0,0 +1,16 @@
[
"enode://bc4a130219ae94c2a66e3ec3377c2a460e006d56b877d5e8edb0d0f8064cf400f117a53a5389f552c9e1a122b9a07eeaa41e7ed885268ee825b6a788188fb52e@51.15.55.219:30379",
"enode://fbddff478e18292dc32b90f139bf773a08da89ffe29208e4de0091f6c589e60fccfaf16d4f4a76be49f57782c061ec8ea97078601c6f367feabda740f5ce8246@51.15.55.219:30303",
"enode://df20352d07924ffe93e67ee7e81105d7b250f7932ff4e0351da2cfa17282e53f765895c32cda36b70dfb98def0b472b29d79321737035641bf3bcf595dcc1041@51.15.35.110:30379",
"enode://4e5ee0487a4d8349ab9a9925b00eed0f976d98972c5a22f43fd50d1424897757032c36f273b434a4d3e013a2544eca74a9d1a0419f9f07f7bb43182a73df3690@51.15.35.110:30303",
"enode://2cbcc7b3d067581072066143e0fade0d007e80ecc8f86eb475200c3da3a6d81dd4e1e7051fc3dfaee337f110ceec61594a901b09e36eb367629ddff6e1dfd955@51.15.34.3:30379",
"enode://18efd9afb60443e00fed602cc0df526cd1d8543d2f6037df9380eb973d30b5fd04ac9f221053f82034581051bfd6e54356a99af2255f1a674d71d17440a6c95b@51.15.34.3:30303",
"enode://cdb95f3d866472a74195342979ffea4ed7f9b68cd1e8c6f9a25b3197c221f01bc076ccba760341d8b69bb6bfbc9bf4fdeabd0caa99ee0bf4e79917fa1f42423c@51.15.56.154:30379",
"enode://5b99c0cb372299fd3f2d94612a682990722eb7c3a252dacefc8270eb7f172fc699c1ddfad826fbfc979270538e8d89bd6919703eb9ef526eac0a45e9fb455123@51.15.56.154:30303",
"enode://5ce8e96d9589671767a7b1c6b9a34bcf532587387eb062de712a9f716a66f05f412126121ce4d97330bc5dc7a4938ff1ecc22306b0b8b97a7f748c6f5f59c620@51.15.60.23:30379",
"enode://0e1d4d0fcfe888bf8a478b0fd89760a47733a5c04cd47de353295a6eb8dde8f54821b31196527d0c5c73a7024dc9ff34127692d237840fc09c312b3a19cd28fe@51.15.60.23:30303"
]

View File

@ -0,0 +1,16 @@
[
"enode://6853f434735e540f0fcd85ffebcaa75280d1171ca9a205e8c41d87428d71b07ad14ab266236b64268467ccc462679edc888f76326418d18d7bcfe8d1159391aa@51.15.61.194:30379",
"enode://1fa2dfe6b925ca753496ea197c973b66ef889ef4de2bf52acd5b8665c0cc2e8b95fbd192e764f10735f589297f1ae533f350f004e403063e8d4ad979aae44c12@51.15.61.194:30303",
"enode://a8f1dae49f665c566734e002f89c1feb9b01e3ed09bdea6199aa6093f25085c4777fd553d2d1d14457286c24aaa48eaf6db99315e0caf62d97ea8bce801ae7c1@51.15.35.2:30379",
"enode://44b91c043bcd96bc5279524f1bfe32df0670374135435ebacb29ba5d0e18192623e63ead711c9c363afdf2500fac423116ac28bdd2d700bd70e096326f95c63f@51.15.35.2:30303",
"enode://64278f1e4224a5ff4608da54b7b045ae0b875a332c57e6f9b4cbb3e9ac1e56a1d5b91ff2def2662c767146b3f7f08924c15f66d41352a18ebe71832c35f6a0cf@51.15.54.229:30379",
"enode://fb7622d3a50dc603f5c76919dd99c4112e5925cb891a67086b9dce581166fbdad361fd0bfb7ff128ab8f5e24e209e0b923668fbddb7e8b99edb82c1e3d782a80@51.15.54.229:30303",
"enode://e1fcf9e7a47ab43a546d1c1633c511d98d13646bbf5c82d74ff98a1c88e54567b0be6574e977164e1b4c997ef8e79b19f1e12e85a6230c746dd74206fe37cfa0@51.15.35.70:30379",
"enode://14c2960f57f6d63ed541cf64226aafbc7a21c40c6e4935a2e58fd2466fa7d06ec32082734c64d32f7c4692f4b90f26d019f472ba55cdda6d624ef4d7d8441285@51.15.35.70:30303",
"enode://a8512bcaae1245fda71d400291dd22937d89947b6fc31283945557abe1281c5a9325ffc11e363cfed6362e4d2d9b941c5b325270662ba43ac8c424168e6567a6@51.15.39.57:30379",
"enode://02cfa2b02b5431bfdc1bad0f575de8ea151029fe9a9c689074793d704d1b428255bd111bf578f0b4fcaa18267da7c335db9557e1012434f4a8ab2c25f4b3da4d@51.15.39.57:30303"
]

View File

@ -1,4 +1,4 @@
// Package static embeds static (JS, HTML) resources right into the binaries
package static
//go:generate go-bindata -pkg static -o bindata.go scripts/ config/ keys/
//go:generate go-bindata -pkg static -o bindata.go scripts/ bootcluster/ config/ keys/

View File

@ -10,8 +10,8 @@ describe('Whisper Tests', function () {
node1.setProvider(new web3.providers.HttpProvider('http://localhost:8645'));
node2.setProvider(new web3.providers.HttpProvider('http://localhost:8745'));
console.log('Node is expected: statusd --datadir app1 --http --httpport 8645 wnode');
console.log('Node is expected: statusd --datadir app2 --http --httpport 8745 wnode');
console.log('Node is expected: statusd --datadir app1 wnode --http --httpport 8645');
console.log('Node is expected: statusd --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');
// some common vars
@ -216,9 +216,9 @@ describe('Whisper Tests', function () {
assert.lengthOf(filterid1, 64);
});
it('shh.getMessages(filterID) - symmetric filter', function () {
it('shh.getFloatingMessages(filterID) - symmetric filter', function () {
// let's try to capture message that was there *before* filter is created
var messages = node1.shh.getMessages(filterid1);
var messages = node1.shh.getFloatingMessages(filterid1);
assert.typeOf(messages, 'array');
assert.lengthOf(messages, 1);
assert.equal(web3.toAscii(messages[0].payload), payloadBeforeSymFilter);
@ -233,9 +233,9 @@ describe('Whisper Tests', function () {
expect(node1.shh.post(message)).to.equal(null);
});
it('shh.getMessages(filterID) - asymmetric filter', function () {
it('shh.getFloatingMessages(filterID) - asymmetric filter', function () {
// let's try to capture message that was there *before* filter is created
var messages = node1.shh.getMessages(filterid2);
var messages = node1.shh.getFloatingMessages(filterid2);
assert.typeOf(messages, 'array');
assert.lengthOf(messages, 1);
assert.equal(web3.toAscii(messages[0].payload), payloadBeforeAsymFilter);
@ -250,17 +250,17 @@ describe('Whisper Tests', function () {
expect(node1.shh.post(message)).to.equal(null);
});
it('shh.getSubscriptionMessages(filterID) - symmetric filter', function (done) {
it('shh.getNewSubscriptionMessages(filterID) - symmetric filter', function (done) {
// allow some time for message to propagate
setTimeout(function () {
// now let's try to capture new messages from our last capture
var messages = node1.shh.getSubscriptionMessages(filterid1);
var messages = node1.shh.getNewSubscriptionMessages(filterid1);
assert.typeOf(messages, 'array');
assert.lengthOf(messages, 1);
assert.equal(web3.toAscii(messages[0].payload), payloadAfterSymFilter);
// no more messages should be returned
messages = node1.shh.getSubscriptionMessages(filterid1);
messages = node1.shh.getNewSubscriptionMessages(filterid1);
assert.typeOf(messages, 'array');
assert.lengthOf(messages, 0);
@ -268,17 +268,17 @@ describe('Whisper Tests', function () {
}, 200);
});
it('shh.getSubscriptionMessages(filterID) - asymmetric filter', function () {
it('shh.getNewSubscriptionMessages(filterID) - asymmetric filter', function () {
// allow some time for message to propagate
setTimeout(function () {
// now let's try to capture new messages from our last capture
var messages = node1.shh.getSubscriptionMessages(filterid2);
var messages = node1.shh.getNewSubscriptionMessages(filterid2);
assert.typeOf(messages, 'array');
assert.lengthOf(messages, 1);
assert.equal(web3.toAscii(messages[0].payload), payloadAfterAsymFilter);
// no more messages should be returned
messages = node1.shh.getSubscriptionMessages(filterid2);
messages = node1.shh.getNewSubscriptionMessages(filterid2);
assert.typeOf(messages, 'array');
assert.lengthOf(messages, 0);

View File

@ -278,6 +278,18 @@ func DefaultTestnetGenesisBlock() *Genesis {
}
}
// DefaultRinkebyGenesisBlock returns the Rinkeby network genesis block.
func DefaultRinkebyGenesisBlock() *Genesis {
return &Genesis{
Config: params.RinkebyChainConfig,
Timestamp: 1492009146,
ExtraData: hexutil.MustDecode("0x52657370656374206d7920617574686f7269746168207e452e436172746d616e42eb768f2244c8811c63729a21a3569731535f067ffc57839b00206d1ad20c69a1981b489f772031b279182d99e65703f0076e4812653aab85fca0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"),
GasLimit: 4700000,
Difficulty: big.NewInt(1),
Alloc: decodePrealloc(rinkebyAllocData),
}
}
// DevGenesisBlock returns the 'geth --dev' genesis block.
func DevGenesisBlock() *Genesis {
return &Genesis{

File diff suppressed because one or more lines are too long

View File

@ -50,6 +50,22 @@ var (
Ethash: new(EthashConfig),
}
// RinkebyChainConfig contains the chain parameters to run a node on the Rinkeby test network.
RinkebyChainConfig = &ChainConfig{
ChainId: big.NewInt(4),
HomesteadBlock: big.NewInt(1),
DAOForkBlock: nil,
DAOForkSupport: true,
EIP150Block: big.NewInt(2),
EIP150Hash: common.HexToHash("0x9b095b36c15eaf13044373aef8ee0bd3a382a5abb92e402afa44b8249c3a90e9"),
EIP155Block: big.NewInt(3),
EIP158Block: big.NewInt(3),
Clique: &CliqueConfig{
Period: 15,
Epoch: 30000,
},
}
// AllProtocolChanges contains every protocol change (EIPs)
// introduced and accepted by the Ethereum core developers.
// TestChainConfig is like AllProtocolChanges but has chain ID 1.

View File

@ -81,7 +81,10 @@ func (s *discoveryService) processDiscoveryRequest(msg *whisper.ReceivedMessage)
PoW: s.server.config.MinimumPoW,
WorkTime: 5,
}
response := whisper.NewSentMessage(&msgParams)
response, err := whisper.NewSentMessage(&msgParams)
if err != nil {
return fmt.Errorf("failed to create proposal message: %v", err)
}
env, err := response.Wrap(&msgParams)
if err != nil {
return fmt.Errorf("failed to wrap server proposal message: %v", err)
@ -133,7 +136,10 @@ func (s *discoveryService) processServerAcceptedRequest(msg *whisper.ReceivedMes
PoW: s.server.config.MinimumPoW,
WorkTime: 5,
}
response := whisper.NewSentMessage(&msgParams)
response, err := whisper.NewSentMessage(&msgParams)
if err != nil {
return fmt.Errorf("failed to create server proposal message: %v", err)
}
env, err := response.Wrap(&msgParams)
if err != nil {
return fmt.Errorf("failed to wrap server proposal message: %v", err)

View File

@ -324,7 +324,10 @@ func (s *NotificationServer) processNewChatSessionRequest(msg *whisper.ReceivedM
PoW: s.config.MinimumPoW,
WorkTime: 5,
}
response := whisper.NewSentMessage(&msgParams)
response, err := whisper.NewSentMessage(&msgParams)
if err != nil {
return fmt.Errorf("failed to create server response message: %v", err)
}
env, err := response.Wrap(&msgParams)
if err != nil {
return fmt.Errorf("failed to wrap server response message: %v", err)
@ -386,7 +389,10 @@ func (s *NotificationServer) processNewDeviceRegistrationRequest(msg *whisper.Re
PoW: s.config.MinimumPoW,
WorkTime: 5,
}
response := whisper.NewSentMessage(&msgParams)
response, err := whisper.NewSentMessage(&msgParams)
if err != nil {
return fmt.Errorf("failed to create server response message: %v", err)
}
env, err := response.Wrap(&msgParams)
if err != nil {
return fmt.Errorf("failed to wrap server response message: %v", err)
@ -459,7 +465,10 @@ func (s *NotificationServer) processClientSessionStatusRequest(msg *whisper.Rece
PoW: s.config.MinimumPoW,
WorkTime: 5,
}
response := whisper.NewSentMessage(&msgParams)
response, err := whisper.NewSentMessage(&msgParams)
if err != nil {
return fmt.Errorf("failed to create server response message: %v", err)
}
env, err := response.Wrap(&msgParams)
if err != nil {
return fmt.Errorf("failed to wrap server response message: %v", err)

View File

@ -3,6 +3,7 @@ package notifications
import (
"crypto/sha512"
"errors"
"crypto/sha256"
crand "crypto/rand"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
@ -14,8 +15,7 @@ import (
func makeSessionKey() ([]byte, error) {
// generate random key
const keyLen = 32
const size = keyLen * 2
buf := make([]byte, size)
buf := make([]byte, keyLen)
_, err := crand.Read(buf)
if err != nil {
return nil, err
@ -24,8 +24,7 @@ func makeSessionKey() ([]byte, error) {
}
key := buf[:keyLen]
salt := buf[keyLen:]
derived, err := whisper.DeriveOneTimeKey(key, salt, whisper.EnvelopeVersion)
derived, err := deriveKeyMaterial(key, whisper.EnvelopeVersion)
if err != nil {
return nil, err
} else if !validateSymmetricKey(derived) {
@ -50,6 +49,19 @@ func containsOnlyZeros(data []byte) bool {
return true
}
// deriveKeyMaterial derives symmetric key material from the key or password./~~~
// pbkdf2 is used for security, in case people use password instead of randomly generated keys.
func deriveKeyMaterial(key []byte, version uint64) (derivedKey []byte, err error) {
if version == 0 {
// kdf should run no less than 0.1 seconds on average compute,
// because it's a once in a session experience
derivedKey := pbkdf2.Key(key, nil, 65356, 32, sha256.New)
return derivedKey, nil
} else {
return nil, errors.New("unknown version")
}
}
// MakeTopic returns Whisper topic *as bytes array* by generating cryptographic key from the provided password
func MakeTopicAsBytes(password []byte) ([]byte) {
topic := make([]byte, int(whisper.TopicLength))

View File

@ -219,7 +219,6 @@ func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) {
}
filter := Filter{
Src: crypto.ToECDSAPub(common.FromHex(args.Sig)),
PoW: args.MinPoW,
Messages: make(map[common.Hash]*ReceivedMessage),
AllowP2P: args.AllowP2P,
@ -243,8 +242,13 @@ func (api *PublicWhisperAPI) Subscribe(args WhisperFilterArgs) (string, error) {
}
if len(args.Sig) > 0 {
sb := common.FromHex(args.Sig)
if sb == nil {
return "", errors.New("subscribe: sig parameter is invalid")
}
filter.Src = crypto.ToECDSAPub(sb)
if !ValidatePublicKey(filter.Src) {
return "", errors.New("subscribe: sig invalid is invalid")
return "", errors.New("subscribe: invalid 'sig' field")
}
}
@ -284,14 +288,15 @@ func (api *PublicWhisperAPI) Unsubscribe(id string) {
api.whisper.Unsubscribe(id)
}
// GetFilterChanges is alias for GetSubscriptionMessages
// GetFilterChanges is alias for GetNewSubscriptionMessages
func (api *PublicWhisperAPI) GetFilterChanges(filterId string) []*WhisperMessage {
return api.GetSubscriptionMessages(filterId)
return api.GetNewSubscriptionMessages(filterId)
}
// GetSubscriptionMessages retrieves all the new messages matched by a filter since the last retrieval.
func (api *PublicWhisperAPI) GetSubscriptionMessages(filterId string) []*WhisperMessage {
f := api.whisper.GetFilter(filterId)
// GetNewSubscriptionMessages retrieves all the new messages matched by the corresponding
// subscription filter since the last retrieval.
func (api *PublicWhisperAPI) GetNewSubscriptionMessages(id string) []*WhisperMessage {
f := api.whisper.GetFilter(id)
if f != nil {
newMail := f.Retrieve()
return toWhisperMessages(newMail)
@ -299,10 +304,10 @@ func (api *PublicWhisperAPI) GetSubscriptionMessages(filterId string) []*Whisper
return toWhisperMessages(nil)
}
// GetMessages retrieves all the floating messages that match a specific filter.
// GetMessages retrieves all the floating messages that match a specific subscription filter.
// It is likely to be called once per session, right after Subscribe call.
func (api *PublicWhisperAPI) GetMessages(filterId string) []*WhisperMessage {
all := api.whisper.Messages(filterId)
func (api *PublicWhisperAPI) GetFloatingMessages(id string) []*WhisperMessage {
all := api.whisper.Messages(id)
return toWhisperMessages(all)
}
@ -365,7 +370,11 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
return errors.New("post: topic is missing for symmetric encryption")
}
} else if args.Type == "asym" {
params.Dst = crypto.ToECDSAPub(common.FromHex(args.Key))
kb := common.FromHex(args.Key)
if kb == nil {
return errors.New("post: public key for asymmetric encryption is invalid")
}
params.Dst = crypto.ToECDSAPub(kb)
if !ValidatePublicKey(params.Dst) {
return errors.New("post: public key for asymmetric encryption is invalid")
}
@ -374,9 +383,9 @@ func (api *PublicWhisperAPI) Post(args PostArgs) error {
}
// encrypt and send
message := NewSentMessage(&params)
if message == nil {
return errors.New("post: failed create new message, probably due to failed rand function (OS level)")
message, err := NewSentMessage(&params)
if err != nil {
return err
}
envelope, err := message.Wrap(&params)
if err != nil {
@ -403,7 +412,7 @@ type PostArgs struct {
Type string `json:"type"` // "sym"/"asym" (symmetric or asymmetric)
TTL uint32 `json:"ttl"` // time-to-live in seconds
Sig string `json:"sig"` // id of the signing key
Key string `json:"key"` // id of encryption key
Key string `json:"key"` // key id (in case of sym) or public key (in case of asym)
Topic hexutil.Bytes `json:"topic"` // topic (4 bytes)
Padding hexutil.Bytes `json:"padding"` // optional padding bytes
Payload hexutil.Bytes `json:"payload"` // payload to be encrypted
@ -544,7 +553,6 @@ type WhisperMessage struct {
// NewWhisperMessage converts an internal message into an API version.
func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage {
msg := WhisperMessage{
Topic: common.ToHex(message.Topic[:]),
Payload: common.ToHex(message.Payload),
Padding: common.ToHex(message.Padding),
Timestamp: message.Sent,
@ -553,11 +561,20 @@ func NewWhisperMessage(message *ReceivedMessage) *WhisperMessage {
Hash: common.ToHex(message.EnvelopeHash.Bytes()),
}
if len(message.Topic) == TopicLength {
msg.Topic = common.ToHex(message.Topic[:])
}
if message.Dst != nil {
msg.Dst = common.ToHex(crypto.FromECDSAPub(message.Dst))
b := crypto.FromECDSAPub(message.Dst)
if b != nil {
msg.Dst = common.ToHex(b)
}
}
if isMessageSigned(message.Raw[0]) {
msg.Src = common.ToHex(crypto.FromECDSAPub(message.SigToPubKey()))
b := crypto.FromECDSAPub(message.SigToPubKey())
if b != nil {
msg.Src = common.ToHex(b)
}
}
return &msg
}

View File

@ -51,19 +51,17 @@ const (
paddingMask = byte(3)
signatureFlag = byte(4)
TopicLength = 4
signatureLength = 65
aesKeyLength = 32
saltLength = 12
AESNonceMaxLength = 12
keyIdSize = 32
TopicLength = 4
signatureLength = 65
aesKeyLength = 32
AESNonceLength = 12
keyIdSize = 32
DefaultMaxMessageLength = 1024 * 1024
DefaultMinimumPoWTime = 2 // todo: review after testing.
DefaultMinimumPoW = 0.001 // todo: review after testing.
padSizeLimitLower = 128 // it can not be less - we don't want to reveal the absence of signature
padSizeLimitUpper = 256 // just an arbitrary number, could be changed without losing compatibility
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol (must not exceed 2^24)
messageQueueLimit = 1024
expirationCycle = time.Second

View File

@ -40,7 +40,6 @@ type Envelope struct {
Expiry uint32
TTL uint32
Topic TopicType
Salt []byte
AESNonce []byte
Data []byte
EnvNonce uint64
@ -50,15 +49,25 @@ type Envelope struct {
// Don't access hash directly, use Hash() function instead.
}
// size returns the size of envelope as it is sent (i.e. public fields only)
func (e *Envelope) size() int {
return 20 + len(e.Version) + len(e.AESNonce) + len(e.Data)
}
// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
func (e *Envelope) rlpWithoutNonce() []byte {
res, _ := rlp.EncodeToBytes([]interface{}{e.Version, e.Expiry, e.TTL, e.Topic, e.AESNonce, e.Data})
return res
}
// NewEnvelope wraps a Whisper message with expiration and destination data
// included into an envelope for network forwarding.
func NewEnvelope(ttl uint32, topic TopicType, salt []byte, aesNonce []byte, msg *SentMessage) *Envelope {
func NewEnvelope(ttl uint32, topic TopicType, aesNonce []byte, msg *SentMessage) *Envelope {
env := Envelope{
Version: make([]byte, 1),
Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()),
TTL: ttl,
Topic: topic,
Salt: salt,
AESNonce: aesNonce,
Data: msg.Raw,
EnvNonce: 0,
@ -126,10 +135,6 @@ func (e *Envelope) Seal(options *MessageParams) error {
return nil
}
func (e *Envelope) size() int {
return len(e.Data) + len(e.Version) + len(e.AESNonce) + len(e.Salt) + 20
}
func (e *Envelope) PoW() float64 {
if e.pow == 0 {
e.calculatePoW(0)
@ -159,12 +164,6 @@ func (e *Envelope) powToFirstBit(pow float64) int {
return int(bits)
}
// rlpWithoutNonce returns the RLP encoded envelope contents, except the nonce.
func (e *Envelope) rlpWithoutNonce() []byte {
res, _ := rlp.EncodeToBytes([]interface{}{e.Expiry, e.TTL, e.Topic, e.Salt, e.AESNonce, e.Data})
return res
}
// Hash returns the SHA3 hash of the envelope, calculating it if not yet done.
func (e *Envelope) Hash() common.Hash {
if (e.hash == common.Hash{}) {
@ -210,7 +209,7 @@ func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, erro
// OpenSymmetric tries to decrypt an envelope, potentially encrypted with a particular key.
func (e *Envelope) OpenSymmetric(key []byte) (msg *ReceivedMessage, err error) {
msg = &ReceivedMessage{Raw: e.Data}
err = msg.decryptSymmetric(key, e.Salt, e.AESNonce)
err = msg.decryptSymmetric(key, e.AESNonce)
if err != nil {
msg = nil
}

View File

@ -23,14 +23,14 @@ import (
"crypto/cipher"
"crypto/ecdsa"
crand "crypto/rand"
"crypto/sha256"
"encoding/binary"
"errors"
"strconv"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/ecies"
"github.com/ethereum/go-ethereum/log"
"golang.org/x/crypto/pbkdf2"
)
// Options specifies the exact way a message should be wrapped into an Envelope.
@ -86,58 +86,76 @@ func (msg *ReceivedMessage) isAsymmetricEncryption() bool {
return msg.Dst != nil
}
func DeriveOneTimeKey(key []byte, salt []byte, version uint64) ([]byte, error) {
if version == 0 {
derivedKey := pbkdf2.Key(key, salt, 8, aesKeyLength, sha256.New)
return derivedKey, nil
} else {
return nil, unknownVersionError(version)
}
}
// NewMessage creates and initializes a non-signed, non-encrypted Whisper message.
func NewSentMessage(params *MessageParams) *SentMessage {
func NewSentMessage(params *MessageParams) (*SentMessage, error) {
msg := SentMessage{}
msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Payload)+signatureLength+padSizeLimitUpper)
msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit)
msg.Raw[0] = 0 // set all the flags to zero
err := msg.appendPadding(params)
if err != nil {
log.Error("failed to create NewSentMessage", "err", err)
return nil
return nil, err
}
msg.Raw = append(msg.Raw, params.Payload...)
return &msg
return &msg, nil
}
// getSizeOfLength returns the number of bytes necessary to encode the entire size padding (including these bytes)
func getSizeOfLength(b []byte) (sz int, err error) {
sz = intSize(len(b)) // first iteration
sz = intSize(len(b) + sz) // second iteration
if sz > 3 {
err = errors.New("oversized padding parameter")
}
return sz, err
}
// sizeOfIntSize returns minimal number of bytes necessary to encode an integer value
func intSize(i int) (s int) {
for s = 1; i >= 256; s++ {
i /= 256
}
return s
}
// appendPadding appends the pseudorandom padding bytes and sets the padding flag.
// The last byte contains the size of padding (thus, its size must not exceed 256).
func (msg *SentMessage) appendPadding(params *MessageParams) error {
total := len(params.Payload) + 1
rawSize := len(params.Payload) + 1
if params.Src != nil {
total += signatureLength
rawSize += signatureLength
}
padChunk := padSizeLimitUpper
if total <= padSizeLimitLower {
padChunk = padSizeLimitLower
}
odd := total % padChunk
if odd > 0 {
padSize := padChunk - odd
if padSize > 255 {
// this algorithm is only valid if padSizeLimitUpper <= 256.
// if padSizeLimitUpper will ever change, please fix the algorithm
// (for more information see ReceivedMessage.extractPadding() function).
odd := rawSize % padSizeLimit
if len(params.Padding) != 0 {
padSize := len(params.Padding)
padLengthSize, err := getSizeOfLength(params.Padding)
if err != nil {
return err
}
totalPadSize := padSize + padLengthSize
buf := make([]byte, 8)
binary.LittleEndian.PutUint32(buf, uint32(totalPadSize))
buf = buf[:padLengthSize]
msg.Raw = append(msg.Raw, buf...)
msg.Raw = append(msg.Raw, params.Padding...)
msg.Raw[0] |= byte(padLengthSize) // number of bytes indicating the padding size
} else if odd != 0 {
totalPadSize := padSizeLimit - odd
if totalPadSize > 255 {
// this algorithm is only valid if padSizeLimit < 256.
// if padSizeLimit will ever change, please fix the algorithm
// (please see also ReceivedMessage.extractPadding() function).
panic("please fix the padding algorithm before releasing new version")
}
buf := make([]byte, padSize)
buf := make([]byte, totalPadSize)
_, err := crand.Read(buf[1:])
if err != nil {
return err
}
buf[0] = byte(padSize)
if params.Padding != nil {
copy(buf[1:], params.Padding)
if totalPadSize > 6 && !validateSymmetricKey(buf) {
return errors.New("failed to generate random padding of size " + strconv.Itoa(totalPadSize))
}
buf[0] = byte(totalPadSize)
msg.Raw = append(msg.Raw, buf...)
msg.Raw[0] |= byte(0x1) // number of bytes indicating the padding size
}
@ -178,46 +196,31 @@ func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error {
// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256.
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
func (msg *SentMessage) encryptSymmetric(key []byte) (salt []byte, nonce []byte, err error) {
func (msg *SentMessage) encryptSymmetric(key []byte) (nonce []byte, err error) {
if !validateSymmetricKey(key) {
return nil, nil, errors.New("invalid key provided for symmetric encryption")
return nil, errors.New("invalid key provided for symmetric encryption")
}
salt = make([]byte, saltLength)
_, err = crand.Read(salt)
block, err := aes.NewCipher(key)
if err != nil {
return nil, nil, err
} else if !validateSymmetricKey(salt) {
return nil, nil, errors.New("crypto/rand failed to generate salt")
}
derivedKey, err := DeriveOneTimeKey(key, salt, EnvelopeVersion)
if err != nil {
return nil, nil, err
}
if !validateSymmetricKey(derivedKey) {
return nil, nil, errors.New("failed to derive one-time key")
}
block, err := aes.NewCipher(derivedKey)
if err != nil {
return nil, nil, err
return nil, err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, nil, err
return nil, err
}
// never use more than 2^32 random nonces with a given key
nonce = make([]byte, aesgcm.NonceSize())
_, err = crand.Read(nonce)
if err != nil {
return nil, nil, err
return nil, err
} else if !validateSymmetricKey(nonce) {
return nil, nil, errors.New("crypto/rand failed to generate nonce")
return nil, errors.New("crypto/rand failed to generate nonce")
}
msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil)
return salt, nonce, nil
return nonce, nil
}
// Wrap bundles the message into an Envelope to transmit over the network.
@ -231,11 +234,11 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
return nil, err
}
}
var salt, nonce []byte
var nonce []byte
if options.Dst != nil {
err = msg.encryptAsymmetric(options.Dst)
} else if options.KeySym != nil {
salt, nonce, err = msg.encryptSymmetric(options.KeySym)
nonce, err = msg.encryptSymmetric(options.KeySym)
} else {
err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided")
}
@ -244,7 +247,7 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
return nil, err
}
envelope = NewEnvelope(options.TTL, options.Topic, salt, nonce, msg)
envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg)
err = envelope.Seal(options)
if err != nil {
return nil, err
@ -254,13 +257,8 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
// decryptSymmetric decrypts a message with a topic key, using AES-GCM-256.
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
func (msg *ReceivedMessage) decryptSymmetric(key []byte, salt []byte, nonce []byte) error {
derivedKey, err := DeriveOneTimeKey(key, salt, msg.EnvelopeVersion)
if err != nil {
return err
}
block, err := aes.NewCipher(derivedKey)
func (msg *ReceivedMessage) decryptSymmetric(key []byte, nonce []byte) error {
block, err := aes.NewCipher(key)
if err != nil {
return err
}
@ -323,7 +321,8 @@ func (msg *ReceivedMessage) Validate() bool {
// can be successfully decrypted.
func (msg *ReceivedMessage) extractPadding(end int) (int, bool) {
paddingSize := 0
sz := int(msg.Raw[0] & paddingMask) // number of bytes containing the entire size of padding, could be zero
sz := int(msg.Raw[0] & paddingMask) // number of bytes indicating the entire size of padding (including these bytes)
// could be zero -- it means no padding
if sz != 0 {
paddingSize = int(bytesToUintLittleEndian(msg.Raw[1 : 1+sz]))
if paddingSize < sz || paddingSize+1 > end {

View File

@ -149,23 +149,22 @@ func (peer *Peer) expire() {
// broadcast iterates over the collection of envelopes and transmits yet unknown
// ones over the network.
func (p *Peer) broadcast() error {
// Fetch the envelopes and collect the unknown ones
var cnt int
envelopes := p.host.Envelopes()
transmit := make([]*Envelope, 0, len(envelopes))
for _, envelope := range envelopes {
if !p.marked(envelope) {
transmit = append(transmit, envelope)
p.mark(envelope)
err := p2p.Send(p.ws, messagesCode, envelope)
if err != nil {
return err
} else {
p.mark(envelope)
cnt++
}
}
}
if len(transmit) == 0 {
return nil
if cnt > 0 {
log.Trace("broadcast", "num. messages", cnt)
}
// Transmit the unknown batch (potentially empty)
if err := p2p.Send(p.ws, messagesCode, transmit); err != nil {
return err
}
log.Trace("broadcast", "num. messages", len(transmit))
return nil
}

View File

@ -331,24 +331,14 @@ func (w *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) {
// GenerateSymKey generates a random symmetric key and stores it under id,
// which is then returned. Will be used in the future for session key exchange.
func (w *Whisper) GenerateSymKey() (string, error) {
const size = aesKeyLength * 2
buf := make([]byte, size)
_, err := crand.Read(buf)
key := make([]byte, aesKeyLength)
_, err := crand.Read(key)
if err != nil {
return "", err
} else if !validateSymmetricKey(buf) {
} else if !validateSymmetricKey(key) {
return "", fmt.Errorf("error in GenerateSymKey: crypto/rand failed to generate random data")
}
key := buf[:aesKeyLength]
salt := buf[aesKeyLength:]
derived, err := DeriveOneTimeKey(key, salt, EnvelopeVersion)
if err != nil {
return "", err
} else if !validateSymmetricKey(derived) {
return "", fmt.Errorf("failed to derive valid key")
}
id, err := GenerateRandomID()
if err != nil {
return "", fmt.Errorf("failed to generate ID: %s", err)
@ -360,7 +350,7 @@ func (w *Whisper) GenerateSymKey() (string, error) {
if w.symKeys[id] != nil {
return "", fmt.Errorf("failed to generate unique ID")
}
w.symKeys[id] = derived
w.symKeys[id] = key
return id, nil
}
@ -481,6 +471,9 @@ func (w *Whisper) Unsubscribe(id string) error {
// network in the coming cycles.
func (w *Whisper) Send(envelope *Envelope) error {
ok, err := w.add(envelope)
if err != nil {
return err
}
if !ok {
return fmt.Errorf("failed to add envelope")
}
@ -568,21 +561,18 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
log.Warn("unxepected status message received", "peer", p.peer.ID())
case messagesCode:
// decode the contained envelopes
var envelopes []*Envelope
if err := packet.Decode(&envelopes); err != nil {
var envelope Envelope
if err := packet.Decode(&envelope); err != nil {
log.Warn("failed to decode envelope, peer will be disconnected", "peer", p.peer.ID(), "err", err)
return errors.New("invalid envelope")
}
// inject all envelopes into the internal pool
for _, envelope := range envelopes {
cached, err := wh.add(envelope)
if err != nil {
log.Warn("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)
return errors.New("invalid envelope")
}
if cached {
p.mark(envelope)
}
cached, err := wh.add(&envelope)
if err != nil {
log.Warn("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err)
return errors.New("invalid envelope")
}
if cached {
p.mark(&envelope)
}
case p2pCode:
// peer-to-peer message, sent directly to peer bypassing PoW checks, etc.
@ -649,14 +639,11 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
return false, fmt.Errorf("oversized version [%x]", envelope.Hash())
}
if len(envelope.AESNonce) > AESNonceMaxLength {
// the standard AES GSM nonce size is 12,
// but const gcmStandardNonceSize cannot be accessed directly
return false, fmt.Errorf("oversized AESNonce [%x]", envelope.Hash())
}
if len(envelope.Salt) > saltLength {
return false, fmt.Errorf("oversized salt [%x]", envelope.Hash())
aesNonceSize := len(envelope.AESNonce)
if aesNonceSize != 0 && aesNonceSize != AESNonceLength {
// the standard AES GCM nonce size is 12 bytes,
// but constant gcmStandardNonceSize cannot be accessed (not exported)
return false, fmt.Errorf("wrong size of AESNonce: %d bytes [env: %x]", aesNonceSize, envelope.Hash())
}
if envelope.PoW() < wh.minPoW {