Commit feb29327 authored by Péter Szilágyi's avatar Péter Szilágyi Committed by GitHub

Merge pull request #14540 from bas-vk/whisper-api

whisperv5: integrate whisper and implement API
parents f321ed23 ea1d1825
...@@ -33,6 +33,7 @@ import ( ...@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/params" "github.com/ethereum/go-ethereum/params"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
"github.com/naoina/toml" "github.com/naoina/toml"
) )
...@@ -42,7 +43,7 @@ var ( ...@@ -42,7 +43,7 @@ var (
Name: "dumpconfig", Name: "dumpconfig",
Usage: "Show configuration values", Usage: "Show configuration values",
ArgsUsage: "", ArgsUsage: "",
Flags: append(nodeFlags, rpcFlags...), Flags: append(append(nodeFlags, rpcFlags...), whisperFlags...),
Category: "MISCELLANEOUS COMMANDS", Category: "MISCELLANEOUS COMMANDS",
Description: `The dumpconfig command shows configuration values.`, Description: `The dumpconfig command shows configuration values.`,
} }
...@@ -76,6 +77,7 @@ type ethstatsConfig struct { ...@@ -76,6 +77,7 @@ type ethstatsConfig struct {
type gethConfig struct { type gethConfig struct {
Eth eth.Config Eth eth.Config
Shh whisper.Config
Node node.Config Node node.Config
Ethstats ethstatsConfig Ethstats ethstatsConfig
} }
...@@ -99,8 +101,8 @@ func defaultNodeConfig() node.Config { ...@@ -99,8 +101,8 @@ func defaultNodeConfig() node.Config {
cfg := node.DefaultConfig cfg := node.DefaultConfig
cfg.Name = clientIdentifier cfg.Name = clientIdentifier
cfg.Version = params.VersionWithCommit(gitCommit) cfg.Version = params.VersionWithCommit(gitCommit)
cfg.HTTPModules = append(cfg.HTTPModules, "eth") cfg.HTTPModules = append(cfg.HTTPModules, "eth", "shh")
cfg.WSModules = append(cfg.WSModules, "eth") cfg.WSModules = append(cfg.WSModules, "eth", "shh")
cfg.IPCPath = "geth.ipc" cfg.IPCPath = "geth.ipc"
return cfg return cfg
} }
...@@ -109,6 +111,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { ...@@ -109,6 +111,7 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
// Load defaults. // Load defaults.
cfg := gethConfig{ cfg := gethConfig{
Eth: eth.DefaultConfig, Eth: eth.DefaultConfig,
Shh: whisper.DefaultConfig,
Node: defaultNodeConfig(), Node: defaultNodeConfig(),
} }
...@@ -130,19 +133,37 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) { ...@@ -130,19 +133,37 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name) cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
} }
utils.SetShhConfig(ctx, stack, &cfg.Shh)
return stack, cfg return stack, cfg
} }
// enableWhisper returns true in case one of the whisper flags is set.
func enableWhisper(ctx *cli.Context) bool {
for _, flag := range whisperFlags {
if ctx.GlobalIsSet(flag.GetName()) {
return true
}
}
return false
}
func makeFullNode(ctx *cli.Context) *node.Node { func makeFullNode(ctx *cli.Context) *node.Node {
stack, cfg := makeConfigNode(ctx) stack, cfg := makeConfigNode(ctx)
utils.RegisterEthService(stack, &cfg.Eth) utils.RegisterEthService(stack, &cfg.Eth)
// Whisper must be explicitly enabled, but is auto-enabled in --dev mode. // Whisper must be explicitly enabled by specifying at least 1 whisper flag or in dev mode
shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name) shhEnabled := enableWhisper(ctx)
shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name) shhAutoEnabled := !ctx.GlobalIsSet(utils.WhisperEnabledFlag.Name) && ctx.GlobalIsSet(utils.DevModeFlag.Name)
if shhEnabled || shhAutoEnabled { if shhEnabled || shhAutoEnabled {
utils.RegisterShhService(stack) if ctx.GlobalIsSet(utils.WhisperMaxMessageSizeFlag.Name) {
cfg.Shh.MaxMessageSize = uint32(ctx.Int(utils.WhisperMaxMessageSizeFlag.Name))
}
if ctx.GlobalIsSet(utils.WhisperMinPOWFlag.Name) {
cfg.Shh.MinimumAcceptedPOW = ctx.Float64(utils.WhisperMinPOWFlag.Name)
}
utils.RegisterShhService(stack, &cfg.Shh)
} }
// Add the Ethereum Stats daemon if requested. // Add the Ethereum Stats daemon if requested.
......
...@@ -35,7 +35,7 @@ var ( ...@@ -35,7 +35,7 @@ var (
Action: utils.MigrateFlags(localConsole), Action: utils.MigrateFlags(localConsole),
Name: "console", Name: "console",
Usage: "Start an interactive JavaScript environment", Usage: "Start an interactive JavaScript environment",
Flags: append(append(nodeFlags, rpcFlags...), consoleFlags...), Flags: append(append(append(nodeFlags, rpcFlags...), consoleFlags...), whisperFlags...),
Category: "CONSOLE COMMANDS", Category: "CONSOLE COMMANDS",
Description: ` Description: `
The Geth console is an interactive shell for the JavaScript runtime environment The Geth console is an interactive shell for the JavaScript runtime environment
......
...@@ -95,7 +95,6 @@ var ( ...@@ -95,7 +95,6 @@ var (
utils.NetrestrictFlag, utils.NetrestrictFlag,
utils.NodeKeyFileFlag, utils.NodeKeyFileFlag,
utils.NodeKeyHexFlag, utils.NodeKeyHexFlag,
utils.WhisperEnabledFlag,
utils.DevModeFlag, utils.DevModeFlag,
utils.TestnetFlag, utils.TestnetFlag,
utils.RinkebyFlag, utils.RinkebyFlag,
...@@ -125,6 +124,12 @@ var ( ...@@ -125,6 +124,12 @@ var (
utils.IPCDisabledFlag, utils.IPCDisabledFlag,
utils.IPCPathFlag, utils.IPCPathFlag,
} }
whisperFlags = []cli.Flag{
utils.WhisperEnabledFlag,
utils.WhisperMaxMessageSizeFlag,
utils.WhisperMinPOWFlag,
}
) )
func init() { func init() {
...@@ -161,6 +166,7 @@ func init() { ...@@ -161,6 +166,7 @@ func init() {
app.Flags = append(app.Flags, rpcFlags...) app.Flags = append(app.Flags, rpcFlags...)
app.Flags = append(app.Flags, consoleFlags...) app.Flags = append(app.Flags, consoleFlags...)
app.Flags = append(app.Flags, debug.Flags...) app.Flags = append(app.Flags, debug.Flags...)
app.Flags = append(app.Flags, whisperFlags...)
app.Before = func(ctx *cli.Context) error { app.Before = func(ctx *cli.Context) error {
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
......
...@@ -187,6 +187,10 @@ var AppHelpFlagGroups = []flagGroup{ ...@@ -187,6 +187,10 @@ var AppHelpFlagGroups = []flagGroup{
utils.NoCompactionFlag, utils.NoCompactionFlag,
}, debug.Flags...), }, debug.Flags...),
}, },
{
Name: "WHISPER (EXPERIMENTAL)",
Flags: whisperFlags,
},
{ {
Name: "DEPRECATED", Name: "DEPRECATED",
Flags: []cli.Flag{ Flags: []cli.Flag{
...@@ -195,10 +199,7 @@ var AppHelpFlagGroups = []flagGroup{ ...@@ -195,10 +199,7 @@ var AppHelpFlagGroups = []flagGroup{
}, },
}, },
{ {
Name: "EXPERIMENTAL", Name: "MISC",
Flags: []cli.Flag{
utils.WhisperEnabledFlag,
},
}, },
} }
......
...@@ -440,11 +440,6 @@ var ( ...@@ -440,11 +440,6 @@ var (
Usage: "Restricts network communication to the given IP networks (CIDR masks)", Usage: "Restricts network communication to the given IP networks (CIDR masks)",
} }
WhisperEnabledFlag = cli.BoolFlag{
Name: "shh",
Usage: "Enable Whisper",
}
// ATM the url is left to the user and deployment to // ATM the url is left to the user and deployment to
JSpathFlag = cli.StringFlag{ JSpathFlag = cli.StringFlag{
Name: "jspath", Name: "jspath",
...@@ -463,6 +458,20 @@ var ( ...@@ -463,6 +458,20 @@ var (
Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices", Usage: "Suggested gas price is the given percentile of a set of recent transaction gas prices",
Value: eth.DefaultConfig.GPO.Percentile, Value: eth.DefaultConfig.GPO.Percentile,
} }
WhisperEnabledFlag = cli.BoolFlag{
Name: "shh",
Usage: "Enable Whisper",
}
WhisperMaxMessageSizeFlag = cli.IntFlag{
Name: "shh.maxmessagesize",
Usage: "Max message size accepted",
Value: int(whisper.DefaultMaxMessageSize),
}
WhisperMinPOWFlag = cli.Float64Flag{
Name: "shh.pow",
Usage: "Minimum POW accepted",
Value: whisper.DefaultMinimumPoW,
}
) )
// MakeDataDir retrieves the currently requested data directory, terminating // MakeDataDir retrieves the currently requested data directory, terminating
...@@ -878,6 +887,16 @@ func checkExclusive(ctx *cli.Context, flags ...cli.Flag) { ...@@ -878,6 +887,16 @@ func checkExclusive(ctx *cli.Context, flags ...cli.Flag) {
} }
} }
// SetShhConfig applies shh-related command line flags to the config.
func SetShhConfig(ctx *cli.Context, stack *node.Node, cfg *whisper.Config) {
if ctx.GlobalIsSet(WhisperMaxMessageSizeFlag.Name) {
cfg.MaxMessageSize = uint32(ctx.GlobalUint(WhisperMaxMessageSizeFlag.Name))
}
if ctx.GlobalIsSet(WhisperMinPOWFlag.Name) {
cfg.MinimumAcceptedPOW = ctx.GlobalFloat64(WhisperMinPOWFlag.Name)
}
}
// SetEthConfig applies eth-related command line flags to the config. // SetEthConfig applies eth-related command line flags to the config.
func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) { func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
// Avoid conflicting network flags // Avoid conflicting network flags
...@@ -983,8 +1002,10 @@ func RegisterEthService(stack *node.Node, cfg *eth.Config) { ...@@ -983,8 +1002,10 @@ func RegisterEthService(stack *node.Node, cfg *eth.Config) {
} }
// RegisterShhService configures Whisper and adds it to the given node. // RegisterShhService configures Whisper and adds it to the given node.
func RegisterShhService(stack *node.Node) { func RegisterShhService(stack *node.Node, cfg *whisper.Config) {
if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil { if err := stack.Register(func(n *node.ServiceContext) (node.Service, error) {
return whisper.New(cfg), nil
}); err != nil {
Fatalf("Failed to register the Whisper service: %v", err) Fatalf("Failed to register the Whisper service: %v", err)
} }
} }
......
...@@ -87,7 +87,7 @@ var ( ...@@ -87,7 +87,7 @@ var (
argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level") argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level")
argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds") argTTL = flag.Uint("ttl", 30, "time-to-live for messages in seconds")
argWorkTime = flag.Uint("work", 5, "work time in seconds") argWorkTime = flag.Uint("work", 5, "work time in seconds")
argMaxSize = flag.Int("maxsize", whisper.DefaultMaxMessageLength, "max size of message") argMaxSize = flag.Uint("maxsize", uint(whisper.DefaultMaxMessageSize), "max size of message")
argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)") argPoW = flag.Float64("pow", whisper.DefaultMinimumPoW, "PoW for normal messages in float format (e.g. 2.7)")
argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request") argServerPoW = flag.Float64("mspow", whisper.DefaultMinimumPoW, "PoW requirement for Mail Server request")
...@@ -198,6 +198,11 @@ func initialize() { ...@@ -198,6 +198,11 @@ func initialize() {
peers = append(peers, peer) peers = append(peers, peer)
} }
cfg := &whisper.Config{
MaxMessageSize: uint32(*argMaxSize),
MinimumAcceptedPOW: *argPoW,
}
if *mailServerMode { if *mailServerMode {
if len(msPassword) == 0 { if len(msPassword) == 0 {
msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ") msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
...@@ -205,11 +210,12 @@ func initialize() { ...@@ -205,11 +210,12 @@ func initialize() {
utils.Fatalf("Failed to read Mail Server password: %s", err) utils.Fatalf("Failed to read Mail Server password: %s", err)
} }
} }
shh = whisper.New()
shh = whisper.New(cfg)
shh.RegisterServer(&mailServer) shh.RegisterServer(&mailServer)
mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW) mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
} else { } else {
shh = whisper.New() shh = whisper.New(cfg)
} }
if *argPoW != whisper.DefaultMinimumPoW { if *argPoW != whisper.DefaultMinimumPoW {
...@@ -219,8 +225,8 @@ func initialize() { ...@@ -219,8 +225,8 @@ func initialize() {
} }
} }
if *argMaxSize != whisper.DefaultMaxMessageLength { if uint32(*argMaxSize) != whisper.DefaultMaxMessageSize {
err := shh.SetMaxMessageLength(*argMaxSize) err := shh.SetMaxMessageSize(uint32(*argMaxSize))
if err != nil { if err != nil {
utils.Fatalf("Failed to set max message size: %s", err) utils.Fatalf("Failed to set max message size: %s", err)
} }
......
...@@ -526,10 +526,6 @@ const Shh_JS = ` ...@@ -526,10 +526,6 @@ const Shh_JS = `
web3._extend({ web3._extend({
property: 'shh', property: 'shh',
methods: [ methods: [
new web3._extend.Method({
name: 'info',
call: 'shh_info'
}),
new web3._extend.Method({ new web3._extend.Method({
name: 'setMaxMessageLength', name: 'setMaxMessageLength',
call: 'shh_setMaxMessageLength', call: 'shh_setMaxMessageLength',
...@@ -541,8 +537,8 @@ web3._extend({ ...@@ -541,8 +537,8 @@ web3._extend({
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'allowP2PMessagesFromPeer', name: 'markTrustedPeer',
call: 'shh_allowP2PMessagesFromPeer', call: 'shh_markTrustedPeer',
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({
...@@ -570,38 +566,38 @@ web3._extend({ ...@@ -570,38 +566,38 @@ web3._extend({
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'generateSymmetricKey', name: 'newSymKey',
call: 'shh_generateSymmetricKey', call: 'shh_newSymKey',
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'addSymmetricKeyDirect', name: 'addSymKey',
call: 'shh_addSymmetricKeyDirect', call: 'shh_addSymKey',
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'addSymmetricKeyFromPassword', name: 'generateSymKeyFromPassword',
call: 'shh_addSymmetricKeyFromPassword', call: 'shh_generateSymKeyFromPassword',
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'hasSymmetricKey', name: 'hasSymKey',
call: 'shh_hasSymmetricKey', call: 'shh_hasSymKey',
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'getSymmetricKey', name: 'getSymKey',
call: 'shh_getSymmetricKey', call: 'shh_getSymKey',
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'deleteSymmetricKey', name: 'deleteSymKey',
call: 'shh_deleteSymmetricKey', call: 'shh_deleteSymKey',
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'subscribe', name: 'subscribe',
call: 'shh_subscribe', call: 'shh_subscribe',
params: 1 params: 2
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'unsubscribe', name: 'unsubscribe',
...@@ -609,18 +605,28 @@ web3._extend({ ...@@ -609,18 +605,28 @@ web3._extend({
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'getNewSubscriptionMessages', name: 'post',
call: 'shh_getNewSubscriptionMessages', call: 'shh_post',
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'getFloatingMessages', name: 'publicKey',
call: 'shh_getFloatingMessages', call: 'shh_getPublicKey',
params: 1 params: 1
}), }),
new web3._extend.Method({ new web3._extend.Method({
name: 'post', name: 'getFilterMessages',
call: 'shh_post', call: 'shh_getFilterMessages',
params: 1
}),
new web3._extend.Method({
name: 'deleteMessageFilter',
call: 'shh_deleteMessageFilter',
params: 1
}),
new web3._extend.Method({
name: 'newMessageFilter',
call: 'shh_newMessageFilter',
params: 1 params: 1
}) })
], ],
...@@ -630,7 +636,11 @@ web3._extend({ ...@@ -630,7 +636,11 @@ web3._extend({
name: 'version', name: 'version',
getter: 'shh_version', getter: 'shh_version',
outputFormatter: web3._extend.utils.toDecimal outputFormatter: web3._extend.utils.toDecimal
}) }),
new web3._extend.Property({
name: 'info',
getter: 'shh_info'
}),
] ]
}); });
` `
......
...@@ -169,7 +169,9 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) { ...@@ -169,7 +169,9 @@ func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) {
} }
// Register the Whisper protocol if requested // Register the Whisper protocol if requested
if config.WhisperEnabled { if config.WhisperEnabled {
if err := rawStack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil { if err := rawStack.Register(func(*node.ServiceContext) (node.Service, error) {
return whisper.New(&whisper.DefaultConfig), nil
}); err != nil {
return nil, fmt.Errorf("whisper init: %v", err) return nil, fmt.Errorf("whisper init: %v", err)
} }
} }
......
...@@ -349,6 +349,52 @@ func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error { ...@@ -349,6 +349,52 @@ func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error {
return err return err
} }
// ShhSubscribe calls the "shh_subscribe" method with the given arguments,
// registering a subscription. Server notifications for the subscription are
// sent to the given channel. The element type of the channel must match the
// expected type of content returned by the subscription.
//
// The context argument cancels the RPC request that sets up the subscription but has no
// effect on the subscription after ShhSubscribe has returned.
//
// Slow subscribers will be dropped eventually. Client buffers up to 8000 notifications
// before considering the subscriber dead. The subscription Err channel will receive
// ErrSubscriptionQueueOverflow. Use a sufficiently large buffer on the channel or ensure
// that the channel usually has at least one reader to prevent this issue.
func (c *Client) ShhSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) {
// Check type of channel first.
chanVal := reflect.ValueOf(channel)
if chanVal.Kind() != reflect.Chan || chanVal.Type().ChanDir()&reflect.SendDir == 0 {
panic("first argument to ShhSubscribe must be a writable channel")
}
if chanVal.IsNil() {
panic("channel given to ShhSubscribe must not be nil")
}
if c.isHTTP {
return nil, ErrNotificationsUnsupported
}
msg, err := c.newMessage("shh"+subscribeMethodSuffix, args...)
if err != nil {
return nil, err
}
op := &requestOp{
ids: []json.RawMessage{msg.ID},
resp: make(chan *jsonrpcMessage),
sub: newClientSubscription(c, "shh", chanVal),
}
// Send the subscription request.
// The arrival and validity of the response is signaled on sub.quit.
if err := c.send(ctx, op, msg); err != nil {
return nil, err
}
if _, err := op.wait(ctx); err != nil {
return nil, err
}
return op.sub, nil
}
// EthSubscribe calls the "eth_subscribe" method with the given arguments, // EthSubscribe calls the "eth_subscribe" method with the given arguments,
// registering a subscription. Server notifications for the subscription are // registering a subscription. Server notifications for the subscription are
// sent to the given channel. The element type of the channel must match the // sent to the given channel. The element type of the channel must match the
......
Copyright (c) 2009 The Go Authors. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Additional IP Rights Grant (Patents)
"This implementation" means the copyrightable works distributed by
Google as part of the Go project.
Google hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section)
patent license to make, have made, use, offer to sell, sell, import,
transfer and otherwise run, modify and propagate the contents of this
implementation of Go, where such license applies only to those patent
claims, both currently owned or controlled by Google and acquired in
the future, licensable by Google that are necessarily infringed by this
implementation of Go. This grant does not include claims that would be
infringed only as a consequence of further modification of this
implementation. If you or your agent or exclusive licensee institute or
order or agree to the institution of patent litigation against any
entity (including a cross-claim or counterclaim in a lawsuit) alleging
that this implementation of Go or any code incorporated within this
implementation of Go constitutes direct or contributory patent
infringement, or inducement of patent infringement, then any patent
rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.
This diff is collapsed.
...@@ -507,6 +507,12 @@ ...@@ -507,6 +507,12 @@
"revision": "b4690f45fa1cafc47b1c280c2e75116efe40cc13", "revision": "b4690f45fa1cafc47b1c280c2e75116efe40cc13",
"revisionTime": "2017-02-15T08:41:58Z" "revisionTime": "2017-02-15T08:41:58Z"
}, },
{
"checksumSHA1": "4TEYFKrAUuwBMqExjQBsnf/CgjQ=",
"path": "golang.org/x/sync/syncmap",
"revision": "f52d1811a62927559de87708c8913c1650ce4f26",
"revisionTime": "2017-05-17T20:25:26Z"
},
{ {
"checksumSHA1": "rTPzsn0jeqfgnQR0OsMKR8JRy5Y=", "checksumSHA1": "rTPzsn0jeqfgnQR0OsMKR8JRy5Y=",
"path": "golang.org/x/sys/unix", "path": "golang.org/x/sys/unix",
......
...@@ -88,7 +88,7 @@ func TestMailServer(t *testing.T) { ...@@ -88,7 +88,7 @@ func TestMailServer(t *testing.T) {
} }
var server WMailServer var server WMailServer
shh = whisper.New() shh = whisper.New(&whisper.DefaultConfig)
shh.RegisterServer(&server) shh.RegisterServer(&server)
server.Init(shh, dir, password, powRequirement) server.Init(shh, dir, password, powRequirement)
......
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package shhclient
import (
"context"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/rpc"
whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
)
// Client defines typed wrappers for the Whisper v5 RPC API.
type Client struct {
c *rpc.Client
}
// Dial connects a client to the given URL.
func Dial(rawurl string) (*Client, error) {
c, err := rpc.Dial(rawurl)
if err != nil {
return nil, err
}
return NewClient(c), nil
}
// NewClient creates a client that uses the given RPC client.
func NewClient(c *rpc.Client) *Client {
return &Client{c}
}
// Version returns the Whisper sub-protocol version.
func (sc *Client) Version(ctx context.Context) (uint, error) {
var result uint
err := sc.c.CallContext(ctx, &result, "shh_version")
return result, err
}
// Info returns diagnostic information about the whisper node.
func (sc *Client) Info(ctx context.Context) (whisper.Info, error) {
var info whisper.Info
err := sc.c.CallContext(ctx, &info, "shh_info")
return info, err
}
// SetMaxMessageSize sets the maximal message size allowed by this node. Incoming
// and outgoing messages with a larger size will be rejected. Whisper message size
// can never exceed the limit imposed by the underlying P2P protocol (10 Mb).
func (sc *Client) SetMaxMessageSize(ctx context.Context, size uint32) error {
var ignored bool
return sc.c.CallContext(ctx, &ignored, "shh_setMaxMessageSize", size)
}
// SetMinimumPoW (experimental) sets the minimal PoW required by this node.
// This experimental function was introduced for the future dynamic adjustment of
// PoW requirement. If the node is overwhelmed with messages, it should raise the
// PoW requirement and notify the peers. The new value should be set relative to
// the old value (e.g. double). The old value could be obtained via shh_info call.
func (sc *Client) SetMinimumPoW(ctx context.Context, pow float64) error {
var ignored bool
return sc.c.CallContext(ctx, &ignored, "shh_setMinPoW", pow)
}
// Marks specific peer trusted, which will allow it to send historic (expired) messages.
// Note This function is not adding new nodes, the node needs to exists as a peer.
func (sc *Client) MarkTrustedPeer(ctx context.Context, enode string) error {
var ignored bool
return sc.c.CallContext(ctx, &ignored, "shh_markTrustedPeer", enode)
}
// NewKeyPair generates a new public and private key pair for message decryption and encryption.
// It returns an identifier that can be used to refer to the key.
func (sc *Client) NewKeyPair(ctx context.Context) (string, error) {
var id string
return id, sc.c.CallContext(ctx, &id, "shh_newKeyPair")
}
// AddPrivateKey stored the key pair, and returns its ID.
func (sc *Client) AddPrivateKey(ctx context.Context, key []byte) (string, error) {
var id string
return id, sc.c.CallContext(ctx, &id, "shh_addPrivateKey", hexutil.Bytes(key))
}
// DeleteKeyPair delete the specifies key.
func (sc *Client) DeleteKeyPair(ctx context.Context, id string) (string, error) {
var ignored bool
return id, sc.c.CallContext(ctx, &ignored, "shh_deleteKeyPair", id)
}
// HasKeyPair returns an indication if the node has a private key or
// key pair matching the given ID.
func (sc *Client) HasKeyPair(ctx context.Context, id string) (bool, error) {
var has bool
return has, sc.c.CallContext(ctx, &has, "shh_hasKeyPair", id)
}
// PublicKey return the public key for a key ID.
func (sc *Client) PublicKey(ctx context.Context, id string) ([]byte, error) {
var key hexutil.Bytes
return []byte(key), sc.c.CallContext(ctx, &key, "shh_getPublicKey", id)
}
// PrivateKey return the private key for a key ID.
func (sc *Client) PrivateKey(ctx context.Context, id string) ([]byte, error) {
var key hexutil.Bytes
return []byte(key), sc.c.CallContext(ctx, &key, "shh_getPrivateKey", id)
}
// NewSymmetricKey generates a random symmetric key and returns its identifier.
// Can be used encrypting and decrypting messages where the key is known to both parties.
func (sc *Client) NewSymmetricKey(ctx context.Context) (string, error) {
var id string
return id, sc.c.CallContext(ctx, &id, "shh_newSymKey")
}
// AddSymmetricKey stores the key, and returns its identifier.
func (sc *Client) AddSymmetricKey(ctx context.Context, key []byte) (string, error) {
var id string
return id, sc.c.CallContext(ctx, &id, "shh_addSymKey", hexutil.Bytes(key))
}
// GenerateSymmetricKeyFromPassword generates the key from password, stores it, and returns its identifier.
func (sc *Client) GenerateSymmetricKeyFromPassword(ctx context.Context, passwd []byte) (string, error) {
var id string
return id, sc.c.CallContext(ctx, &id, "shh_generateSymKeyFromPassword", hexutil.Bytes(passwd))
}
// HasSymmetricKey returns an indication if the key associated with the given id is stored in the node.
func (sc *Client) HasSymmetricKey(ctx context.Context, id string) (bool, error) {
var found bool
return found, sc.c.CallContext(ctx, &found, "shh_hasSymKey", id)
}
// GetSymmetricKey returns the symmetric key associated with the given identifier.
func (sc *Client) GetSymmetricKey(ctx context.Context, id string) ([]byte, error) {
var key hexutil.Bytes
return []byte(key), sc.c.CallContext(ctx, &key, "shh_getSymKey", id)
}
// DeleteSymmetricKey deletes the symmetric key associated with the given identifier.
func (sc *Client) DeleteSymmetricKey(ctx context.Context, id string) error {
var ignored bool
return sc.c.CallContext(ctx, &ignored, "shh_deleteSymKey", id)
}
// Post a message onto the network.
func (sc *Client) Post(ctx context.Context, message whisper.NewMessage) error {
var ignored bool
return sc.c.CallContext(ctx, &ignored, "shh_post", message)
}
// SubscribeMessages subscribes to messages that match the given criteria. This method
// is only supported on bi-directional connections such as websockets and IPC.
// NewMessageFilter uses polling and is supported over HTTP.
func (ec *Client) SubscribeMessages(ctx context.Context, criteria whisper.Criteria, ch chan<- *whisper.Message) (ethereum.Subscription, error) {
return ec.c.ShhSubscribe(ctx, ch, "messages", criteria)
}
// NewMessageFilter creates a filter within the node. This filter can be used to poll
// for new messages (see FilterMessages) that satisfy the given criteria. A filter can
// timeout when it was polled for in whisper.filterTimeout.
func (ec *Client) NewMessageFilter(ctx context.Context, criteria whisper.Criteria) (string, error) {
var id string
return id, ec.c.CallContext(ctx, &id, "shh_newMessageFilter", criteria)
}
// DeleteMessageFilter removes the filter associated with the given id.
func (ec *Client) DeleteMessageFilter(ctx context.Context, id string) error {
var ignored bool
return ec.c.CallContext(ctx, &ignored, "shh_deleteMessageFilter", id)
}
// FilterMessages retrieves all messages that are received between the last call to
// this function and match the criteria that where given when the filter was created.
func (ec *Client) FilterMessages(ctx context.Context, id string) ([]*whisper.Message, error) {
var messages []*whisper.Message
return messages, ec.c.CallContext(ctx, &messages, "shh_getFilterMessages", id)
}
This diff is collapsed.
This diff is collapsed.
// Copyright 2017 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package whisperv5
type Config struct {
MaxMessageSize uint32 `toml:",omitempty"`
MinimumAcceptedPOW float64 `toml:",omitempty"`
}
var DefaultConfig = Config{
MaxMessageSize: DefaultMaxMessageSize,
MinimumAcceptedPOW: DefaultMinimumPoW,
}
var ()
...@@ -55,8 +55,9 @@ const ( ...@@ -55,8 +55,9 @@ const (
AESNonceLength = 12 AESNonceLength = 12
keyIdSize = 32 keyIdSize = 32
DefaultMaxMessageLength = 1024 * 1024 MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message.
DefaultMinimumPoW = 0.2 DefaultMaxMessageSize = uint32(1024 * 1024)
DefaultMinimumPoW = 0.2
padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol (must not exceed 2^24) padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol (must not exceed 2^24)
messageQueueLimit = 1024 messageQueueLimit = 1024
......
...@@ -62,7 +62,7 @@ func (e *Envelope) rlpWithoutNonce() []byte { ...@@ -62,7 +62,7 @@ func (e *Envelope) rlpWithoutNonce() []byte {
// NewEnvelope wraps a Whisper message with expiration and destination data // NewEnvelope wraps a Whisper message with expiration and destination data
// included into an envelope for network forwarding. // included into an envelope for network forwarding.
func NewEnvelope(ttl uint32, topic TopicType, aesNonce []byte, msg *SentMessage) *Envelope { func NewEnvelope(ttl uint32, topic TopicType, aesNonce []byte, msg *sentMessage) *Envelope {
env := Envelope{ env := Envelope{
Version: make([]byte, 1), Version: make([]byte, 1),
Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()), Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()),
......
...@@ -163,6 +163,7 @@ func (f *Filter) Retrieve() (all []*ReceivedMessage) { ...@@ -163,6 +163,7 @@ func (f *Filter) Retrieve() (all []*ReceivedMessage) {
for _, msg := range f.Messages { for _, msg := range f.Messages {
all = append(all, msg) all = append(all, msg)
} }
f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages
return all return all
} }
......
...@@ -97,7 +97,7 @@ func TestInstallFilters(t *testing.T) { ...@@ -97,7 +97,7 @@ func TestInstallFilters(t *testing.T) {
InitSingleTest() InitSingleTest()
const SizeTestFilters = 256 const SizeTestFilters = 256
w := New() w := New(&Config{})
filters := NewFilters(w) filters := NewFilters(w)
tst := generateTestCases(t, SizeTestFilters) tst := generateTestCases(t, SizeTestFilters)
...@@ -542,7 +542,7 @@ func TestWatchers(t *testing.T) { ...@@ -542,7 +542,7 @@ func TestWatchers(t *testing.T) {
var x, firstID string var x, firstID string
var err error var err error
w := New() w := New(&Config{})
filters := NewFilters(w) filters := NewFilters(w)
tst := generateTestCases(t, NumFilters) tst := generateTestCases(t, NumFilters)
for i = 0; i < NumFilters; i++ { for i = 0; i < NumFilters; i++ {
......
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package whisperv5
import (
"encoding/json"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func (c Criteria) MarshalJSON() ([]byte, error) {
type Criteria struct {
SymKeyID string `json:"symKeyID"`
PrivateKeyID string `json:"privateKeyID"`
Sig hexutil.Bytes `json:"sig"`
MinPow float64 `json:"minPow"`
Topics []TopicType `json:"topics"`
AllowP2P bool `json:"allowP2P"`
}
var enc Criteria
enc.SymKeyID = c.SymKeyID
enc.PrivateKeyID = c.PrivateKeyID
enc.Sig = c.Sig
enc.MinPow = c.MinPow
enc.Topics = c.Topics
enc.AllowP2P = c.AllowP2P
return json.Marshal(&enc)
}
func (c *Criteria) UnmarshalJSON(input []byte) error {
type Criteria struct {
SymKeyID *string `json:"symKeyID"`
PrivateKeyID *string `json:"privateKeyID"`
Sig hexutil.Bytes `json:"sig"`
MinPow *float64 `json:"minPow"`
Topics []TopicType `json:"topics"`
AllowP2P *bool `json:"allowP2P"`
}
var dec Criteria
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.SymKeyID != nil {
c.SymKeyID = *dec.SymKeyID
}
if dec.PrivateKeyID != nil {
c.PrivateKeyID = *dec.PrivateKeyID
}
if dec.Sig != nil {
c.Sig = dec.Sig
}
if dec.MinPow != nil {
c.MinPow = *dec.MinPow
}
if dec.Topics != nil {
c.Topics = dec.Topics
}
if dec.AllowP2P != nil {
c.AllowP2P = *dec.AllowP2P
}
return nil
}
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package whisperv5
import (
"encoding/json"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func (m Message) MarshalJSON() ([]byte, error) {
type Message struct {
Sig hexutil.Bytes `json:"sig,omitempty"`
TTL uint32 `json:"ttl"`
Timestamp uint32 `json:"timestamp"`
Topic TopicType `json:"topic"`
Payload hexutil.Bytes `json:"payload"`
Padding hexutil.Bytes `json:"padding"`
PoW float64 `json:"pow"`
Hash hexutil.Bytes `json:"hash"`
Dst hexutil.Bytes `json:"recipientPublicKey,omitempty"`
}
var enc Message
enc.Sig = m.Sig
enc.TTL = m.TTL
enc.Timestamp = m.Timestamp
enc.Topic = m.Topic
enc.Payload = m.Payload
enc.Padding = m.Padding
enc.PoW = m.PoW
enc.Hash = m.Hash
enc.Dst = m.Dst
return json.Marshal(&enc)
}
func (m *Message) UnmarshalJSON(input []byte) error {
type Message struct {
Sig hexutil.Bytes `json:"sig,omitempty"`
TTL *uint32 `json:"ttl"`
Timestamp *uint32 `json:"timestamp"`
Topic *TopicType `json:"topic"`
Payload hexutil.Bytes `json:"payload"`
Padding hexutil.Bytes `json:"padding"`
PoW *float64 `json:"pow"`
Hash hexutil.Bytes `json:"hash"`
Dst hexutil.Bytes `json:"recipientPublicKey,omitempty"`
}
var dec Message
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.Sig != nil {
m.Sig = dec.Sig
}
if dec.TTL != nil {
m.TTL = *dec.TTL
}
if dec.Timestamp != nil {
m.Timestamp = *dec.Timestamp
}
if dec.Topic != nil {
m.Topic = *dec.Topic
}
if dec.Payload != nil {
m.Payload = dec.Payload
}
if dec.Padding != nil {
m.Padding = dec.Padding
}
if dec.PoW != nil {
m.PoW = *dec.PoW
}
if dec.Hash != nil {
m.Hash = dec.Hash
}
if dec.Dst != nil {
m.Dst = dec.Dst
}
return nil
}
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package whisperv5
import (
"encoding/json"
"github.com/ethereum/go-ethereum/common/hexutil"
)
func (n NewMessage) MarshalJSON() ([]byte, error) {
type NewMessage struct {
SymKeyID string `json:"symKeyID"`
PublicKey hexutil.Bytes `json:"pubKey"`
Sig string `json:"sig"`
TTL uint32 `json:"ttl"`
Topic TopicType `json:"topic"`
Payload hexutil.Bytes `json:"payload"`
Padding hexutil.Bytes `json:"padding"`
PowTime uint32 `json:"powTime"`
PowTarget float64 `json:"powTarget"`
TargetPeer string `json:"targetPeer"`
}
var enc NewMessage
enc.SymKeyID = n.SymKeyID
enc.PublicKey = n.PublicKey
enc.Sig = n.Sig
enc.TTL = n.TTL
enc.Topic = n.Topic
enc.Payload = n.Payload
enc.Padding = n.Padding
enc.PowTime = n.PowTime
enc.PowTarget = n.PowTarget
enc.TargetPeer = n.TargetPeer
return json.Marshal(&enc)
}
func (n *NewMessage) UnmarshalJSON(input []byte) error {
type NewMessage struct {
SymKeyID *string `json:"symKeyID"`
PublicKey hexutil.Bytes `json:"pubKey"`
Sig *string `json:"sig"`
TTL *uint32 `json:"ttl"`
Topic *TopicType `json:"topic"`
Payload hexutil.Bytes `json:"payload"`
Padding hexutil.Bytes `json:"padding"`
PowTime *uint32 `json:"powTime"`
PowTarget *float64 `json:"powTarget"`
TargetPeer *string `json:"targetPeer"`
}
var dec NewMessage
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.SymKeyID != nil {
n.SymKeyID = *dec.SymKeyID
}
if dec.PublicKey != nil {
n.PublicKey = dec.PublicKey
}
if dec.Sig != nil {
n.Sig = *dec.Sig
}
if dec.TTL != nil {
n.TTL = *dec.TTL
}
if dec.Topic != nil {
n.Topic = *dec.Topic
}
if dec.Payload != nil {
n.Payload = dec.Payload
}
if dec.Padding != nil {
n.Padding = dec.Padding
}
if dec.PowTime != nil {
n.PowTime = *dec.PowTime
}
if dec.PowTarget != nil {
n.PowTarget = *dec.PowTarget
}
if dec.TargetPeer != nil {
n.TargetPeer = *dec.TargetPeer
}
return nil
}
...@@ -49,7 +49,7 @@ type MessageParams struct { ...@@ -49,7 +49,7 @@ type MessageParams struct {
// SentMessage represents an end-user data packet to transmit through the // SentMessage represents an end-user data packet to transmit through the
// Whisper protocol. These are wrapped into Envelopes that need not be // Whisper protocol. These are wrapped into Envelopes that need not be
// understood by intermediate nodes, just forwarded. // understood by intermediate nodes, just forwarded.
type SentMessage struct { type sentMessage struct {
Raw []byte Raw []byte
} }
...@@ -87,8 +87,8 @@ func (msg *ReceivedMessage) isAsymmetricEncryption() bool { ...@@ -87,8 +87,8 @@ func (msg *ReceivedMessage) isAsymmetricEncryption() bool {
} }
// NewMessage creates and initializes a non-signed, non-encrypted Whisper message. // NewMessage creates and initializes a non-signed, non-encrypted Whisper message.
func NewSentMessage(params *MessageParams) (*SentMessage, error) { func NewSentMessage(params *MessageParams) (*sentMessage, error) {
msg := SentMessage{} msg := sentMessage{}
msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit) msg.Raw = make([]byte, 1, len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit)
msg.Raw[0] = 0 // set all the flags to zero msg.Raw[0] = 0 // set all the flags to zero
err := msg.appendPadding(params) err := msg.appendPadding(params)
...@@ -119,7 +119,7 @@ func intSize(i int) (s int) { ...@@ -119,7 +119,7 @@ func intSize(i int) (s int) {
// appendPadding appends the pseudorandom padding bytes and sets the padding flag. // 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). // The last byte contains the size of padding (thus, its size must not exceed 256).
func (msg *SentMessage) appendPadding(params *MessageParams) error { func (msg *sentMessage) appendPadding(params *MessageParams) error {
rawSize := len(params.Payload) + 1 rawSize := len(params.Payload) + 1
if params.Src != nil { if params.Src != nil {
rawSize += signatureLength rawSize += signatureLength
...@@ -164,7 +164,7 @@ func (msg *SentMessage) appendPadding(params *MessageParams) error { ...@@ -164,7 +164,7 @@ func (msg *SentMessage) appendPadding(params *MessageParams) error {
// sign calculates and sets the cryptographic signature for the message, // sign calculates and sets the cryptographic signature for the message,
// also setting the sign flag. // also setting the sign flag.
func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error { func (msg *sentMessage) sign(key *ecdsa.PrivateKey) error {
if isMessageSigned(msg.Raw[0]) { if isMessageSigned(msg.Raw[0]) {
// this should not happen, but no reason to panic // this should not happen, but no reason to panic
log.Error("failed to sign the message: already signed") log.Error("failed to sign the message: already signed")
...@@ -183,7 +183,7 @@ func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error { ...@@ -183,7 +183,7 @@ func (msg *SentMessage) sign(key *ecdsa.PrivateKey) error {
} }
// encryptAsymmetric encrypts a message with a public key. // encryptAsymmetric encrypts a message with a public key.
func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { func (msg *sentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error {
if !ValidatePublicKey(key) { if !ValidatePublicKey(key) {
return errors.New("invalid public key provided for asymmetric encryption") return errors.New("invalid public key provided for asymmetric encryption")
} }
...@@ -196,7 +196,7 @@ func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { ...@@ -196,7 +196,7 @@ func (msg *SentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error {
// encryptSymmetric encrypts a message with a topic key, using AES-GCM-256. // encryptSymmetric encrypts a message with a topic key, using AES-GCM-256.
// nonce size should be 12 bytes (see cipher.gcmStandardNonceSize). // nonce size should be 12 bytes (see cipher.gcmStandardNonceSize).
func (msg *SentMessage) encryptSymmetric(key []byte) (nonce []byte, err error) { func (msg *sentMessage) encryptSymmetric(key []byte) (nonce []byte, err error) {
if !validateSymmetricKey(key) { if !validateSymmetricKey(key) {
return nil, errors.New("invalid key provided for symmetric encryption") return nil, errors.New("invalid key provided for symmetric encryption")
} }
...@@ -224,13 +224,12 @@ func (msg *SentMessage) encryptSymmetric(key []byte) (nonce []byte, err error) { ...@@ -224,13 +224,12 @@ func (msg *SentMessage) encryptSymmetric(key []byte) (nonce []byte, err error) {
} }
// Wrap bundles the message into an Envelope to transmit over the network. // Wrap bundles the message into an Envelope to transmit over the network.
func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) { func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) {
if options.TTL == 0 { if options.TTL == 0 {
options.TTL = DefaultTTL options.TTL = DefaultTTL
} }
if options.Src != nil { if options.Src != nil {
err = msg.sign(options.Src) if err = msg.sign(options.Src); err != nil {
if err != nil {
return nil, err return nil, err
} }
} }
...@@ -242,14 +241,12 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er ...@@ -242,14 +241,12 @@ func (msg *SentMessage) Wrap(options *MessageParams) (envelope *Envelope, err er
} else { } else {
err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided") err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided")
} }
if err != nil { if err != nil {
return nil, err return nil, err
} }
envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg) envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg)
err = envelope.Seal(options) if err = envelope.Seal(options); err != nil {
if err != nil {
return nil, err return nil, err
} }
return envelope, nil return envelope, nil
......
...@@ -113,7 +113,7 @@ func initialize(t *testing.T) { ...@@ -113,7 +113,7 @@ func initialize(t *testing.T) {
for i := 0; i < NumNodes; i++ { for i := 0; i < NumNodes; i++ {
var node TestNode var node TestNode
node.shh = New() node.shh = New(&DefaultConfig)
node.shh.SetMinimumPoW(0.00000001) node.shh.SetMinimumPoW(0.00000001)
node.shh.Start(nil) node.shh.Start(nil)
topics := make([]TopicType, 0) topics := make([]TopicType, 0)
......
...@@ -19,10 +19,8 @@ ...@@ -19,10 +19,8 @@
package whisperv5 package whisperv5
import ( import (
"fmt"
"strings"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
) )
// Topic represents a cryptographically secure, probabilistic partial // Topic represents a cryptographically secure, probabilistic partial
...@@ -46,24 +44,12 @@ func (topic *TopicType) String() string { ...@@ -46,24 +44,12 @@ func (topic *TopicType) String() string {
return string(common.ToHex(topic[:])) return string(common.ToHex(topic[:]))
} }
// UnmarshalJSON parses a hex representation to a topic. // MarshalText returns the hex representation of t.
func (t *TopicType) UnmarshalJSON(input []byte) error { func (t TopicType) MarshalText() ([]byte, error) {
length := len(input) return hexutil.Bytes(t[:]).MarshalText()
if length >= 2 && input[0] == '"' && input[length-1] == '"' { }
input = input[1 : length-1]
} // UnmarshalText parses a hex representation to a topic.
// strip "0x" for length check func (t *TopicType) UnmarshalText(input []byte) error {
if len(input) > 1 && strings.ToLower(string(input[:2])) == "0x" { return hexutil.UnmarshalFixedText("Topic", input, t[:])
input = input[2:]
}
// validate the length of the input
if len(input) != TopicLength*2 {
return fmt.Errorf("unmarshalJSON failed: topic must be exactly %d bytes", TopicLength)
}
b := common.FromHex(string(input))
if b == nil {
return fmt.Errorf("unmarshalJSON failed: wrong topic format")
}
*t = BytesToTopic(b)
return nil
} }
...@@ -16,7 +16,10 @@ ...@@ -16,7 +16,10 @@
package whisperv5 package whisperv5
import "testing" import (
"encoding/json"
"testing"
)
var topicStringTests = []struct { var topicStringTests = []struct {
topic TopicType topic TopicType
...@@ -53,60 +56,55 @@ var bytesToTopicTests = []struct { ...@@ -53,60 +56,55 @@ var bytesToTopicTests = []struct {
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: nil}, {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: nil},
} }
func TestBytesToTopic(t *testing.T) {
for i, tst := range bytesToTopicTests {
top := BytesToTopic(tst.data)
if top != tst.topic {
t.Fatalf("failed test %d: have %v, want %v.", i, t, tst.topic)
}
}
}
var unmarshalTestsGood = []struct { var unmarshalTestsGood = []struct {
topic TopicType topic TopicType
data []byte data []byte
}{ }{
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0x00000000")}, {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x00000000"`)},
{topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte("0x007f80ff")}, {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte(`"0x007f80ff"`)},
{topic: TopicType{0xff, 0x80, 0x7f, 0x00}, data: []byte("0xff807f00")}, {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, data: []byte(`"0xff807f00"`)},
{topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, data: []byte("0xf26e7779")}, {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, data: []byte(`"0xf26e7779"`)},
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("00000000")},
{topic: TopicType{0x00, 0x80, 0x01, 0x00}, data: []byte("00800100")},
{topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte("007f80ff")},
{topic: TopicType{0xff, 0x80, 0x7f, 0x00}, data: []byte("ff807f00")},
{topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, data: []byte("f26e7779")},
} }
var unmarshalTestsBad = []struct { var unmarshalTestsBad = []struct {
topic TopicType topic TopicType
data []byte data []byte
}{ }{
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0x000000")}, {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x000000"`)},
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0x0000000")}, {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x0000000"`)},
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0x000000000")}, {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x000000000"`)},
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0x0000000000")}, {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x0000000000"`)},
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("000000")}, {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"000000"`)},
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0000000")}, {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0000000"`)},
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("000000000")}, {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"000000000"`)},
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("0000000000")}, {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0000000000"`)},
{topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte("abcdefg0")}, {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"abcdefg0"`)},
} }
var unmarshalTestsUgly = []struct { var unmarshalTestsUgly = []struct {
topic TopicType topic TopicType
data []byte data []byte
}{ }{
{topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte("00000001")}, {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte(`"0x00000001"`)},
}
func TestBytesToTopic(t *testing.T) {
for i, tst := range bytesToTopicTests {
top := BytesToTopic(tst.data)
if top != tst.topic {
t.Fatalf("failed test %d: have %v, want %v.", i, t, tst.topic)
}
}
} }
func TestUnmarshalTestsGood(t *testing.T) { func TestUnmarshalTestsGood(t *testing.T) {
for i, tst := range unmarshalTestsGood { for i, tst := range unmarshalTestsGood {
var top TopicType var top TopicType
err := top.UnmarshalJSON(tst.data) err := json.Unmarshal(tst.data, &top)
if err != nil { if err != nil {
t.Fatalf("failed test %d. input: %v.", i, tst.data) t.Errorf("failed test %d. input: %v. err: %v", i, tst.data, err)
} else if top != tst.topic { } else if top != tst.topic {
t.Fatalf("failed test %d: have %v, want %v.", i, t, tst.topic) t.Errorf("failed test %d: have %v, want %v.", i, t, tst.topic)
} }
} }
} }
...@@ -115,7 +113,7 @@ func TestUnmarshalTestsBad(t *testing.T) { ...@@ -115,7 +113,7 @@ func TestUnmarshalTestsBad(t *testing.T) {
// in this test UnmarshalJSON() is supposed to fail // in this test UnmarshalJSON() is supposed to fail
for i, tst := range unmarshalTestsBad { for i, tst := range unmarshalTestsBad {
var top TopicType var top TopicType
err := top.UnmarshalJSON(tst.data) err := json.Unmarshal(tst.data, &top)
if err == nil { if err == nil {
t.Fatalf("failed test %d. input: %v.", i, tst.data) t.Fatalf("failed test %d. input: %v.", i, tst.data)
} }
...@@ -126,11 +124,11 @@ func TestUnmarshalTestsUgly(t *testing.T) { ...@@ -126,11 +124,11 @@ func TestUnmarshalTestsUgly(t *testing.T) {
// in this test UnmarshalJSON() is NOT supposed to fail, but result should be wrong // in this test UnmarshalJSON() is NOT supposed to fail, but result should be wrong
for i, tst := range unmarshalTestsUgly { for i, tst := range unmarshalTestsUgly {
var top TopicType var top TopicType
err := top.UnmarshalJSON(tst.data) err := json.Unmarshal(tst.data, &top)
if err != nil { if err != nil {
t.Fatalf("failed test %d. input: %v.", i, tst.data) t.Errorf("failed test %d. input: %v.", i, tst.data)
} else if top == tst.topic { } else if top == tst.topic {
t.Fatalf("failed test %d: have %v, want %v.", i, top, tst.topic) t.Errorf("failed test %d: have %v, want %v.", i, top, tst.topic)
} }
} }
} }
...@@ -33,6 +33,7 @@ import ( ...@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/syndtr/goleveldb/leveldb/errors" "github.com/syndtr/goleveldb/leveldb/errors"
"golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/pbkdf2"
"golang.org/x/sync/syncmap"
set "gopkg.in/fatih/set.v0" set "gopkg.in/fatih/set.v0"
) )
...@@ -44,6 +45,12 @@ type Statistics struct { ...@@ -44,6 +45,12 @@ type Statistics struct {
totalMessagesCleared int totalMessagesCleared int
} }
const (
minPowIdx = iota // Minimal PoW required by the whisper node
maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node
overflowIdx = iota // Indicator of message queue overflow
)
// Whisper represents a dark communication interface through the Ethereum // Whisper represents a dark communication interface through the Ethereum
// network, using its very own P2P communication layer. // network, using its very own P2P communication layer.
type Whisper struct { type Whisper struct {
...@@ -54,28 +61,31 @@ type Whisper struct { ...@@ -54,28 +61,31 @@ type Whisper struct {
symKeys map[string][]byte // Symmetric key storage symKeys map[string][]byte // Symmetric key storage
keyMu sync.RWMutex // Mutex associated with key storages keyMu sync.RWMutex // Mutex associated with key storages
poolMu sync.RWMutex // Mutex to sync the message and expiration pools
envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node envelopes map[common.Hash]*Envelope // Pool of envelopes currently tracked by this node
expirations map[uint32]*set.SetNonTS // Message expiration pool expirations map[uint32]*set.SetNonTS // Message expiration pool
poolMu sync.RWMutex // Mutex to sync the message and expiration pools
peers map[*Peer]struct{} // Set of currently active peers
peerMu sync.RWMutex // Mutex to sync the active peer set peerMu sync.RWMutex // Mutex to sync the active peer set
peers map[*Peer]struct{} // Set of currently active peers
messageQueue chan *Envelope // Message queue for normal whisper messages messageQueue chan *Envelope // Message queue for normal whisper messages
p2pMsgQueue chan *Envelope // Message queue for peer-to-peer messages (not to be forwarded any further) p2pMsgQueue chan *Envelope // Message queue for peer-to-peer messages (not to be forwarded any further)
quit chan struct{} // Channel used for graceful exit quit chan struct{} // Channel used for graceful exit
minPoW float64 // Minimal PoW required by the whisper node settings syncmap.Map // holds configuration settings that can be dynamically changed
maxMsgLength int // Maximal message length allowed by the whisper node
overflow bool // Indicator of message queue overflow
stats Statistics // Statistics of whisper node statsMu sync.Mutex // guard stats
stats Statistics // Statistics of whisper node
mailServer MailServer // MailServer interface mailServer MailServer // MailServer interface
} }
// New creates a Whisper client ready to communicate through the Ethereum P2P network. // New creates a Whisper client ready to communicate through the Ethereum P2P network.
func New() *Whisper { func New(cfg *Config) *Whisper {
if cfg == nil {
cfg = &DefaultConfig
}
whisper := &Whisper{ whisper := &Whisper{
privateKeys: make(map[string]*ecdsa.PrivateKey), privateKeys: make(map[string]*ecdsa.PrivateKey),
symKeys: make(map[string][]byte), symKeys: make(map[string][]byte),
...@@ -85,22 +95,49 @@ func New() *Whisper { ...@@ -85,22 +95,49 @@ func New() *Whisper {
messageQueue: make(chan *Envelope, messageQueueLimit), messageQueue: make(chan *Envelope, messageQueueLimit),
p2pMsgQueue: make(chan *Envelope, messageQueueLimit), p2pMsgQueue: make(chan *Envelope, messageQueueLimit),
quit: make(chan struct{}), quit: make(chan struct{}),
minPoW: DefaultMinimumPoW,
maxMsgLength: DefaultMaxMessageLength,
} }
whisper.filters = NewFilters(whisper) whisper.filters = NewFilters(whisper)
whisper.settings.Store(minPowIdx, cfg.MinimumAcceptedPOW)
whisper.settings.Store(maxMsgSizeIdx, cfg.MaxMessageSize)
whisper.settings.Store(overflowIdx, false)
// p2p whisper sub protocol handler // p2p whisper sub protocol handler
whisper.protocol = p2p.Protocol{ whisper.protocol = p2p.Protocol{
Name: ProtocolName, Name: ProtocolName,
Version: uint(ProtocolVersion), Version: uint(ProtocolVersion),
Length: NumberOfMessageCodes, Length: NumberOfMessageCodes,
Run: whisper.HandlePeer, Run: whisper.HandlePeer,
NodeInfo: func() interface{} {
return map[string]interface{}{
"version": ProtocolVersionStr,
"maxMessageSize": whisper.MaxMessageSize(),
"minimumPoW": whisper.MinPow(),
}
},
} }
return whisper return whisper
} }
func (w *Whisper) MinPow() float64 {
val, _ := w.settings.Load(minPowIdx)
return val.(float64)
}
// MaxMessageSize returns the maximum accepted message size.
func (w *Whisper) MaxMessageSize() uint32 {
val, _ := w.settings.Load(maxMsgSizeIdx)
return val.(uint32)
}
// Overflow returns an indication if the message queue is full.
func (w *Whisper) Overflow() bool {
val, _ := w.settings.Load(overflowIdx)
return val.(bool)
}
// APIs returns the RPC descriptors the Whisper implementation offers // APIs returns the RPC descriptors the Whisper implementation offers
func (w *Whisper) APIs() []rpc.API { func (w *Whisper) APIs() []rpc.API {
return []rpc.API{ return []rpc.API{
...@@ -129,12 +166,12 @@ func (w *Whisper) Version() uint { ...@@ -129,12 +166,12 @@ func (w *Whisper) Version() uint {
return w.protocol.Version return w.protocol.Version
} }
// SetMaxMessageLength sets the maximal message length allowed by this node // SetMaxMessageSize sets the maximal message size allowed by this node
func (w *Whisper) SetMaxMessageLength(val int) error { func (w *Whisper) SetMaxMessageSize(size uint32) error {
if val <= 0 { if size > MaxMessageSize {
return fmt.Errorf("invalid message length: %d", val) return fmt.Errorf("message size too large [%d>%d]", size, MaxMessageSize)
} }
w.maxMsgLength = val w.settings.Store(maxMsgSizeIdx, uint32(size))
return nil return nil
} }
...@@ -143,7 +180,7 @@ func (w *Whisper) SetMinimumPoW(val float64) error { ...@@ -143,7 +180,7 @@ func (w *Whisper) SetMinimumPoW(val float64) error {
if val <= 0.0 { if val <= 0.0 {
return fmt.Errorf("invalid PoW: %f", val) return fmt.Errorf("invalid PoW: %f", val)
} }
w.minPoW = val w.settings.Store(minPowIdx, val)
return nil return nil
} }
...@@ -240,6 +277,20 @@ func (w *Whisper) DeleteKeyPair(key string) bool { ...@@ -240,6 +277,20 @@ func (w *Whisper) DeleteKeyPair(key string) bool {
return false return false
} }
// AddKeyPair imports a asymmetric private key and returns it identifier.
func (w *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) {
id, err := GenerateRandomID()
if err != nil {
return "", fmt.Errorf("failed to generate ID: %s", err)
}
w.keyMu.Lock()
w.privateKeys[id] = key
w.keyMu.Unlock()
return id, nil
}
// HasKeyPair checks if the the whisper node is configured with the private key // HasKeyPair checks if the the whisper node is configured with the private key
// of the specified public pair. // of the specified public pair.
func (w *Whisper) HasKeyPair(id string) bool { func (w *Whisper) HasKeyPair(id string) bool {
...@@ -451,7 +502,7 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { ...@@ -451,7 +502,7 @@ func (wh *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error {
log.Warn("message loop", "peer", p.peer.ID(), "err", err) log.Warn("message loop", "peer", p.peer.ID(), "err", err)
return err return err
} }
if packet.Size > uint32(wh.maxMsgLength) { if packet.Size > wh.MaxMessageSize() {
log.Warn("oversized message received", "peer", p.peer.ID()) log.Warn("oversized message received", "peer", p.peer.ID())
return errors.New("oversized message received") return errors.New("oversized message received")
} }
...@@ -532,7 +583,7 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) { ...@@ -532,7 +583,7 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
} }
} }
if envelope.size() > wh.maxMsgLength { if uint32(envelope.size()) > wh.MaxMessageSize() {
return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash()) return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash())
} }
...@@ -547,7 +598,7 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) { ...@@ -547,7 +598,7 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
return false, fmt.Errorf("wrong size of AESNonce: %d bytes [env: %x]", aesNonceSize, envelope.Hash()) return false, fmt.Errorf("wrong size of AESNonce: %d bytes [env: %x]", aesNonceSize, envelope.Hash())
} }
if envelope.PoW() < wh.minPoW { if envelope.PoW() < wh.MinPow() {
log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex()) log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex())
return false, nil // drop envelope without error return false, nil // drop envelope without error
} }
...@@ -571,7 +622,9 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) { ...@@ -571,7 +622,9 @@ func (wh *Whisper) add(envelope *Envelope) (bool, error) {
log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex()) log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex())
} else { } else {
log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex()) log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex())
wh.statsMu.Lock()
wh.stats.memoryUsed += envelope.size() wh.stats.memoryUsed += envelope.size()
wh.statsMu.Unlock()
wh.postEvent(envelope, false) // notify the local node about the new message wh.postEvent(envelope, false) // notify the local node about the new message
if wh.mailServer != nil { if wh.mailServer != nil {
wh.mailServer.Archive(envelope) wh.mailServer.Archive(envelope)
...@@ -600,13 +653,13 @@ func (w *Whisper) checkOverflow() { ...@@ -600,13 +653,13 @@ func (w *Whisper) checkOverflow() {
queueSize := len(w.messageQueue) queueSize := len(w.messageQueue)
if queueSize == messageQueueLimit { if queueSize == messageQueueLimit {
if !w.overflow { if !w.Overflow() {
w.overflow = true w.settings.Store(overflowIdx, true)
log.Warn("message queue overflow") log.Warn("message queue overflow")
} }
} else if queueSize <= messageQueueLimit/2 { } else if queueSize <= messageQueueLimit/2 {
if w.overflow { if w.Overflow() {
w.overflow = false w.settings.Store(overflowIdx, false)
log.Warn("message queue overflow fixed (back to normal)") log.Warn("message queue overflow fixed (back to normal)")
} }
} }
...@@ -653,6 +706,8 @@ func (w *Whisper) expire() { ...@@ -653,6 +706,8 @@ func (w *Whisper) expire() {
w.poolMu.Lock() w.poolMu.Lock()
defer w.poolMu.Unlock() defer w.poolMu.Unlock()
w.statsMu.Lock()
defer w.statsMu.Unlock()
w.stats.reset() w.stats.reset()
now := uint32(time.Now().Unix()) now := uint32(time.Now().Unix())
for expiry, hashSet := range w.expirations { for expiry, hashSet := range w.expirations {
...@@ -673,17 +728,11 @@ func (w *Whisper) expire() { ...@@ -673,17 +728,11 @@ func (w *Whisper) expire() {
} }
// Stats returns the whisper node statistics. // Stats returns the whisper node statistics.
func (w *Whisper) Stats() string { func (w *Whisper) Stats() Statistics {
result := fmt.Sprintf("Memory usage: %d bytes. Average messages cleared per expiry cycle: %d. Total messages cleared: %d.", w.statsMu.Lock()
w.stats.memoryUsed, w.stats.totalMessagesCleared/w.stats.cycles, w.stats.totalMessagesCleared) defer w.statsMu.Unlock()
if w.stats.messagesCleared > 0 {
result += fmt.Sprintf(" Latest expiry cycle cleared %d messages (%d bytes).", return w.stats
w.stats.messagesCleared, w.stats.memoryCleared)
}
if w.overflow {
result += " Message queue state: overflow."
}
return result
} }
// Envelopes retrieves all the messages currently pooled by the node. // Envelopes retrieves all the messages currently pooled by the node.
...@@ -734,15 +783,6 @@ func (s *Statistics) reset() { ...@@ -734,15 +783,6 @@ func (s *Statistics) reset() {
s.messagesCleared = 0 s.messagesCleared = 0
} }
// ValidateKeyID checks the format of key id.
func ValidateKeyID(id string) error {
const target = keyIdSize * 2
if len(id) != target {
return fmt.Errorf("wrong size of key ID (expected %d bytes, got %d)", target, len(id))
}
return nil
}
// ValidatePublicKey checks the format of the given public key. // ValidatePublicKey checks the format of the given public key.
func ValidatePublicKey(k *ecdsa.PublicKey) bool { func ValidatePublicKey(k *ecdsa.PublicKey) bool {
return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0
......
...@@ -18,13 +18,14 @@ package whisperv5 ...@@ -18,13 +18,14 @@ package whisperv5
import ( import (
"bytes" "bytes"
"crypto/ecdsa"
mrand "math/rand" mrand "math/rand"
"testing" "testing"
"time" "time"
) )
func TestWhisperBasic(t *testing.T) { func TestWhisperBasic(t *testing.T) {
w := New() w := New(&DefaultConfig)
p := w.Protocols() p := w.Protocols()
shh := p[0] shh := p[0]
if shh.Name != ProtocolName { if shh.Name != ProtocolName {
...@@ -117,8 +118,39 @@ func TestWhisperBasic(t *testing.T) { ...@@ -117,8 +118,39 @@ func TestWhisperBasic(t *testing.T) {
} }
} }
func TestWhisperAsymmetricKeyImport(t *testing.T) {
var (
w = New(&DefaultConfig)
privateKeys []*ecdsa.PrivateKey
)
for i := 0; i < 50; i++ {
id, err := w.NewKeyPair()
if err != nil {
t.Fatalf("could not generate key: %v", err)
}
pk, err := w.GetPrivateKey(id)
if err != nil {
t.Fatalf("could not export private key: %v", err)
}
privateKeys = append(privateKeys, pk)
if !w.DeleteKeyPair(id) {
t.Fatalf("could not delete private key")
}
}
for _, pk := range privateKeys {
if _, err := w.AddKeyPair(pk); err != nil {
t.Fatalf("could not import private key: %v", err)
}
}
}
func TestWhisperIdentityManagement(t *testing.T) { func TestWhisperIdentityManagement(t *testing.T) {
w := New() w := New(&DefaultConfig)
id1, err := w.NewKeyPair() id1, err := w.NewKeyPair()
if err != nil { if err != nil {
t.Fatalf("failed to generate new key pair: %s.", err) t.Fatalf("failed to generate new key pair: %s.", err)
...@@ -240,7 +272,7 @@ func TestWhisperSymKeyManagement(t *testing.T) { ...@@ -240,7 +272,7 @@ func TestWhisperSymKeyManagement(t *testing.T) {
var err error var err error
var k1, k2 []byte var k1, k2 []byte
w := New() w := New(&DefaultConfig)
id1 := string("arbitrary-string-1") id1 := string("arbitrary-string-1")
id2 := string("arbitrary-string-2") id2 := string("arbitrary-string-2")
...@@ -443,7 +475,7 @@ func TestWhisperSymKeyManagement(t *testing.T) { ...@@ -443,7 +475,7 @@ func TestWhisperSymKeyManagement(t *testing.T) {
func TestExpiry(t *testing.T) { func TestExpiry(t *testing.T) {
InitSingleTest() InitSingleTest()
w := New() w := New(&DefaultConfig)
w.SetMinimumPoW(0.0000001) w.SetMinimumPoW(0.0000001)
defer w.SetMinimumPoW(DefaultMinimumPoW) defer w.SetMinimumPoW(DefaultMinimumPoW)
w.Start(nil) w.Start(nil)
...@@ -500,9 +532,9 @@ func TestExpiry(t *testing.T) { ...@@ -500,9 +532,9 @@ func TestExpiry(t *testing.T) {
func TestCustomization(t *testing.T) { func TestCustomization(t *testing.T) {
InitSingleTest() InitSingleTest()
w := New() w := New(&DefaultConfig)
defer w.SetMinimumPoW(DefaultMinimumPoW) defer w.SetMinimumPoW(DefaultMinimumPoW)
defer w.SetMaxMessageLength(DefaultMaxMessageLength) defer w.SetMaxMessageSize(DefaultMaxMessageSize)
w.Start(nil) w.Start(nil)
defer w.Stop() defer w.Stop()
...@@ -547,13 +579,13 @@ func TestCustomization(t *testing.T) { ...@@ -547,13 +579,13 @@ func TestCustomization(t *testing.T) {
if err != nil { if err != nil {
t.Fatalf("failed Wrap with seed %d: %s.", seed, err) t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
} }
w.SetMaxMessageLength(env.size() - 1) w.SetMaxMessageSize(uint32(env.size() - 1))
err = w.Send(env) err = w.Send(env)
if err == nil { if err == nil {
t.Fatalf("successfully sent oversized envelope (seed %d): false positive.", seed) t.Fatalf("successfully sent oversized envelope (seed %d): false positive.", seed)
} }
w.SetMaxMessageLength(DefaultMaxMessageLength) w.SetMaxMessageSize(DefaultMaxMessageSize)
err = w.Send(env) err = w.Send(env)
if err != nil { if err != nil {
t.Fatalf("failed to send second envelope with seed %d: %s.", seed, err) t.Fatalf("failed to send second envelope with seed %d: %s.", seed, err)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment