From 6918dc483b6ae5013f1d85f9b601cb560c608bc1 Mon Sep 17 00:00:00 2001 From: Raul Jordan Date: Mon, 2 Jul 2018 17:39:56 -0500 Subject: [PATCH] Eliminate More Unnecessary Dependencies: Whisper, Etc. (#229) sharding: eliminate unnecessary dependencies Former-commit-id: 0b6c06f979f1daa72557b79251f83a40356d6936 [formerly 22a70de8740e5a82a1db94070bbe7d8903f0a987] Former-commit-id: a5ea8ad58f2e3302f773a308b7584ef3f4e79e02 --- VERSION | 1 - cmd/utils/flags.go | 3 +- console/bridge.go | 358 - console/console.go | 442 -- console/console_test.go | 339 - console/prompter.go | 172 - console/testdata/exec.js | 1 - console/testdata/preload.js | 1 - containers/docker/develop-alpine/Dockerfile | 14 - containers/docker/develop-ubuntu/Dockerfile | 17 - containers/docker/master-alpine/Dockerfile | 14 - containers/docker/master-ubuntu/Dockerfile | 17 - dashboard/README.md | 58 - dashboard/assets.go.REMOVED.git-id | 1 - dashboard/assets/.eslintrc | 86 - dashboard/assets/.flowconfig | 9 - dashboard/assets/common.jsx | 71 - dashboard/assets/components/Body.jsx | 61 - dashboard/assets/components/ChartRow.jsx | 57 - dashboard/assets/components/CustomTooltip.jsx | 95 - dashboard/assets/components/Dashboard.jsx | 235 - dashboard/assets/components/Footer.jsx | 179 - dashboard/assets/components/Header.jsx | 73 - dashboard/assets/components/Main.jsx | 88 - dashboard/assets/components/SideBar.jsx | 116 - dashboard/assets/fa-only-woff-loader.js | 25 - dashboard/assets/index.html | 23 - dashboard/assets/index.jsx | 41 - dashboard/assets/package.json | 47 - dashboard/assets/types/content.jsx | 70 - dashboard/assets/webpack.config.js | 77 - dashboard/assets/yarn.lock | 6551 ----------------- dashboard/config.go | 41 - dashboard/cpu.go | 35 - dashboard/cpu_windows.go | 23 - dashboard/dashboard.go | 402 - dashboard/message.go | 72 - ethstats/ethstats.go | 717 -- les/api_backend.go | 199 - les/backend.go | 259 - les/bloombits.go | 74 - les/distributor.go | 285 - les/distributor_test.go | 185 - les/execqueue.go | 97 - les/execqueue_test.go | 62 - les/fetcher.go | 769 -- les/flowcontrol/control.go | 184 - les/flowcontrol/manager.go | 224 - les/handler.go | 1283 ---- les/handler_test.go | 578 -- les/helper_test.go | 328 - les/metrics.go | 111 - les/odr.go | 118 - les/odr_requests.go | 566 -- les/odr_test.go | 220 - les/peer.go | 660 -- les/protocol.go | 229 - les/randselect.go | 170 - les/randselect_test.go | 67 - les/request_test.go | 140 - les/retrieve.go | 402 - les/server.go | 383 - les/serverpool.go | 816 -- les/sync.go | 79 - les/txrelay.go | 175 - light/lightchain.go | 523 -- light/lightchain_test.go | 349 - light/nodeset.go | 148 - light/odr.go | 172 - light/odr_test.go | 312 - light/odr_util.go | 232 - light/postprocess.go | 307 - light/trie.go | 243 - light/trie_test.go | 82 - light/txpool.go | 530 -- light/txpool_test.go | 143 - mobile/accounts.go | 221 - mobile/android_test.go | 266 - mobile/big.go | 112 - mobile/bind.go | 191 - mobile/common.go | 230 - mobile/context.go | 80 - mobile/discover.go | 104 - mobile/doc.go | 61 - mobile/ethclient.go | 312 - mobile/ethereum.go | 146 - mobile/geth.go | 219 - mobile/geth_android.go | 22 - mobile/geth_ios.go | 22 - mobile/geth_other.go | 22 - mobile/init.go | 34 - mobile/interface.go | 148 - mobile/logger.go | 28 - mobile/p2p.go | 74 - mobile/params.go | 61 - mobile/primitives.go | 54 - mobile/types.go | 339 - mobile/vm.go | 56 - whisper/mailserver/mailserver.go | 195 - whisper/mailserver/server_test.go | 212 - whisper/shhclient/client.go | 193 - whisper/whisperv5/api.go | 561 -- whisper/whisperv5/benchmarks_test.go | 206 - whisper/whisperv5/config.go | 27 - whisper/whisperv5/doc.go | 87 - whisper/whisperv5/envelope.go | 246 - whisper/whisperv5/filter.go | 243 - whisper/whisperv5/filter_test.go | 848 --- whisper/whisperv5/gen_criteria_json.go | 64 - whisper/whisperv5/gen_message_json.go | 82 - whisper/whisperv5/gen_newmessage_json.go | 88 - whisper/whisperv5/message.go | 352 - whisper/whisperv5/message_test.go | 415 -- whisper/whisperv5/peer.go | 174 - whisper/whisperv5/peer_test.go | 298 - whisper/whisperv5/topic.go | 55 - whisper/whisperv5/topic_test.go | 134 - whisper/whisperv5/whisper.go | 855 --- whisper/whisperv5/whisper_test.go | 851 --- whisper/whisperv6/api.go | 593 -- whisper/whisperv6/api_test.go | 78 - whisper/whisperv6/benchmarks_test.go | 208 - whisper/whisperv6/config.go | 29 - whisper/whisperv6/doc.go | 97 - whisper/whisperv6/envelope.go | 279 - whisper/whisperv6/envelope_test.go | 64 - whisper/whisperv6/filter.go | 279 - whisper/whisperv6/filter_test.go | 867 --- whisper/whisperv6/gen_criteria_json.go | 66 - whisper/whisperv6/gen_message_json.go | 84 - whisper/whisperv6/gen_newmessage_json.go | 90 - whisper/whisperv6/message.go | 355 - whisper/whisperv6/message_test.go | 471 -- whisper/whisperv6/peer.go | 251 - whisper/whisperv6/peer_test.go | 509 -- whisper/whisperv6/topic.go | 57 - whisper/whisperv6/topic_test.go | 134 - whisper/whisperv6/whisper.go | 1050 --- whisper/whisperv6/whisper_test.go | 885 --- 139 files changed, 1 insertion(+), 37769 deletions(-) delete mode 100644 VERSION delete mode 100644 console/bridge.go delete mode 100644 console/console.go delete mode 100644 console/console_test.go delete mode 100644 console/prompter.go delete mode 100644 console/testdata/exec.js delete mode 100644 console/testdata/preload.js delete mode 100644 containers/docker/develop-alpine/Dockerfile delete mode 100644 containers/docker/develop-ubuntu/Dockerfile delete mode 100644 containers/docker/master-alpine/Dockerfile delete mode 100644 containers/docker/master-ubuntu/Dockerfile delete mode 100644 dashboard/README.md delete mode 100644 dashboard/assets.go.REMOVED.git-id delete mode 100644 dashboard/assets/.eslintrc delete mode 100644 dashboard/assets/.flowconfig delete mode 100644 dashboard/assets/common.jsx delete mode 100644 dashboard/assets/components/Body.jsx delete mode 100644 dashboard/assets/components/ChartRow.jsx delete mode 100644 dashboard/assets/components/CustomTooltip.jsx delete mode 100644 dashboard/assets/components/Dashboard.jsx delete mode 100644 dashboard/assets/components/Footer.jsx delete mode 100644 dashboard/assets/components/Header.jsx delete mode 100644 dashboard/assets/components/Main.jsx delete mode 100644 dashboard/assets/components/SideBar.jsx delete mode 100644 dashboard/assets/fa-only-woff-loader.js delete mode 100644 dashboard/assets/index.html delete mode 100644 dashboard/assets/index.jsx delete mode 100644 dashboard/assets/package.json delete mode 100644 dashboard/assets/types/content.jsx delete mode 100644 dashboard/assets/webpack.config.js delete mode 100644 dashboard/assets/yarn.lock delete mode 100644 dashboard/config.go delete mode 100644 dashboard/cpu.go delete mode 100644 dashboard/cpu_windows.go delete mode 100644 dashboard/dashboard.go delete mode 100644 dashboard/message.go delete mode 100644 ethstats/ethstats.go delete mode 100644 les/api_backend.go delete mode 100644 les/backend.go delete mode 100644 les/bloombits.go delete mode 100644 les/distributor.go delete mode 100644 les/distributor_test.go delete mode 100644 les/execqueue.go delete mode 100644 les/execqueue_test.go delete mode 100644 les/fetcher.go delete mode 100644 les/flowcontrol/control.go delete mode 100644 les/flowcontrol/manager.go delete mode 100644 les/handler.go delete mode 100644 les/handler_test.go delete mode 100644 les/helper_test.go delete mode 100644 les/metrics.go delete mode 100644 les/odr.go delete mode 100644 les/odr_requests.go delete mode 100644 les/odr_test.go delete mode 100644 les/peer.go delete mode 100644 les/protocol.go delete mode 100644 les/randselect.go delete mode 100644 les/randselect_test.go delete mode 100644 les/request_test.go delete mode 100644 les/retrieve.go delete mode 100644 les/server.go delete mode 100644 les/serverpool.go delete mode 100644 les/sync.go delete mode 100644 les/txrelay.go delete mode 100644 light/lightchain.go delete mode 100644 light/lightchain_test.go delete mode 100644 light/nodeset.go delete mode 100644 light/odr.go delete mode 100644 light/odr_test.go delete mode 100644 light/odr_util.go delete mode 100644 light/postprocess.go delete mode 100644 light/trie.go delete mode 100644 light/trie_test.go delete mode 100644 light/txpool.go delete mode 100644 light/txpool_test.go delete mode 100644 mobile/accounts.go delete mode 100644 mobile/android_test.go delete mode 100644 mobile/big.go delete mode 100644 mobile/bind.go delete mode 100644 mobile/common.go delete mode 100644 mobile/context.go delete mode 100644 mobile/discover.go delete mode 100644 mobile/doc.go delete mode 100644 mobile/ethclient.go delete mode 100644 mobile/ethereum.go delete mode 100644 mobile/geth.go delete mode 100644 mobile/geth_android.go delete mode 100644 mobile/geth_ios.go delete mode 100644 mobile/geth_other.go delete mode 100644 mobile/init.go delete mode 100644 mobile/interface.go delete mode 100644 mobile/logger.go delete mode 100644 mobile/p2p.go delete mode 100644 mobile/params.go delete mode 100644 mobile/primitives.go delete mode 100644 mobile/types.go delete mode 100644 mobile/vm.go delete mode 100644 whisper/mailserver/mailserver.go delete mode 100644 whisper/mailserver/server_test.go delete mode 100644 whisper/shhclient/client.go delete mode 100644 whisper/whisperv5/api.go delete mode 100644 whisper/whisperv5/benchmarks_test.go delete mode 100644 whisper/whisperv5/config.go delete mode 100644 whisper/whisperv5/doc.go delete mode 100644 whisper/whisperv5/envelope.go delete mode 100644 whisper/whisperv5/filter.go delete mode 100644 whisper/whisperv5/filter_test.go delete mode 100644 whisper/whisperv5/gen_criteria_json.go delete mode 100644 whisper/whisperv5/gen_message_json.go delete mode 100644 whisper/whisperv5/gen_newmessage_json.go delete mode 100644 whisper/whisperv5/message.go delete mode 100644 whisper/whisperv5/message_test.go delete mode 100644 whisper/whisperv5/peer.go delete mode 100644 whisper/whisperv5/peer_test.go delete mode 100644 whisper/whisperv5/topic.go delete mode 100644 whisper/whisperv5/topic_test.go delete mode 100644 whisper/whisperv5/whisper.go delete mode 100644 whisper/whisperv5/whisper_test.go delete mode 100644 whisper/whisperv6/api.go delete mode 100644 whisper/whisperv6/api_test.go delete mode 100644 whisper/whisperv6/benchmarks_test.go delete mode 100644 whisper/whisperv6/config.go delete mode 100644 whisper/whisperv6/doc.go delete mode 100644 whisper/whisperv6/envelope.go delete mode 100644 whisper/whisperv6/envelope_test.go delete mode 100644 whisper/whisperv6/filter.go delete mode 100644 whisper/whisperv6/filter_test.go delete mode 100644 whisper/whisperv6/gen_criteria_json.go delete mode 100644 whisper/whisperv6/gen_message_json.go delete mode 100644 whisper/whisperv6/gen_newmessage_json.go delete mode 100644 whisper/whisperv6/message.go delete mode 100644 whisper/whisperv6/message_test.go delete mode 100644 whisper/whisperv6/peer.go delete mode 100644 whisper/whisperv6/peer_test.go delete mode 100644 whisper/whisperv6/topic.go delete mode 100644 whisper/whisperv6/topic_test.go delete mode 100644 whisper/whisperv6/whisper.go delete mode 100644 whisper/whisperv6/whisper_test.go diff --git a/VERSION b/VERSION deleted file mode 100644 index 7d2424c90b..0000000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.8.12 diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go index 2f02b9c464..a742b24ef5 100644 --- a/cmd/utils/flags.go +++ b/cmd/utils/flags.go @@ -22,7 +22,6 @@ import ( "os" "path/filepath" - "github.com/ethereum/go-ethereum/eth" "github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/params" shardparams "github.com/ethereum/go-ethereum/sharding/params" @@ -93,7 +92,7 @@ var ( NetworkIdFlag = cli.Uint64Flag{ Name: "networkid", Usage: "Network identifier (integer, 1=Frontier, 2=Morden (disused), 3=Ropsten, 4=Rinkeby)", - Value: eth.DefaultConfig.NetworkId, + Value: 1, } PasswordFileFlag = cli.StringFlag{ Name: "password", diff --git a/console/bridge.go b/console/bridge.go deleted file mode 100644 index b0b4d37985..0000000000 --- a/console/bridge.go +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package console - -import ( - "encoding/json" - "fmt" - "io" - "strings" - "time" - - "github.com/ethereum/go-ethereum/accounts/usbwallet" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rpc" - "github.com/robertkrimen/otto" -) - -// bridge is a collection of JavaScript utility methods to bride the .js runtime -// environment and the Go RPC connection backing the remote method calls. -type bridge struct { - client *rpc.Client // RPC client to execute Ethereum requests through - prompter UserPrompter // Input prompter to allow interactive user feedback - printer io.Writer // Output writer to serialize any display strings to -} - -// newBridge creates a new JavaScript wrapper around an RPC client. -func newBridge(client *rpc.Client, prompter UserPrompter, printer io.Writer) *bridge { - return &bridge{ - client: client, - prompter: prompter, - printer: printer, - } -} - -// NewAccount is a wrapper around the personal.newAccount RPC method that uses a -// non-echoing password prompt to acquire the passphrase and executes the original -// RPC method (saved in jeth.newAccount) with it to actually execute the RPC call. -func (b *bridge) NewAccount(call otto.FunctionCall) (response otto.Value) { - var ( - password string - confirm string - err error - ) - switch { - // No password was specified, prompt the user for it - case len(call.ArgumentList) == 0: - if password, err = b.prompter.PromptPassword("Passphrase: "); err != nil { - throwJSException(err.Error()) - } - if confirm, err = b.prompter.PromptPassword("Repeat passphrase: "); err != nil { - throwJSException(err.Error()) - } - if password != confirm { - throwJSException("passphrases don't match!") - } - - // A single string password was specified, use that - case len(call.ArgumentList) == 1 && call.Argument(0).IsString(): - password, _ = call.Argument(0).ToString() - - // Otherwise fail with some error - default: - throwJSException("expected 0 or 1 string argument") - } - // Password acquired, execute the call and return - ret, err := call.Otto.Call("jeth.newAccount", nil, password) - if err != nil { - throwJSException(err.Error()) - } - return ret -} - -// OpenWallet is a wrapper around personal.openWallet which can interpret and -// react to certain error messages, such as the Trezor PIN matrix request. -func (b *bridge) OpenWallet(call otto.FunctionCall) (response otto.Value) { - // Make sure we have a wallet specified to open - if !call.Argument(0).IsString() { - throwJSException("first argument must be the wallet URL to open") - } - wallet := call.Argument(0) - - var passwd otto.Value - if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() { - passwd, _ = otto.ToValue("") - } else { - passwd = call.Argument(1) - } - // Open the wallet and return if successful in itself - val, err := call.Otto.Call("jeth.openWallet", nil, wallet, passwd) - if err == nil { - return val - } - // Wallet open failed, report error unless it's a PIN entry - if !strings.HasSuffix(err.Error(), usbwallet.ErrTrezorPINNeeded.Error()) { - throwJSException(err.Error()) - } - // Trezor PIN matrix input requested, display the matrix to the user and fetch the data - fmt.Fprintf(b.printer, "Look at the device for number positions\n\n") - fmt.Fprintf(b.printer, "7 | 8 | 9\n") - fmt.Fprintf(b.printer, "--+---+--\n") - fmt.Fprintf(b.printer, "4 | 5 | 6\n") - fmt.Fprintf(b.printer, "--+---+--\n") - fmt.Fprintf(b.printer, "1 | 2 | 3\n\n") - - if input, err := b.prompter.PromptPassword("Please enter current PIN: "); err != nil { - throwJSException(err.Error()) - } else { - passwd, _ = otto.ToValue(input) - } - if val, err = call.Otto.Call("jeth.openWallet", nil, wallet, passwd); err != nil { - throwJSException(err.Error()) - } - return val -} - -// UnlockAccount is a wrapper around the personal.unlockAccount RPC method that -// uses a non-echoing password prompt to acquire the passphrase and executes the -// original RPC method (saved in jeth.unlockAccount) with it to actually execute -// the RPC call. -func (b *bridge) UnlockAccount(call otto.FunctionCall) (response otto.Value) { - // Make sure we have an account specified to unlock - if !call.Argument(0).IsString() { - throwJSException("first argument must be the account to unlock") - } - account := call.Argument(0) - - // If password is not given or is the null value, prompt the user for it - var passwd otto.Value - - if call.Argument(1).IsUndefined() || call.Argument(1).IsNull() { - fmt.Fprintf(b.printer, "Unlock account %s\n", account) - if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil { - throwJSException(err.Error()) - } else { - passwd, _ = otto.ToValue(input) - } - } else { - if !call.Argument(1).IsString() { - throwJSException("password must be a string") - } - passwd = call.Argument(1) - } - // Third argument is the duration how long the account must be unlocked. - duration := otto.NullValue() - if call.Argument(2).IsDefined() && !call.Argument(2).IsNull() { - if !call.Argument(2).IsNumber() { - throwJSException("unlock duration must be a number") - } - duration = call.Argument(2) - } - // Send the request to the backend and return - val, err := call.Otto.Call("jeth.unlockAccount", nil, account, passwd, duration) - if err != nil { - throwJSException(err.Error()) - } - return val -} - -// Sign is a wrapper around the personal.sign RPC method that uses a non-echoing password -// prompt to acquire the passphrase and executes the original RPC method (saved in -// jeth.sign) with it to actually execute the RPC call. -func (b *bridge) Sign(call otto.FunctionCall) (response otto.Value) { - var ( - message = call.Argument(0) - account = call.Argument(1) - passwd = call.Argument(2) - ) - - if !message.IsString() { - throwJSException("first argument must be the message to sign") - } - if !account.IsString() { - throwJSException("second argument must be the account to sign with") - } - - // if the password is not given or null ask the user and ensure password is a string - if passwd.IsUndefined() || passwd.IsNull() { - fmt.Fprintf(b.printer, "Give password for account %s\n", account) - if input, err := b.prompter.PromptPassword("Passphrase: "); err != nil { - throwJSException(err.Error()) - } else { - passwd, _ = otto.ToValue(input) - } - } - if !passwd.IsString() { - throwJSException("third argument must be the password to unlock the account") - } - - // Send the request to the backend and return - val, err := call.Otto.Call("jeth.sign", nil, message, account, passwd) - if err != nil { - throwJSException(err.Error()) - } - return val -} - -// Sleep will block the console for the specified number of seconds. -func (b *bridge) Sleep(call otto.FunctionCall) (response otto.Value) { - if call.Argument(0).IsNumber() { - sleep, _ := call.Argument(0).ToInteger() - time.Sleep(time.Duration(sleep) * time.Second) - return otto.TrueValue() - } - return throwJSException("usage: sleep()") -} - -// SleepBlocks will block the console for a specified number of new blocks optionally -// until the given timeout is reached. -func (b *bridge) SleepBlocks(call otto.FunctionCall) (response otto.Value) { - var ( - blocks = int64(0) - sleep = int64(9999999999999999) // indefinitely - ) - // Parse the input parameters for the sleep - nArgs := len(call.ArgumentList) - if nArgs == 0 { - throwJSException("usage: sleepBlocks([, max sleep in seconds])") - } - if nArgs >= 1 { - if call.Argument(0).IsNumber() { - blocks, _ = call.Argument(0).ToInteger() - } else { - throwJSException("expected number as first argument") - } - } - if nArgs >= 2 { - if call.Argument(1).IsNumber() { - sleep, _ = call.Argument(1).ToInteger() - } else { - throwJSException("expected number as second argument") - } - } - // go through the console, this will allow web3 to call the appropriate - // callbacks if a delayed response or notification is received. - blockNumber := func() int64 { - result, err := call.Otto.Run("eth.blockNumber") - if err != nil { - throwJSException(err.Error()) - } - block, err := result.ToInteger() - if err != nil { - throwJSException(err.Error()) - } - return block - } - // Poll the current block number until either it ot a timeout is reached - targetBlockNr := blockNumber() + blocks - deadline := time.Now().Add(time.Duration(sleep) * time.Second) - - for time.Now().Before(deadline) { - if blockNumber() >= targetBlockNr { - return otto.TrueValue() - } - time.Sleep(time.Second) - } - return otto.FalseValue() -} - -type jsonrpcCall struct { - ID int64 - Method string - Params []interface{} -} - -// Send implements the web3 provider "send" method. -func (b *bridge) Send(call otto.FunctionCall) (response otto.Value) { - // Remarshal the request into a Go value. - JSON, _ := call.Otto.Object("JSON") - reqVal, err := JSON.Call("stringify", call.Argument(0)) - if err != nil { - throwJSException(err.Error()) - } - var ( - rawReq = reqVal.String() - dec = json.NewDecoder(strings.NewReader(rawReq)) - reqs []jsonrpcCall - batch bool - ) - dec.UseNumber() // avoid float64s - if rawReq[0] == '[' { - batch = true - dec.Decode(&reqs) - } else { - batch = false - reqs = make([]jsonrpcCall, 1) - dec.Decode(&reqs[0]) - } - - // Execute the requests. - resps, _ := call.Otto.Object("new Array()") - for _, req := range reqs { - resp, _ := call.Otto.Object(`({"jsonrpc":"2.0"})`) - resp.Set("id", req.ID) - var result json.RawMessage - err = b.client.Call(&result, req.Method, req.Params...) - switch err := err.(type) { - case nil: - if result == nil { - // Special case null because it is decoded as an empty - // raw message for some reason. - resp.Set("result", otto.NullValue()) - } else { - resultVal, err := JSON.Call("parse", string(result)) - if err != nil { - setError(resp, -32603, err.Error()) - } else { - resp.Set("result", resultVal) - } - } - case rpc.Error: - setError(resp, err.ErrorCode(), err.Error()) - default: - setError(resp, -32603, err.Error()) - } - resps.Call("push", resp) - } - - // Return the responses either to the callback (if supplied) - // or directly as the return value. - if batch { - response = resps.Value() - } else { - response, _ = resps.Get("0") - } - if fn := call.Argument(1); fn.Class() == "Function" { - fn.Call(otto.NullValue(), otto.NullValue(), response) - return otto.UndefinedValue() - } - return response -} - -func setError(resp *otto.Object, code int, msg string) { - resp.Set("error", map[string]interface{}{"code": code, "message": msg}) -} - -// throwJSException panics on an otto.Value. The Otto VM will recover from the -// Go panic and throw msg as a JavaScript error. -func throwJSException(msg interface{}) otto.Value { - val, err := otto.ToValue(msg) - if err != nil { - log.Error("Failed to serialize JavaScript exception", "exception", msg, "err", err) - } - panic(val) -} diff --git a/console/console.go b/console/console.go deleted file mode 100644 index 56e03837ac..0000000000 --- a/console/console.go +++ /dev/null @@ -1,442 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package console - -import ( - "fmt" - "io" - "io/ioutil" - "os" - "os/signal" - "path/filepath" - "regexp" - "sort" - "strings" - "syscall" - - "github.com/ethereum/go-ethereum/internal/jsre" - "github.com/ethereum/go-ethereum/internal/web3ext" - "github.com/ethereum/go-ethereum/rpc" - "github.com/mattn/go-colorable" - "github.com/peterh/liner" - "github.com/robertkrimen/otto" -) - -var ( - passwordRegexp = regexp.MustCompile(`personal.[nus]`) - onlyWhitespace = regexp.MustCompile(`^\s*$`) - exit = regexp.MustCompile(`^\s*exit\s*;*\s*$`) -) - -// HistoryFile is the file within the data directory to store input scrollback. -const HistoryFile = "history" - -// DefaultPrompt is the default prompt line prefix to use for user input querying. -const DefaultPrompt = "> " - -// Config is the collection of configurations to fine tune the behavior of the -// JavaScript console. -type Config struct { - DataDir string // Data directory to store the console history at - DocRoot string // Filesystem path from where to load JavaScript files from - Client *rpc.Client // RPC client to execute Ethereum requests through - Prompt string // Input prompt prefix string (defaults to DefaultPrompt) - Prompter UserPrompter // Input prompter to allow interactive user feedback (defaults to TerminalPrompter) - Printer io.Writer // Output writer to serialize any display strings to (defaults to os.Stdout) - Preload []string // Absolute paths to JavaScript files to preload -} - -// Console is a JavaScript interpreted runtime environment. It is a fully fledged -// JavaScript console attached to a running node via an external or in-process RPC -// client. -type Console struct { - client *rpc.Client // RPC client to execute Ethereum requests through - jsre *jsre.JSRE // JavaScript runtime environment running the interpreter - prompt string // Input prompt prefix string - prompter UserPrompter // Input prompter to allow interactive user feedback - histPath string // Absolute path to the console scrollback history - history []string // Scroll history maintained by the console - printer io.Writer // Output writer to serialize any display strings to -} - -// New initializes a JavaScript interpreted runtime environment and sets defaults -// with the config struct. -func New(config Config) (*Console, error) { - // Handle unset config values gracefully - if config.Prompter == nil { - config.Prompter = Stdin - } - if config.Prompt == "" { - config.Prompt = DefaultPrompt - } - if config.Printer == nil { - config.Printer = colorable.NewColorableStdout() - } - // Initialize the console and return - console := &Console{ - client: config.Client, - jsre: jsre.New(config.DocRoot, config.Printer), - prompt: config.Prompt, - prompter: config.Prompter, - printer: config.Printer, - histPath: filepath.Join(config.DataDir, HistoryFile), - } - if err := os.MkdirAll(config.DataDir, 0700); err != nil { - return nil, err - } - if err := console.init(config.Preload); err != nil { - return nil, err - } - return console, nil -} - -// init retrieves the available APIs from the remote RPC provider and initializes -// the console's JavaScript namespaces based on the exposed modules. -func (c *Console) init(preload []string) error { - // Initialize the JavaScript <-> Go RPC bridge - bridge := newBridge(c.client, c.prompter, c.printer) - c.jsre.Set("jeth", struct{}{}) - - jethObj, _ := c.jsre.Get("jeth") - jethObj.Object().Set("send", bridge.Send) - jethObj.Object().Set("sendAsync", bridge.Send) - - consoleObj, _ := c.jsre.Get("console") - consoleObj.Object().Set("log", c.consoleOutput) - consoleObj.Object().Set("error", c.consoleOutput) - - // Load all the internal utility JavaScript libraries - if err := c.jsre.Compile("bignumber.js", jsre.BigNumber_JS); err != nil { - return fmt.Errorf("bignumber.js: %v", err) - } - if err := c.jsre.Compile("web3.js", jsre.Web3_JS); err != nil { - return fmt.Errorf("web3.js: %v", err) - } - if _, err := c.jsre.Run("var Web3 = require('web3');"); err != nil { - return fmt.Errorf("web3 require: %v", err) - } - if _, err := c.jsre.Run("var web3 = new Web3(jeth);"); err != nil { - return fmt.Errorf("web3 provider: %v", err) - } - // Load the supported APIs into the JavaScript runtime environment - apis, err := c.client.SupportedModules() - if err != nil { - return fmt.Errorf("api modules: %v", err) - } - flatten := "var eth = web3.eth; var personal = web3.personal; " - for api := range apis { - if api == "web3" { - continue // manually mapped or ignore - } - if file, ok := web3ext.Modules[api]; ok { - // Load our extension for the module. - if err = c.jsre.Compile(fmt.Sprintf("%s.js", api), file); err != nil { - return fmt.Errorf("%s.js: %v", api, err) - } - flatten += fmt.Sprintf("var %s = web3.%s; ", api, api) - } else if obj, err := c.jsre.Run("web3." + api); err == nil && obj.IsObject() { - // Enable web3.js built-in extension if available. - flatten += fmt.Sprintf("var %s = web3.%s; ", api, api) - } - } - if _, err = c.jsre.Run(flatten); err != nil { - return fmt.Errorf("namespace flattening: %v", err) - } - // Initialize the global name register (disabled for now) - //c.jsre.Run(`var GlobalRegistrar = eth.contract(` + registrar.GlobalRegistrarAbi + `); registrar = GlobalRegistrar.at("` + registrar.GlobalRegistrarAddr + `");`) - - // If the console is in interactive mode, instrument password related methods to query the user - if c.prompter != nil { - // Retrieve the account management object to instrument - personal, err := c.jsre.Get("personal") - if err != nil { - return err - } - // Override the openWallet, unlockAccount, newAccount and sign methods since - // these require user interaction. Assign these method in the Console the - // original web3 callbacks. These will be called by the jeth.* methods after - // they got the password from the user and send the original web3 request to - // the backend. - if obj := personal.Object(); obj != nil { // make sure the personal api is enabled over the interface - if _, err = c.jsre.Run(`jeth.openWallet = personal.openWallet;`); err != nil { - return fmt.Errorf("personal.openWallet: %v", err) - } - if _, err = c.jsre.Run(`jeth.unlockAccount = personal.unlockAccount;`); err != nil { - return fmt.Errorf("personal.unlockAccount: %v", err) - } - if _, err = c.jsre.Run(`jeth.newAccount = personal.newAccount;`); err != nil { - return fmt.Errorf("personal.newAccount: %v", err) - } - if _, err = c.jsre.Run(`jeth.sign = personal.sign;`); err != nil { - return fmt.Errorf("personal.sign: %v", err) - } - obj.Set("openWallet", bridge.OpenWallet) - obj.Set("unlockAccount", bridge.UnlockAccount) - obj.Set("newAccount", bridge.NewAccount) - obj.Set("sign", bridge.Sign) - } - } - // The admin.sleep and admin.sleepBlocks are offered by the console and not by the RPC layer. - admin, err := c.jsre.Get("admin") - if err != nil { - return err - } - if obj := admin.Object(); obj != nil { // make sure the admin api is enabled over the interface - obj.Set("sleepBlocks", bridge.SleepBlocks) - obj.Set("sleep", bridge.Sleep) - obj.Set("clearHistory", c.clearHistory) - } - // Preload any JavaScript files before starting the console - for _, path := range preload { - if err := c.jsre.Exec(path); err != nil { - failure := err.Error() - if ottoErr, ok := err.(*otto.Error); ok { - failure = ottoErr.String() - } - return fmt.Errorf("%s: %v", path, failure) - } - } - // Configure the console's input prompter for scrollback and tab completion - if c.prompter != nil { - if content, err := ioutil.ReadFile(c.histPath); err != nil { - c.prompter.SetHistory(nil) - } else { - c.history = strings.Split(string(content), "\n") - c.prompter.SetHistory(c.history) - } - c.prompter.SetWordCompleter(c.AutoCompleteInput) - } - return nil -} - -func (c *Console) clearHistory() { - c.history = nil - c.prompter.ClearHistory() - if err := os.Remove(c.histPath); err != nil { - fmt.Fprintln(c.printer, "can't delete history file:", err) - } else { - fmt.Fprintln(c.printer, "history file deleted") - } -} - -// consoleOutput is an override for the console.log and console.error methods to -// stream the output into the configured output stream instead of stdout. -func (c *Console) consoleOutput(call otto.FunctionCall) otto.Value { - output := []string{} - for _, argument := range call.ArgumentList { - output = append(output, fmt.Sprintf("%v", argument)) - } - fmt.Fprintln(c.printer, strings.Join(output, " ")) - return otto.Value{} -} - -// AutoCompleteInput is a pre-assembled word completer to be used by the user -// input prompter to provide hints to the user about the methods available. -func (c *Console) AutoCompleteInput(line string, pos int) (string, []string, string) { - // No completions can be provided for empty inputs - if len(line) == 0 || pos == 0 { - return "", nil, "" - } - // Chunck data to relevant part for autocompletion - // E.g. in case of nested lines eth.getBalance(eth.coinb - start := pos - 1 - for ; start > 0; start-- { - // Skip all methods and namespaces (i.e. including the dot) - if line[start] == '.' || (line[start] >= 'a' && line[start] <= 'z') || (line[start] >= 'A' && line[start] <= 'Z') { - continue - } - // Handle web3 in a special way (i.e. other numbers aren't auto completed) - if start >= 3 && line[start-3:start] == "web3" { - start -= 3 - continue - } - // We've hit an unexpected character, autocomplete form here - start++ - break - } - return line[:start], c.jsre.CompleteKeywords(line[start:pos]), line[pos:] -} - -// Welcome show summary of current Geth instance and some metadata about the -// console's available modules. -func (c *Console) Welcome() { - // Print some generic Geth metadata - fmt.Fprintf(c.printer, "Welcome to the Geth JavaScript console!\n\n") - c.jsre.Run(` - console.log("instance: " + web3.version.node); - console.log("coinbase: " + eth.coinbase); - console.log("at block: " + eth.blockNumber + " (" + new Date(1000 * eth.getBlock(eth.blockNumber).timestamp) + ")"); - console.log(" datadir: " + admin.datadir); - `) - // List all the supported modules for the user to call - if apis, err := c.client.SupportedModules(); err == nil { - modules := make([]string, 0, len(apis)) - for api, version := range apis { - modules = append(modules, fmt.Sprintf("%s:%s", api, version)) - } - sort.Strings(modules) - fmt.Fprintln(c.printer, " modules:", strings.Join(modules, " ")) - } - fmt.Fprintln(c.printer) -} - -// Evaluate executes code and pretty prints the result to the specified output -// stream. -func (c *Console) Evaluate(statement string) error { - defer func() { - if r := recover(); r != nil { - fmt.Fprintf(c.printer, "[native] error: %v\n", r) - } - }() - return c.jsre.Evaluate(statement, c.printer) -} - -// Interactive starts an interactive user session, where input is propted from -// the configured user prompter. -func (c *Console) Interactive() { - var ( - prompt = c.prompt // Current prompt line (used for multi-line inputs) - indents = 0 // Current number of input indents (used for multi-line inputs) - input = "" // Current user input - scheduler = make(chan string) // Channel to send the next prompt on and receive the input - ) - // Start a goroutine to listen for promt requests and send back inputs - go func() { - for { - // Read the next user input - line, err := c.prompter.PromptInput(<-scheduler) - if err != nil { - // In case of an error, either clear the prompt or fail - if err == liner.ErrPromptAborted { // ctrl-C - prompt, indents, input = c.prompt, 0, "" - scheduler <- "" - continue - } - close(scheduler) - return - } - // User input retrieved, send for interpretation and loop - scheduler <- line - } - }() - // Monitor Ctrl-C too in case the input is empty and we need to bail - abort := make(chan os.Signal, 1) - signal.Notify(abort, syscall.SIGINT, syscall.SIGTERM) - - // Start sending prompts to the user and reading back inputs - for { - // Send the next prompt, triggering an input read and process the result - scheduler <- prompt - select { - case <-abort: - // User forcefully quite the console - fmt.Fprintln(c.printer, "caught interrupt, exiting") - return - - case line, ok := <-scheduler: - // User input was returned by the prompter, handle special cases - if !ok || (indents <= 0 && exit.MatchString(line)) { - return - } - if onlyWhitespace.MatchString(line) { - continue - } - // Append the line to the input and check for multi-line interpretation - input += line + "\n" - - indents = countIndents(input) - if indents <= 0 { - prompt = c.prompt - } else { - prompt = strings.Repeat(".", indents*3) + " " - } - // If all the needed lines are present, save the command and run - if indents <= 0 { - if len(input) > 0 && input[0] != ' ' && !passwordRegexp.MatchString(input) { - if command := strings.TrimSpace(input); len(c.history) == 0 || command != c.history[len(c.history)-1] { - c.history = append(c.history, command) - if c.prompter != nil { - c.prompter.AppendHistory(command) - } - } - } - c.Evaluate(input) - input = "" - } - } - } -} - -// countIndents returns the number of identations for the given input. -// In case of invalid input such as var a = } the result can be negative. -func countIndents(input string) int { - var ( - indents = 0 - inString = false - strOpenChar = ' ' // keep track of the string open char to allow var str = "I'm ...."; - charEscaped = false // keep track if the previous char was the '\' char, allow var str = "abc\"def"; - ) - - for _, c := range input { - switch c { - case '\\': - // indicate next char as escaped when in string and previous char isn't escaping this backslash - if !charEscaped && inString { - charEscaped = true - } - case '\'', '"': - if inString && !charEscaped && strOpenChar == c { // end string - inString = false - } else if !inString && !charEscaped { // begin string - inString = true - strOpenChar = c - } - charEscaped = false - case '{', '(': - if !inString { // ignore brackets when in string, allow var str = "a{"; without indenting - indents++ - } - charEscaped = false - case '}', ')': - if !inString { - indents-- - } - charEscaped = false - default: - charEscaped = false - } - } - - return indents -} - -// Execute runs the JavaScript file specified as the argument. -func (c *Console) Execute(path string) error { - return c.jsre.Exec(path) -} - -// Stop cleans up the console and terminates the runtime environment. -func (c *Console) Stop(graceful bool) error { - if err := ioutil.WriteFile(c.histPath, []byte(strings.Join(c.history, "\n")), 0600); err != nil { - return err - } - if err := os.Chmod(c.histPath, 0600); err != nil { // Force 0600, even if it was different previously - return err - } - c.jsre.Stop(graceful) - return nil -} diff --git a/console/console_test.go b/console/console_test.go deleted file mode 100644 index 7b1629c032..0000000000 --- a/console/console_test.go +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright 2015 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 . - -package console - -import ( - "bytes" - "errors" - "fmt" - "io/ioutil" - "os" - "strings" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/internal/jsre" - "github.com/ethereum/go-ethereum/node" -) - -const ( - testInstance = "console-tester" - testAddress = "0x8605cdbbdb6d264aa742e77020dcbc58fcdce182" -) - -// hookedPrompter implements UserPrompter to simulate use input via channels. -type hookedPrompter struct { - scheduler chan string -} - -func (p *hookedPrompter) PromptInput(prompt string) (string, error) { - // Send the prompt to the tester - select { - case p.scheduler <- prompt: - case <-time.After(time.Second): - return "", errors.New("prompt timeout") - } - // Retrieve the response and feed to the console - select { - case input := <-p.scheduler: - return input, nil - case <-time.After(time.Second): - return "", errors.New("input timeout") - } -} - -func (p *hookedPrompter) PromptPassword(prompt string) (string, error) { - return "", errors.New("not implemented") -} -func (p *hookedPrompter) PromptConfirm(prompt string) (bool, error) { - return false, errors.New("not implemented") -} -func (p *hookedPrompter) SetHistory(history []string) {} -func (p *hookedPrompter) AppendHistory(command string) {} -func (p *hookedPrompter) ClearHistory() {} -func (p *hookedPrompter) SetWordCompleter(completer WordCompleter) {} - -// tester is a console test environment for the console tests to operate on. -type tester struct { - workspace string - stack *node.Node - ethereum *eth.Ethereum - console *Console - input *hookedPrompter - output *bytes.Buffer -} - -// newTester creates a test environment based on which the console can operate. -// Please ensure you call Close() on the returned tester to avoid leaks. -func newTester(t *testing.T, confOverride func(*eth.Config)) *tester { - // Create a temporary storage for the node keys and initialize it - workspace, err := ioutil.TempDir("", "console-tester-") - if err != nil { - t.Fatalf("failed to create temporary keystore: %v", err) - } - - // Create a networkless protocol stack and start an Ethereum service within - stack, err := node.New(&node.Config{DataDir: workspace, UseLightweightKDF: true, Name: testInstance}) - if err != nil { - t.Fatalf("failed to create node: %v", err) - } - ethConf := ð.Config{ - Genesis: core.DeveloperGenesisBlock(15, common.Address{}), - Etherbase: common.HexToAddress(testAddress), - Ethash: ethash.Config{ - PowMode: ethash.ModeTest, - }, - } - if confOverride != nil { - confOverride(ethConf) - } - if err = stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil { - t.Fatalf("failed to register Ethereum protocol: %v", err) - } - // Start the node and assemble the JavaScript console around it - if err = stack.Start(); err != nil { - t.Fatalf("failed to start test stack: %v", err) - } - client, err := stack.Attach() - if err != nil { - t.Fatalf("failed to attach to node: %v", err) - } - prompter := &hookedPrompter{scheduler: make(chan string)} - printer := new(bytes.Buffer) - - console, err := New(Config{ - DataDir: stack.DataDir(), - DocRoot: "testdata", - Client: client, - Prompter: prompter, - Printer: printer, - Preload: []string{"preload.js"}, - }) - if err != nil { - t.Fatalf("failed to create JavaScript console: %v", err) - } - // Create the final tester and return - var ethereum *eth.Ethereum - stack.Service(ðereum) - - return &tester{ - workspace: workspace, - stack: stack, - ethereum: ethereum, - console: console, - input: prompter, - output: printer, - } -} - -// Close cleans up any temporary data folders and held resources. -func (env *tester) Close(t *testing.T) { - if err := env.console.Stop(false); err != nil { - t.Errorf("failed to stop embedded console: %v", err) - } - if err := env.stack.Stop(); err != nil { - t.Errorf("failed to stop embedded node: %v", err) - } - os.RemoveAll(env.workspace) -} - -// Tests that the node lists the correct welcome message, notably that it contains -// the instance name, coinbase account, block number, data directory and supported -// console modules. -func TestWelcome(t *testing.T) { - tester := newTester(t, nil) - defer tester.Close(t) - - tester.console.Welcome() - - output := tester.output.String() - if want := "Welcome"; !strings.Contains(output, want) { - t.Fatalf("console output missing welcome message: have\n%s\nwant also %s", output, want) - } - if want := fmt.Sprintf("instance: %s", testInstance); !strings.Contains(output, want) { - t.Fatalf("console output missing instance: have\n%s\nwant also %s", output, want) - } - if want := fmt.Sprintf("coinbase: %s", testAddress); !strings.Contains(output, want) { - t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want) - } - if want := "at block: 0"; !strings.Contains(output, want) { - t.Fatalf("console output missing sync status: have\n%s\nwant also %s", output, want) - } - if want := fmt.Sprintf("datadir: %s", tester.workspace); !strings.Contains(output, want) { - t.Fatalf("console output missing coinbase: have\n%s\nwant also %s", output, want) - } -} - -// Tests that JavaScript statement evaluation works as intended. -func TestEvaluate(t *testing.T) { - tester := newTester(t, nil) - defer tester.Close(t) - - tester.console.Evaluate("2 + 2") - if output := tester.output.String(); !strings.Contains(output, "4") { - t.Fatalf("statement evaluation failed: have %s, want %s", output, "4") - } -} - -// Tests that the console can be used in interactive mode. -func TestInteractive(t *testing.T) { - // Create a tester and run an interactive console in the background - tester := newTester(t, nil) - defer tester.Close(t) - - go tester.console.Interactive() - - // Wait for a promt and send a statement back - select { - case <-tester.input.scheduler: - case <-time.After(time.Second): - t.Fatalf("initial prompt timeout") - } - select { - case tester.input.scheduler <- "2+2": - case <-time.After(time.Second): - t.Fatalf("input feedback timeout") - } - // Wait for the second promt and ensure first statement was evaluated - select { - case <-tester.input.scheduler: - case <-time.After(time.Second): - t.Fatalf("secondary prompt timeout") - } - if output := tester.output.String(); !strings.Contains(output, "4") { - t.Fatalf("statement evaluation failed: have %s, want %s", output, "4") - } -} - -// Tests that preloaded JavaScript files have been executed before user is given -// input. -func TestPreload(t *testing.T) { - tester := newTester(t, nil) - defer tester.Close(t) - - tester.console.Evaluate("preloaded") - if output := tester.output.String(); !strings.Contains(output, "some-preloaded-string") { - t.Fatalf("preloaded variable missing: have %s, want %s", output, "some-preloaded-string") - } -} - -// Tests that JavaScript scripts can be executes from the configured asset path. -func TestExecute(t *testing.T) { - tester := newTester(t, nil) - defer tester.Close(t) - - tester.console.Execute("exec.js") - - tester.console.Evaluate("execed") - if output := tester.output.String(); !strings.Contains(output, "some-executed-string") { - t.Fatalf("execed variable missing: have %s, want %s", output, "some-executed-string") - } -} - -// Tests that the JavaScript objects returned by statement executions are properly -// pretty printed instead of just displaing "[object]". -func TestPrettyPrint(t *testing.T) { - tester := newTester(t, nil) - defer tester.Close(t) - - tester.console.Evaluate("obj = {int: 1, string: 'two', list: [3, 3, 3], obj: {null: null, func: function(){}}}") - - // Define some specially formatted fields - var ( - one = jsre.NumberColor("1") - two = jsre.StringColor("\"two\"") - three = jsre.NumberColor("3") - null = jsre.SpecialColor("null") - fun = jsre.FunctionColor("function()") - ) - // Assemble the actual output we're after and verify - want := `{ - int: ` + one + `, - list: [` + three + `, ` + three + `, ` + three + `], - obj: { - null: ` + null + `, - func: ` + fun + ` - }, - string: ` + two + ` -} -` - if output := tester.output.String(); output != want { - t.Fatalf("pretty print mismatch: have %s, want %s", output, want) - } -} - -// Tests that the JavaScript exceptions are properly formatted and colored. -func TestPrettyError(t *testing.T) { - tester := newTester(t, nil) - defer tester.Close(t) - tester.console.Evaluate("throw 'hello'") - - want := jsre.ErrorColor("hello") + "\n" - if output := tester.output.String(); output != want { - t.Fatalf("pretty error mismatch: have %s, want %s", output, want) - } -} - -// Tests that tests if the number of indents for JS input is calculated correct. -func TestIndenting(t *testing.T) { - testCases := []struct { - input string - expectedIndentCount int - }{ - {`var a = 1;`, 0}, - {`"some string"`, 0}, - {`"some string with (parentesis`, 0}, - {`"some string with newline - ("`, 0}, - {`function v(a,b) {}`, 0}, - {`function f(a,b) { var str = "asd("; };`, 0}, - {`function f(a) {`, 1}, - {`function f(a, function(b) {`, 2}, - {`function f(a, function(b) { - var str = "a)}"; - });`, 0}, - {`function f(a,b) { - var str = "a{b(" + a, ", " + b; - }`, 0}, - {`var str = "\"{"`, 0}, - {`var str = "'("`, 0}, - {`var str = "\\{"`, 0}, - {`var str = "\\\\{"`, 0}, - {`var str = 'a"{`, 0}, - {`var obj = {`, 1}, - {`var obj = { {a:1`, 2}, - {`var obj = { {a:1}`, 1}, - {`var obj = { {a:1}, b:2}`, 0}, - {`var obj = {}`, 0}, - {`var obj = { - a: 1, b: 2 - }`, 0}, - {`var test = }`, -1}, - {`var str = "a\""; var obj = {`, 1}, - } - - for i, tt := range testCases { - counted := countIndents(tt.input) - if counted != tt.expectedIndentCount { - t.Errorf("test %d: invalid indenting: have %d, want %d", i, counted, tt.expectedIndentCount) - } - } -} diff --git a/console/prompter.go b/console/prompter.go deleted file mode 100644 index c477b48178..0000000000 --- a/console/prompter.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package console - -import ( - "fmt" - "strings" - - "github.com/peterh/liner" -) - -// Stdin holds the stdin line reader (also using stdout for printing prompts). -// Only this reader may be used for input because it keeps an internal buffer. -var Stdin = newTerminalPrompter() - -// UserPrompter defines the methods needed by the console to promt the user for -// various types of inputs. -type UserPrompter interface { - // PromptInput displays the given prompt to the user and requests some textual - // data to be entered, returning the input of the user. - PromptInput(prompt string) (string, error) - - // PromptPassword displays the given prompt to the user and requests some textual - // data to be entered, but one which must not be echoed out into the terminal. - // The method returns the input provided by the user. - PromptPassword(prompt string) (string, error) - - // PromptConfirm displays the given prompt to the user and requests a boolean - // choice to be made, returning that choice. - PromptConfirm(prompt string) (bool, error) - - // SetHistory sets the the input scrollback history that the prompter will allow - // the user to scroll back to. - SetHistory(history []string) - - // AppendHistory appends an entry to the scrollback history. It should be called - // if and only if the prompt to append was a valid command. - AppendHistory(command string) - - // ClearHistory clears the entire history - ClearHistory() - - // SetWordCompleter sets the completion function that the prompter will call to - // fetch completion candidates when the user presses tab. - SetWordCompleter(completer WordCompleter) -} - -// WordCompleter takes the currently edited line with the cursor position and -// returns the completion candidates for the partial word to be completed. If -// the line is "Hello, wo!!!" and the cursor is before the first '!', ("Hello, -// wo!!!", 9) is passed to the completer which may returns ("Hello, ", {"world", -// "Word"}, "!!!") to have "Hello, world!!!". -type WordCompleter func(line string, pos int) (string, []string, string) - -// terminalPrompter is a UserPrompter backed by the liner package. It supports -// prompting the user for various input, among others for non-echoing password -// input. -type terminalPrompter struct { - *liner.State - warned bool - supported bool - normalMode liner.ModeApplier - rawMode liner.ModeApplier -} - -// newTerminalPrompter creates a liner based user input prompter working off the -// standard input and output streams. -func newTerminalPrompter() *terminalPrompter { - p := new(terminalPrompter) - // Get the original mode before calling NewLiner. - // This is usually regular "cooked" mode where characters echo. - normalMode, _ := liner.TerminalMode() - // Turn on liner. It switches to raw mode. - p.State = liner.NewLiner() - rawMode, err := liner.TerminalMode() - if err != nil || !liner.TerminalSupported() { - p.supported = false - } else { - p.supported = true - p.normalMode = normalMode - p.rawMode = rawMode - // Switch back to normal mode while we're not prompting. - normalMode.ApplyMode() - } - p.SetCtrlCAborts(true) - p.SetTabCompletionStyle(liner.TabPrints) - p.SetMultiLineMode(true) - return p -} - -// PromptInput displays the given prompt to the user and requests some textual -// data to be entered, returning the input of the user. -func (p *terminalPrompter) PromptInput(prompt string) (string, error) { - if p.supported { - p.rawMode.ApplyMode() - defer p.normalMode.ApplyMode() - } else { - // liner tries to be smart about printing the prompt - // and doesn't print anything if input is redirected. - // Un-smart it by printing the prompt always. - fmt.Print(prompt) - prompt = "" - defer fmt.Println() - } - return p.State.Prompt(prompt) -} - -// PromptPassword displays the given prompt to the user and requests some textual -// data to be entered, but one which must not be echoed out into the terminal. -// The method returns the input provided by the user. -func (p *terminalPrompter) PromptPassword(prompt string) (passwd string, err error) { - if p.supported { - p.rawMode.ApplyMode() - defer p.normalMode.ApplyMode() - return p.State.PasswordPrompt(prompt) - } - if !p.warned { - fmt.Println("!! Unsupported terminal, password will be echoed.") - p.warned = true - } - // Just as in Prompt, handle printing the prompt here instead of relying on liner. - fmt.Print(prompt) - passwd, err = p.State.Prompt("") - fmt.Println() - return passwd, err -} - -// PromptConfirm displays the given prompt to the user and requests a boolean -// choice to be made, returning that choice. -func (p *terminalPrompter) PromptConfirm(prompt string) (bool, error) { - input, err := p.Prompt(prompt + " [y/N] ") - if len(input) > 0 && strings.ToUpper(input[:1]) == "Y" { - return true, nil - } - return false, err -} - -// SetHistory sets the the input scrollback history that the prompter will allow -// the user to scroll back to. -func (p *terminalPrompter) SetHistory(history []string) { - p.State.ReadHistory(strings.NewReader(strings.Join(history, "\n"))) -} - -// AppendHistory appends an entry to the scrollback history. -func (p *terminalPrompter) AppendHistory(command string) { - p.State.AppendHistory(command) -} - -// ClearHistory clears the entire history -func (p *terminalPrompter) ClearHistory() { - p.State.ClearHistory() -} - -// SetWordCompleter sets the completion function that the prompter will call to -// fetch completion candidates when the user presses tab. -func (p *terminalPrompter) SetWordCompleter(completer WordCompleter) { - p.State.SetWordCompleter(liner.WordCompleter(completer)) -} diff --git a/console/testdata/exec.js b/console/testdata/exec.js deleted file mode 100644 index 59e34d7c40..0000000000 --- a/console/testdata/exec.js +++ /dev/null @@ -1 +0,0 @@ -var execed = "some-executed-string"; diff --git a/console/testdata/preload.js b/console/testdata/preload.js deleted file mode 100644 index 556793970f..0000000000 --- a/console/testdata/preload.js +++ /dev/null @@ -1 +0,0 @@ -var preloaded = "some-preloaded-string"; diff --git a/containers/docker/develop-alpine/Dockerfile b/containers/docker/develop-alpine/Dockerfile deleted file mode 100644 index 1f3e1112ec..0000000000 --- a/containers/docker/develop-alpine/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM alpine:3.7 - -RUN \ - apk add --update go git make gcc musl-dev linux-headers ca-certificates && \ - git clone --depth 1 https://github.com/ethereum/go-ethereum && \ - (cd go-ethereum && make geth) && \ - cp go-ethereum/build/bin/geth /geth && \ - apk del go git make gcc musl-dev linux-headers && \ - rm -rf /go-ethereum && rm -rf /var/cache/apk/* - -EXPOSE 8545 -EXPOSE 30303 - -ENTRYPOINT ["/geth"] diff --git a/containers/docker/develop-ubuntu/Dockerfile b/containers/docker/develop-ubuntu/Dockerfile deleted file mode 100644 index 8c4fe9f595..0000000000 --- a/containers/docker/develop-ubuntu/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM ubuntu:xenial - -ENV PATH=/usr/lib/go-1.9/bin:$PATH - -RUN \ - apt-get update && apt-get upgrade -q -y && \ - apt-get install -y --no-install-recommends golang-1.9 git make gcc libc-dev ca-certificates && \ - git clone --depth 1 https://github.com/ethereum/go-ethereum && \ - (cd go-ethereum && make geth) && \ - cp go-ethereum/build/bin/geth /geth && \ - apt-get remove -y golang-1.9 git make gcc libc-dev && apt autoremove -y && apt-get clean && \ - rm -rf /go-ethereum - -EXPOSE 8545 -EXPOSE 30303 - -ENTRYPOINT ["/geth"] diff --git a/containers/docker/master-alpine/Dockerfile b/containers/docker/master-alpine/Dockerfile deleted file mode 100644 index 8d4e7fe81a..0000000000 --- a/containers/docker/master-alpine/Dockerfile +++ /dev/null @@ -1,14 +0,0 @@ -FROM alpine:3.7 - -RUN \ - apk add --update go git make gcc musl-dev linux-headers ca-certificates && \ - git clone --depth 1 --branch release/1.8 https://github.com/ethereum/go-ethereum && \ - (cd go-ethereum && make geth) && \ - cp go-ethereum/build/bin/geth /geth && \ - apk del go git make gcc musl-dev linux-headers && \ - rm -rf /go-ethereum && rm -rf /var/cache/apk/* - -EXPOSE 8545 -EXPOSE 30303 - -ENTRYPOINT ["/geth"] diff --git a/containers/docker/master-ubuntu/Dockerfile b/containers/docker/master-ubuntu/Dockerfile deleted file mode 100644 index 4cfc4f58c0..0000000000 --- a/containers/docker/master-ubuntu/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -FROM ubuntu:xenial - -ENV PATH=/usr/lib/go-1.9/bin:$PATH - -RUN \ - apt-get update && apt-get upgrade -q -y && \ - apt-get install -y --no-install-recommends golang-1.9 git make gcc libc-dev ca-certificates && \ - git clone --depth 1 --branch release/1.8 https://github.com/ethereum/go-ethereum && \ - (cd go-ethereum && make geth) && \ - cp go-ethereum/build/bin/geth /geth && \ - apt-get remove -y golang-1.9 git make gcc libc-dev && apt autoremove -y && apt-get clean && \ - rm -rf /go-ethereum - -EXPOSE 8545 -EXPOSE 30303 - -ENTRYPOINT ["/geth"] diff --git a/dashboard/README.md b/dashboard/README.md deleted file mode 100644 index 641c5f44bc..0000000000 --- a/dashboard/README.md +++ /dev/null @@ -1,58 +0,0 @@ -## Go Ethereum Dashboard - -The dashboard is a data visualizer integrated into geth, intended to collect and visualize useful information of an Ethereum node. It consists of two parts: - -* The client visualizes the collected data. -* The server collects the data, and updates the clients. - -The client's UI uses [React][React] with JSX syntax, which is validated by the [ESLint][ESLint] linter mostly according to the [Airbnb React/JSX Style Guide][Airbnb]. The style is defined in the `.eslintrc` configuration file. The resources are bundled into a single `bundle.js` file using [Webpack][Webpack], which relies on the `webpack.config.js`. The bundled file is referenced from `dashboard.html` and takes part in the `assets.go` too. The necessary dependencies for the module bundler are gathered by [Node.js][Node.js]. - -### Development and bundling - -As the dashboard depends on certain NPM packages (which are not included in the `go-ethereum` repo), these need to be installed first: - -``` -$ (cd dashboard/assets && yarn install && yarn flow) -``` - -Normally the dashboard assets are bundled into Geth via `go-bindata` to avoid external dependencies. Rebuilding Geth after each UI modification however is not feasible from a developer perspective. Instead, we can run `yarn dev` to watch for file system changes and refresh the browser automatically. - -``` -$ geth --dashboard --vmodule=dashboard=5 -$ (cd dashboard/assets && yarn dev) -``` - -To bundle up the final UI into Geth, run `go generate`: - -``` -$ (cd dashboard && go generate) -``` - -### Static type checking - -Since JavaScript doesn't provide type safety, [Flow][Flow] is used to check types. These are only useful during development, so at the end of the process Babel will strip them. - -To take advantage of static type checking, your IDE needs to be prepared for it. In case of [Atom][Atom] a configuration guide can be found [here][Atom config]: Install the [Nuclide][Nuclide] package for Flow support, making sure it installs all of its support packages by enabling `Install Recommended Packages on Startup`, and set the path of the `flow-bin` which were installed previously by `yarn`. - -For more IDE support install the `linter-eslint` package too, which finds the `.eslintrc` file, and provides real-time linting. Atom warns, that these two packages are incompatible, but they seem to work well together. For third-party library errors and auto-completion [flow-typed][flow-typed] is used. - -### Have fun - -[Webpack][Webpack] offers handy tools for visualizing the bundle's dependency tree and space usage. - -* Generate the bundle's profile running `yarn stats` -* For the _dependency tree_ go to [Webpack Analyze][WA], and import `stats.json` -* For the _space usage_ go to [Webpack Visualizer][WV], and import `stats.json` - -[React]: https://reactjs.org/ -[ESLint]: https://eslint.org/ -[Airbnb]: https://github.com/airbnb/javascript/tree/master/react -[Webpack]: https://webpack.github.io/ -[WA]: http://webpack.github.io/analyse/ -[WV]: http://chrisbateman.github.io/webpack-visualizer/ -[Node.js]: https://nodejs.org/en/ -[Flow]: https://flow.org/ -[Atom]: https://atom.io/ -[Atom config]: https://medium.com/@fastphrase/integrating-flow-into-a-react-project-fbbc2f130eed -[Nuclide]: https://nuclide.io/docs/quick-start/getting-started/ -[flow-typed]: https://github.com/flowtype/flow-typed diff --git a/dashboard/assets.go.REMOVED.git-id b/dashboard/assets.go.REMOVED.git-id deleted file mode 100644 index 567bffe873..0000000000 --- a/dashboard/assets.go.REMOVED.git-id +++ /dev/null @@ -1 +0,0 @@ -521d134a6ab126bb936a72c39bd3aba4fddffad6 \ No newline at end of file diff --git a/dashboard/assets/.eslintrc b/dashboard/assets/.eslintrc deleted file mode 100644 index ab7a3a0392..0000000000 --- a/dashboard/assets/.eslintrc +++ /dev/null @@ -1,86 +0,0 @@ -// 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 . - -// React syntax style mostly according to https://github.com/airbnb/javascript/tree/master/react -{ - 'env': { - 'browser': true, - 'node': true, - 'es6': true, - }, - 'parser': 'babel-eslint', - 'parserOptions': { - 'sourceType': 'module', - 'ecmaVersion': 6, - 'ecmaFeatures': { - 'jsx': true, - } - }, - 'extends': 'airbnb', - 'plugins': [ - 'flowtype', - 'react', - ], - 'rules': { - 'no-tabs': 'off', - 'indent': ['error', 'tab'], - 'react/jsx-indent': ['error', 'tab'], - 'react/jsx-indent-props': ['error', 'tab'], - 'react/prefer-stateless-function': 'off', - 'jsx-quotes': ['error', 'prefer-single'], - 'no-plusplus': 'off', - 'no-console': ['error', { allow: ['error'] }], - - // Specifies the maximum length of a line. - 'max-len': ['warn', 120, 2, { - 'ignoreUrls': true, - 'ignoreComments': false, - 'ignoreRegExpLiterals': true, - 'ignoreStrings': true, - 'ignoreTemplateLiterals': true, - }], - // Enforces consistent spacing between keys and values in object literal properties. - 'key-spacing': ['error', {'align': { - 'beforeColon': false, - 'afterColon': true, - 'on': 'value' - }}], - // Prohibits padding inside curly braces. - 'object-curly-spacing': ['error', 'never'], - 'no-use-before-define': 'off', // messageAPI - 'default-case': 'off', - - 'flowtype/boolean-style': ['error', 'boolean'], - 'flowtype/define-flow-type': 'warn', - 'flowtype/generic-spacing': ['error', 'never'], - 'flowtype/no-primitive-constructor-types': 'error', - 'flowtype/no-weak-types': 'error', - 'flowtype/object-type-delimiter': ['error', 'comma'], - 'flowtype/require-valid-file-annotation': 'error', - 'flowtype/semi': ['error', 'always'], - 'flowtype/space-after-type-colon': ['error', 'always'], - 'flowtype/space-before-generic-bracket': ['error', 'never'], - 'flowtype/space-before-type-colon': ['error', 'never'], - 'flowtype/union-intersection-spacing': ['error', 'always'], - 'flowtype/use-flow-type': 'warn', - 'flowtype/valid-syntax': 'warn', - }, - 'settings': { - 'flowtype': { - 'onlyFilesWithFlowAnnotation': true, - } - }, -} diff --git a/dashboard/assets/.flowconfig b/dashboard/assets/.flowconfig deleted file mode 100644 index 8051ae5de5..0000000000 --- a/dashboard/assets/.flowconfig +++ /dev/null @@ -1,9 +0,0 @@ -[ignore] -/node_modules/material-ui/.*\.js\.flow - -[libs] -/flow-typed/ -node_modules/jss/flow-typed - -[options] -include_warnings=true diff --git a/dashboard/assets/common.jsx b/dashboard/assets/common.jsx deleted file mode 100644 index 08a7554fff..0000000000 --- a/dashboard/assets/common.jsx +++ /dev/null @@ -1,71 +0,0 @@ -// @flow - -// 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 . - -type ProvidedMenuProp = {|title: string, icon: string|}; -const menuSkeletons: Array<{|id: string, menu: ProvidedMenuProp|}> = [ - { - id: 'home', - menu: { - title: 'Home', - icon: 'home', - }, - }, { - id: 'chain', - menu: { - title: 'Chain', - icon: 'link', - }, - }, { - id: 'txpool', - menu: { - title: 'TxPool', - icon: 'credit-card', - }, - }, { - id: 'network', - menu: { - title: 'Network', - icon: 'globe', - }, - }, { - id: 'system', - menu: { - title: 'System', - icon: 'tachometer', - }, - }, { - id: 'logs', - menu: { - title: 'Logs', - icon: 'list', - }, - }, -]; -export type MenuProp = {|...ProvidedMenuProp, id: string|}; -// The sidebar menu and the main content are rendered based on these elements. -// Using the id is circumstantial in some cases, so it is better to insert it also as a value. -// This way the mistyping is prevented. -export const MENU: Map = new Map(menuSkeletons.map(({id, menu}) => ([id, {id, ...menu}]))); - -export const DURATION = 200; - -export const styles = { - light: { - color: 'rgba(255, 255, 255, 0.54)', - }, -} diff --git a/dashboard/assets/components/Body.jsx b/dashboard/assets/components/Body.jsx deleted file mode 100644 index 054e040648..0000000000 --- a/dashboard/assets/components/Body.jsx +++ /dev/null @@ -1,61 +0,0 @@ -// @flow - -// 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 . - -import React, {Component} from 'react'; - -import SideBar from './SideBar'; -import Main from './Main'; -import type {Content} from '../types/content'; - -// styles contains the constant styles of the component. -const styles = { - body: { - display: 'flex', - width: '100%', - height: '100%', - }, -}; - -export type Props = { - opened: boolean, - changeContent: string => void, - active: string, - content: Content, - shouldUpdate: Object, -}; - -// Body renders the body of the dashboard. -class Body extends Component { - render() { - return ( -
- -
-
- ); - } -} - -export default Body; diff --git a/dashboard/assets/components/ChartRow.jsx b/dashboard/assets/components/ChartRow.jsx deleted file mode 100644 index 1075346fe4..0000000000 --- a/dashboard/assets/components/ChartRow.jsx +++ /dev/null @@ -1,57 +0,0 @@ -// @flow - -// Copyright 2018 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 . - -import React, {Component} from 'react'; -import type {ChildrenArray} from 'react'; - -import Grid from 'material-ui/Grid'; - -// styles contains the constant styles of the component. -const styles = { - container: { - flexWrap: 'nowrap', - height: '100%', - maxWidth: '100%', - margin: 0, - }, - item: { - flex: 1, - padding: 0, - }, -} - -export type Props = { - children: ChildrenArray>, -}; - -// ChartRow renders a row of equally sized responsive charts. -class ChartRow extends Component { - render() { - return ( - - {React.Children.map(this.props.children, child => ( - - {child} - - ))} - - ); - } -} - -export default ChartRow; diff --git a/dashboard/assets/components/CustomTooltip.jsx b/dashboard/assets/components/CustomTooltip.jsx deleted file mode 100644 index 3405f93056..0000000000 --- a/dashboard/assets/components/CustomTooltip.jsx +++ /dev/null @@ -1,95 +0,0 @@ -// @flow - -// Copyright 2018 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 . - -import React, {Component} from 'react'; - -import Typography from 'material-ui/Typography'; -import {styles} from '../common'; - -// multiplier multiplies a number by another. -export const multiplier = (by: number = 1) => (x: number) => x * by; - -// percentPlotter renders a tooltip, which displays the value of the payload followed by a percent sign. -export const percentPlotter = (text: string, mapper: (T => T) = multiplier(1)) => (payload: T) => { - const p = mapper(payload); - if (typeof p !== 'number') { - return null; - } - return ( - - {text} {p.toFixed(2)} % - - ); -}; - -// unit contains the units for the bytePlotter. -const unit = ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi']; - -// simplifyBytes returns the simplified version of the given value followed by the unit. -const simplifyBytes = (x: number) => { - let i = 0; - for (; x > 1024 && i < 8; i++) { - x /= 1024; - } - return x.toFixed(2).toString().concat(' ', unit[i], 'B'); -}; - -// bytePlotter renders a tooltip, which displays the payload as a byte value. -export const bytePlotter = (text: string, mapper: (T => T) = multiplier(1)) => (payload: T) => { - const p = mapper(payload); - if (typeof p !== 'number') { - return null; - } - return ( - - {text} {simplifyBytes(p)} - - ); -}; - -// bytePlotter renders a tooltip, which displays the payload as a byte value followed by '/s'. -export const bytePerSecPlotter = (text: string, mapper: (T => T) = multiplier(1)) => (payload: T) => { - const p = mapper(payload); - if (typeof p !== 'number') { - return null; - } - return ( - - {text} {simplifyBytes(p)}/s - - ); -}; - -export type Props = { - active: boolean, - payload: Object, - tooltip: (text: string, mapper?: T => T) => (payload: mixed) => null | React$Element, -}; - -// CustomTooltip takes a tooltip function, and uses it to plot the active value of the chart. -class CustomTooltip extends Component { - render() { - const {active, payload, tooltip} = this.props; - if (!active || typeof tooltip !== 'function') { - return null; - } - return tooltip(payload[0].value); - } -} - -export default CustomTooltip; diff --git a/dashboard/assets/components/Dashboard.jsx b/dashboard/assets/components/Dashboard.jsx deleted file mode 100644 index 8e6bf98695..0000000000 --- a/dashboard/assets/components/Dashboard.jsx +++ /dev/null @@ -1,235 +0,0 @@ -// @flow - -// 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 . - -import React, {Component} from 'react'; - -import withStyles from 'material-ui/styles/withStyles'; - -import Header from './Header'; -import Body from './Body'; -import {MENU} from '../common'; -import type {Content} from '../types/content'; - -// deepUpdate updates an object corresponding to the given update data, which has -// the shape of the same structure as the original object. updater also has the same -// structure, except that it contains functions where the original data needs to be -// updated. These functions are used to handle the update. -// -// Since the messages have the same shape as the state content, this approach allows -// the generalization of the message handling. The only necessary thing is to set a -// handler function for every path of the state in order to maximize the flexibility -// of the update. -const deepUpdate = (updater: Object, update: Object, prev: Object): $Shape => { - if (typeof update === 'undefined') { - // TODO (kurkomisi): originally this was deep copy, investigate it. - return prev; - } - if (typeof updater === 'function') { - return updater(update, prev); - } - const updated = {}; - Object.keys(prev).forEach((key) => { - updated[key] = deepUpdate(updater[key], update[key], prev[key]); - }); - - return updated; -}; - -// shouldUpdate returns the structure of a message. It is used to prevent unnecessary render -// method triggerings. In the affected component's shouldComponentUpdate method it can be checked -// whether the involved data was changed or not by checking the message structure. -// -// We could return the message itself too, but it's safer not to give access to it. -const shouldUpdate = (updater: Object, msg: Object) => { - const su = {}; - Object.keys(msg).forEach((key) => { - su[key] = typeof updater[key] !== 'function' ? shouldUpdate(updater[key], msg[key]) : true; - }); - - return su; -}; - -// replacer is a state updater function, which replaces the original data. -const replacer = (update: T) => update; - -// appender is a state updater function, which appends the update data to the -// existing data. limit defines the maximum allowed size of the created array, -// mapper maps the update data. -const appender = (limit: number, mapper = replacer) => (update: Array, prev: Array) => [ - ...prev, - ...update.map(sample => mapper(sample)), -].slice(-limit); - -// defaultContent is the initial value of the state content. -const defaultContent: Content = { - general: { - version: null, - commit: null, - }, - home: {}, - chain: {}, - txpool: {}, - network: {}, - system: { - activeMemory: [], - virtualMemory: [], - networkIngress: [], - networkEgress: [], - processCPU: [], - systemCPU: [], - diskRead: [], - diskWrite: [], - }, - logs: { - log: [], - }, -}; - -// updaters contains the state updater functions for each path of the state. -// -// TODO (kurkomisi): Define a tricky type which embraces the content and the updaters. -const updaters = { - general: { - version: replacer, - commit: replacer, - }, - home: null, - chain: null, - txpool: null, - network: null, - system: { - activeMemory: appender(200), - virtualMemory: appender(200), - networkIngress: appender(200), - networkEgress: appender(200), - processCPU: appender(200), - systemCPU: appender(200), - diskRead: appender(200), - diskWrite: appender(200), - }, - logs: { - log: appender(200), - }, -}; - -// styles contains the constant styles of the component. -const styles = { - dashboard: { - display: 'flex', - flexFlow: 'column', - width: '100%', - height: '100%', - zIndex: 1, - overflow: 'hidden', - }, -}; - -// themeStyles returns the styles generated from the theme for the component. -const themeStyles: Object = (theme: Object) => ({ - dashboard: { - background: theme.palette.background.default, - }, -}); - -export type Props = { - classes: Object, // injected by withStyles() -}; - -type State = { - active: string, // active menu - sideBar: boolean, // true if the sidebar is opened - content: Content, // the visualized data - shouldUpdate: Object, // labels for the components, which need to re-render based on the incoming message -}; - -// Dashboard is the main component, which renders the whole page, makes connection with the server and -// listens for messages. When there is an incoming message, updates the page's content correspondingly. -class Dashboard extends Component { - constructor(props: Props) { - super(props); - this.state = { - active: MENU.get('home').id, - sideBar: true, - content: defaultContent, - shouldUpdate: {}, - }; - } - - // componentDidMount initiates the establishment of the first websocket connection after the component is rendered. - componentDidMount() { - this.reconnect(); - } - - // reconnect establishes a websocket connection with the server, listens for incoming messages - // and tries to reconnect on connection loss. - reconnect = () => { - // PROD is defined by webpack. - const server = new WebSocket(`${((window.location.protocol === 'https:') ? 'wss://' : 'ws://')}${PROD ? window.location.host : 'localhost:8080'}/api`); - server.onopen = () => { - this.setState({content: defaultContent, shouldUpdate: {}}); - }; - server.onmessage = (event) => { - const msg: $Shape = JSON.parse(event.data); - if (!msg) { - console.error(`Incoming message is ${msg}`); - return; - } - this.update(msg); - }; - server.onclose = () => { - setTimeout(this.reconnect, 3000); - }; - }; - - // update updates the content corresponding to the incoming message. - update = (msg: $Shape) => { - this.setState(prevState => ({ - content: deepUpdate(updaters, msg, prevState.content), - shouldUpdate: shouldUpdate(updaters, msg), - })); - }; - - // changeContent sets the active label, which is used at the content rendering. - changeContent = (newActive: string) => { - this.setState(prevState => (prevState.active !== newActive ? {active: newActive} : {})); - }; - - // switchSideBar opens or closes the sidebar's state. - switchSideBar = () => { - this.setState(prevState => ({sideBar: !prevState.sideBar})); - }; - - render() { - return ( -
-
- -
- ); - } -} - -export default withStyles(themeStyles)(Dashboard); diff --git a/dashboard/assets/components/Footer.jsx b/dashboard/assets/components/Footer.jsx deleted file mode 100644 index 20830cbba8..0000000000 --- a/dashboard/assets/components/Footer.jsx +++ /dev/null @@ -1,179 +0,0 @@ -// @flow - -// 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 . - -import React, {Component} from 'react'; - -import withStyles from 'material-ui/styles/withStyles'; -import Typography from 'material-ui/Typography'; -import Grid from 'material-ui/Grid'; -import {ResponsiveContainer, AreaChart, Area, Tooltip} from 'recharts'; - -import ChartRow from './ChartRow'; -import CustomTooltip, {bytePlotter, bytePerSecPlotter, percentPlotter, multiplier} from './CustomTooltip'; -import {styles as commonStyles} from '../common'; -import type {General, System} from '../types/content'; - -const FOOTER_SYNC_ID = 'footerSyncId'; - -const CPU = 'cpu'; -const MEMORY = 'memory'; -const DISK = 'disk'; -const TRAFFIC = 'traffic'; - -const TOP = 'Top'; -const BOTTOM = 'Bottom'; - -// styles contains the constant styles of the component. -const styles = { - footer: { - maxWidth: '100%', - flexWrap: 'nowrap', - margin: 0, - }, - chartRowWrapper: { - height: '100%', - padding: 0, - }, - doubleChartWrapper: { - height: '100%', - width: '99%', - }, -}; - -// themeStyles returns the styles generated from the theme for the component. -const themeStyles: Object = (theme: Object) => ({ - footer: { - backgroundColor: theme.palette.grey[900], - color: theme.palette.getContrastText(theme.palette.grey[900]), - zIndex: theme.zIndex.appBar, - height: theme.spacing.unit * 10, - }, -}); - -export type Props = { - classes: Object, // injected by withStyles() - theme: Object, - general: General, - system: System, - shouldUpdate: Object, -}; - -// Footer renders the footer of the dashboard. -class Footer extends Component { - shouldComponentUpdate(nextProps) { - return typeof nextProps.shouldUpdate.general !== 'undefined' || typeof nextProps.shouldUpdate.system !== 'undefined'; - } - - // halfHeightChart renders an area chart with half of the height of its parent. - halfHeightChart = (chartProps, tooltip, areaProps) => ( - - - {!tooltip || (} />)} - - - - ); - - // doubleChart renders a pair of charts separated by the baseline. - doubleChart = (syncId, chartKey, topChart, bottomChart) => { - if (!Array.isArray(topChart.data) || !Array.isArray(bottomChart.data)) { - return null; - } - const topDefault = topChart.default || 0; - const bottomDefault = bottomChart.default || 0; - const topKey = `${chartKey}${TOP}`; - const bottomKey = `${chartKey}${BOTTOM}`; - const topColor = '#8884d8'; - const bottomColor = '#82ca9d'; - - return ( -
- {this.halfHeightChart( - { - syncId, - data: topChart.data.map(({value}) => ({[topKey]: value || topDefault})), - margin: {top: 5, right: 5, bottom: 0, left: 5}, - }, - topChart.tooltip, - {dataKey: topKey, stroke: topColor, fill: topColor}, - )} - {this.halfHeightChart( - { - syncId, - data: bottomChart.data.map(({value}) => ({[bottomKey]: -value || -bottomDefault})), - margin: {top: 0, right: 5, bottom: 5, left: 5}, - }, - bottomChart.tooltip, - {dataKey: bottomKey, stroke: bottomColor, fill: bottomColor}, - )} -
- ); - }; - - render() { - const {general, system} = this.props; - - return ( - - - - {this.doubleChart( - FOOTER_SYNC_ID, - CPU, - {data: system.processCPU, tooltip: percentPlotter('Process load')}, - {data: system.systemCPU, tooltip: percentPlotter('System load', multiplier(-1))}, - )} - {this.doubleChart( - FOOTER_SYNC_ID, - MEMORY, - {data: system.activeMemory, tooltip: bytePlotter('Active memory')}, - {data: system.virtualMemory, tooltip: bytePlotter('Virtual memory', multiplier(-1))}, - )} - {this.doubleChart( - FOOTER_SYNC_ID, - DISK, - {data: system.diskRead, tooltip: bytePerSecPlotter('Disk read')}, - {data: system.diskWrite, tooltip: bytePerSecPlotter('Disk write', multiplier(-1))}, - )} - {this.doubleChart( - FOOTER_SYNC_ID, - TRAFFIC, - {data: system.networkIngress, tooltip: bytePerSecPlotter('Download')}, - {data: system.networkEgress, tooltip: bytePerSecPlotter('Upload', multiplier(-1))}, - )} - - - - - Geth {general.version} - - {general.commit && ( - - {'Commit '} - - {general.commit.substring(0, 8)} - - - )} - - - ); - } -} - -export default withStyles(themeStyles)(Footer); diff --git a/dashboard/assets/components/Header.jsx b/dashboard/assets/components/Header.jsx deleted file mode 100644 index ccdfbc6f02..0000000000 --- a/dashboard/assets/components/Header.jsx +++ /dev/null @@ -1,73 +0,0 @@ -// @flow - -// 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 . - -import React, {Component} from 'react'; - -import withStyles from 'material-ui/styles/withStyles'; -import AppBar from 'material-ui/AppBar'; -import Toolbar from 'material-ui/Toolbar'; -import IconButton from 'material-ui/IconButton'; -import Icon from 'material-ui/Icon'; -import MenuIcon from 'material-ui-icons/Menu'; -import Typography from 'material-ui/Typography'; - -// themeStyles returns the styles generated from the theme for the component. -const themeStyles = (theme: Object) => ({ - header: { - backgroundColor: theme.palette.grey[900], - color: theme.palette.getContrastText(theme.palette.grey[900]), - zIndex: theme.zIndex.appBar, - }, - toolbar: { - paddingLeft: theme.spacing.unit, - paddingRight: theme.spacing.unit, - }, - title: { - paddingLeft: theme.spacing.unit, - fontSize: 3 * theme.spacing.unit, - }, -}); - -export type Props = { - classes: Object, // injected by withStyles() - switchSideBar: () => void, -}; - -// Header renders the header of the dashboard. -class Header extends Component { - render() { - const {classes} = this.props; - - return ( - - - - - - - - - Go Ethereum Dashboard - - - - ); - } -} - -export default withStyles(themeStyles)(Header); diff --git a/dashboard/assets/components/Main.jsx b/dashboard/assets/components/Main.jsx deleted file mode 100644 index fba8ca1f61..0000000000 --- a/dashboard/assets/components/Main.jsx +++ /dev/null @@ -1,88 +0,0 @@ -// @flow - -// 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 . - -import React, {Component} from 'react'; - -import withStyles from 'material-ui/styles/withStyles'; - -import {MENU} from '../common'; -import Footer from './Footer'; -import type {Content} from '../types/content'; - -// styles contains the constant styles of the component. -const styles = { - wrapper: { - display: 'flex', - flexDirection: 'column', - width: '100%', - }, - content: { - flex: 1, - overflow: 'auto', - }, -}; - -// themeStyles returns the styles generated from the theme for the component. -const themeStyles = theme => ({ - content: { - backgroundColor: theme.palette.background.default, - padding: theme.spacing.unit * 3, - }, -}); - -export type Props = { - classes: Object, - active: string, - content: Content, - shouldUpdate: Object, -}; - -// Main renders the chosen content. -class Main extends Component { - render() { - const { - classes, active, content, shouldUpdate, - } = this.props; - - let children = null; - switch (active) { - case MENU.get('home').id: - case MENU.get('chain').id: - case MENU.get('txpool').id: - case MENU.get('network').id: - case MENU.get('system').id: - children =
Work in progress.
; - break; - case MENU.get('logs').id: - children =
{content.logs.log.map((log, index) =>
{log}
)}
; - } - - return ( -
-
{children}
-
-
- ); - } -} - -export default withStyles(themeStyles)(Main); diff --git a/dashboard/assets/components/SideBar.jsx b/dashboard/assets/components/SideBar.jsx deleted file mode 100644 index 463d6cb40f..0000000000 --- a/dashboard/assets/components/SideBar.jsx +++ /dev/null @@ -1,116 +0,0 @@ -// @flow - -// 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 . - -import React, {Component} from 'react'; - -import withStyles from 'material-ui/styles/withStyles'; -import List, {ListItem, ListItemIcon, ListItemText} from 'material-ui/List'; -import Icon from 'material-ui/Icon'; -import Transition from 'react-transition-group/Transition'; -import {Icon as FontAwesome} from 'react-fa'; - -import {MENU, DURATION} from '../common'; - -// styles contains the constant styles of the component. -const styles = { - menu: { - default: { - transition: `margin-left ${DURATION}ms`, - }, - transition: { - entered: {marginLeft: -200}, - }, - }, -}; - -// themeStyles returns the styles generated from the theme for the component. -const themeStyles = theme => ({ - list: { - background: theme.palette.grey[900], - }, - listItem: { - minWidth: theme.spacing.unit * 7, - }, - icon: { - fontSize: theme.spacing.unit * 3, - }, -}); - -export type Props = { - classes: Object, // injected by withStyles() - opened: boolean, - changeContent: string => void, -}; - -// SideBar renders the sidebar of the dashboard. -class SideBar extends Component { - shouldComponentUpdate(nextProps) { - return nextProps.opened !== this.props.opened; - } - - // clickOn returns a click event handler function for the given menu item. - clickOn = menu => (event) => { - event.preventDefault(); - this.props.changeContent(menu); - }; - - // menuItems returns the menu items corresponding to the sidebar state. - menuItems = (transitionState) => { - const {classes} = this.props; - const children = []; - MENU.forEach((menu) => { - children.push(( - - - - - - - - - )); - }); - return children; - }; - - // menu renders the list of the menu items. - menu = (transitionState: Object) => ( -
- - {this.menuItems(transitionState)} - -
- ); - - render() { - return ( - - {this.menu} - - ); - } -} - -export default withStyles(themeStyles)(SideBar); diff --git a/dashboard/assets/fa-only-woff-loader.js b/dashboard/assets/fa-only-woff-loader.js deleted file mode 100644 index 4d30357361..0000000000 --- a/dashboard/assets/fa-only-woff-loader.js +++ /dev/null @@ -1,25 +0,0 @@ -// 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 . - -// fa-only-woff-loader removes the .eot, .ttf, .svg dependencies of the FontAwesome library, -// because they produce unused extra blobs. -module.exports = function(content) { - return content - .replace(/src.*url(?!.*url.*(\.eot)).*(\.eot)[^;]*;/,'') - .replace(/url(?!.*url.*(\.eot)).*(\.eot)[^,]*,/,'') - .replace(/url(?!.*url.*(\.ttf)).*(\.ttf)[^,]*,/,'') - .replace(/,[^,]*url(?!.*url.*(\.svg)).*(\.svg)[^;]*;/,';'); -}; diff --git a/dashboard/assets/index.html b/dashboard/assets/index.html deleted file mode 100644 index 2491bf1eaf..0000000000 --- a/dashboard/assets/index.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - Go Ethereum Dashboard - - - - -
- - - diff --git a/dashboard/assets/index.jsx b/dashboard/assets/index.jsx deleted file mode 100644 index 4ee567b01b..0000000000 --- a/dashboard/assets/index.jsx +++ /dev/null @@ -1,41 +0,0 @@ -// @flow - -// 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 . - -import React from 'react'; -import {render} from 'react-dom'; - -import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider'; -import createMuiTheme from 'material-ui/styles/createMuiTheme'; - -import Dashboard from './components/Dashboard'; - -const theme: Object = createMuiTheme({ - palette: { - type: 'dark', - }, -}); -const dashboard = document.getElementById('dashboard'); -if (dashboard) { - // Renders the whole dashboard. - render( - - - , - dashboard, - ); -} diff --git a/dashboard/assets/package.json b/dashboard/assets/package.json deleted file mode 100644 index 5ac179b9ef..0000000000 --- a/dashboard/assets/package.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "dependencies": { - "babel-core": "^6.26.0", - "babel-eslint": "^8.2.1", - "babel-loader": "^7.1.2", - "babel-plugin-transform-class-properties": "^6.24.1", - "babel-plugin-transform-decorators-legacy": "^1.3.4", - "babel-plugin-transform-flow-strip-types": "^6.22.0", - "babel-plugin-transform-runtime": "^6.23.0", - "babel-preset-env": "^1.6.1", - "babel-preset-react": "^6.24.1", - "babel-preset-stage-0": "^6.24.1", - "babel-runtime": "^6.26.0", - "classnames": "^2.2.5", - "css-loader": "^0.28.9", - "eslint": "^4.16.0", - "eslint-config-airbnb": "^16.1.0", - "eslint-loader": "^2.0.0", - "eslint-plugin-flowtype": "^2.41.0", - "eslint-plugin-import": "^2.8.0", - "eslint-plugin-jsx-a11y": "^6.0.3", - "eslint-plugin-react": "^7.5.1", - "file-loader": "^1.1.6", - "flow-bin": "^0.63.1", - "flow-bin-loader": "^1.0.2", - "flow-typed": "^2.2.3", - "material-ui": "^1.0.0-beta.30", - "material-ui-icons": "^1.0.0-beta.17", - "path": "^0.12.7", - "react": "^16.2.0", - "react-dom": "^16.2.0", - "react-fa": "^5.0.0", - "react-transition-group": "^2.2.1", - "recharts": "^1.0.0-beta.9", - "style-loader": "^0.19.1", - "url": "^0.11.0", - "url-loader": "^0.6.2", - "webpack": "^3.10.0", - "webpack-dev-server": "^2.11.1" - }, - "scripts": { - "build": "NODE_ENV=production webpack", - "stats": "webpack --profile --json > stats.json", - "dev": "webpack-dev-server --port 8081", - "flow": "flow-typed install" - } -} diff --git a/dashboard/assets/types/content.jsx b/dashboard/assets/types/content.jsx deleted file mode 100644 index 0e2e0b49de..0000000000 --- a/dashboard/assets/types/content.jsx +++ /dev/null @@ -1,70 +0,0 @@ -// @flow - -// 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 . - -export type Content = { - general: General, - home: Home, - chain: Chain, - txpool: TxPool, - network: Network, - system: System, - logs: Logs, -}; - -export type ChartEntries = Array; - -export type ChartEntry = { - time: Date, - value: number, -}; - -export type General = { - version: ?string, - commit: ?string, -}; - -export type Home = { - /* TODO (kurkomisi) */ -}; - -export type Chain = { - /* TODO (kurkomisi) */ -}; - -export type TxPool = { - /* TODO (kurkomisi) */ -}; - -export type Network = { - /* TODO (kurkomisi) */ -}; - -export type System = { - activeMemory: ChartEntries, - virtualMemory: ChartEntries, - networkIngress: ChartEntries, - networkEgress: ChartEntries, - processCPU: ChartEntries, - systemCPU: ChartEntries, - diskRead: ChartEntries, - diskWrite: ChartEntries, -}; - -export type Logs = { - log: Array, -}; diff --git a/dashboard/assets/webpack.config.js b/dashboard/assets/webpack.config.js deleted file mode 100644 index f58d35ad1e..0000000000 --- a/dashboard/assets/webpack.config.js +++ /dev/null @@ -1,77 +0,0 @@ -// 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 . - -const webpack = require('webpack'); -const path = require('path'); - -module.exports = { - resolve: { - extensions: ['.js', '.jsx'], - }, - entry: './index', - output: { - path: path.resolve(__dirname, ''), - filename: 'bundle.js', - }, - plugins: [ - new webpack.optimize.UglifyJsPlugin({ - comments: false, - mangle: false, - beautify: true, - }), - new webpack.DefinePlugin({ - PROD: process.env.NODE_ENV === 'production', - }), - ], - module: { - rules: [ - { - test: /\.jsx$/, // regexp for JSX files - exclude: /node_modules/, - use: [ // order: from bottom to top - { - loader: 'babel-loader', - options: { - plugins: [ // order: from top to bottom - // 'transform-decorators-legacy', // @withStyles, @withTheme - 'transform-class-properties', // static defaultProps - 'transform-flow-strip-types', - ], - presets: [ // order: from bottom to top - 'env', - 'react', - 'stage-0', - ], - }, - }, - // 'eslint-loader', // show errors not only in the editor, but also in the console - ], - }, - { - test: /font-awesome\.css$/, - use: [ - 'style-loader', - 'css-loader', - path.resolve(__dirname, './fa-only-woff-loader.js'), - ], - }, - { - test: /\.woff2?$/, // font-awesome icons - use: 'url-loader', - }, - ], - }, -}; diff --git a/dashboard/assets/yarn.lock b/dashboard/assets/yarn.lock deleted file mode 100644 index 7480c719f8..0000000000 --- a/dashboard/assets/yarn.lock +++ /dev/null @@ -1,6551 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@babel/code-frame@7.0.0-beta.40", "@babel/code-frame@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.40.tgz#37e2b0cf7c56026b4b21d3927cadf81adec32ac6" - dependencies: - "@babel/highlight" "7.0.0-beta.40" - -"@babel/generator@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.40.tgz#ab61f9556f4f71dbd1138949c795bb9a21e302ea" - dependencies: - "@babel/types" "7.0.0-beta.40" - jsesc "^2.5.1" - lodash "^4.2.0" - source-map "^0.5.0" - trim-right "^1.0.1" - -"@babel/helper-function-name@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.40.tgz#9d033341ab16517f40d43a73f2d81fc431ccd7b6" - dependencies: - "@babel/helper-get-function-arity" "7.0.0-beta.40" - "@babel/template" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" - -"@babel/helper-get-function-arity@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.40.tgz#ac0419cf067b0ec16453e1274f03878195791c6e" - dependencies: - "@babel/types" "7.0.0-beta.40" - -"@babel/highlight@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.0.0-beta.40.tgz#b43d67d76bf46e1d10d227f68cddcd263786b255" - dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^3.0.0" - -"@babel/template@7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.40.tgz#034988c6424eb5c3268fe6a608626de1f4410fc8" - dependencies: - "@babel/code-frame" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" - babylon "7.0.0-beta.40" - lodash "^4.2.0" - -"@babel/traverse@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.40.tgz#d140e449b2e093ef9fe1a2eecc28421ffb4e521e" - dependencies: - "@babel/code-frame" "7.0.0-beta.40" - "@babel/generator" "7.0.0-beta.40" - "@babel/helper-function-name" "7.0.0-beta.40" - "@babel/types" "7.0.0-beta.40" - babylon "7.0.0-beta.40" - debug "^3.0.1" - globals "^11.1.0" - invariant "^2.2.0" - lodash "^4.2.0" - -"@babel/types@7.0.0-beta.40", "@babel/types@^7.0.0-beta.40": - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.40.tgz#25c3d7aae14126abe05fcb098c65a66b6d6b8c14" - dependencies: - esutils "^2.0.2" - lodash "^4.2.0" - to-fast-properties "^2.0.0" - -"@types/jss@^9.3.0": - version "9.5.0" - resolved "https://registry.yarnpkg.com/@types/jss/-/jss-9.5.0.tgz#65d9b5c61f1e95ad3acd53e9a8b2d2342aa917e8" - dependencies: - csstype "^1.6.0" - -"@types/react-transition-group@^2.0.6": - version "2.0.7" - resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-2.0.7.tgz#2847292d54c5685d982ae5a3ecb6960946689d87" - dependencies: - "@types/react" "*" - -"@types/react@*": - version "16.0.40" - resolved "https://registry.yarnpkg.com/@types/react/-/react-16.0.40.tgz#caabc2296886f40b67f6fc80f0f3464476461df9" - -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - -accepts@~1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2" - dependencies: - mime-types "~2.1.18" - negotiator "0.6.1" - -acorn-dynamic-import@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/acorn-dynamic-import/-/acorn-dynamic-import-2.0.2.tgz#c752bd210bef679501b6c6cb7fc84f8f47158cc4" - dependencies: - acorn "^4.0.3" - -acorn-jsx@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-3.0.1.tgz#afdf9488fb1ecefc8348f6fb22f464e32a58b36b" - dependencies: - acorn "^3.0.4" - -acorn@^3.0.4: - version "3.3.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-3.3.0.tgz#45e37fb39e8da3f25baee3ff5369e2bb5f22017a" - -acorn@^4.0.3: - version "4.0.13" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" - -acorn@^5.0.0, acorn@^5.4.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.5.0.tgz#1abb587fbf051f94e3de20e6b26ef910b1828298" - -ajv-keywords@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" - -ajv-keywords@^3.0.0, ajv-keywords@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.1.0.tgz#ac2b27939c543e95d2c06e7f7f5c27be4aa543be" - -ajv@^4.9.1: - version "4.11.8" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" - dependencies: - co "^4.6.0" - json-stable-stringify "^1.0.1" - -ajv@^5.0.0, ajv@^5.2.3, ajv@^5.3.0: - version "5.5.2" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" - dependencies: - co "^4.6.0" - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - -ajv@^6.0.1, ajv@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.2.0.tgz#afac295bbaa0152449e522742e4547c1ae9328d2" - dependencies: - fast-deep-equal "^1.0.0" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.3.0" - -align-text@^0.1.1, align-text@^0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" - dependencies: - kind-of "^3.0.2" - longest "^1.0.1" - repeat-string "^1.5.2" - -alphanum-sort@^1.0.1, alphanum-sort@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" - -ansi-escapes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.0.0.tgz#ec3e8b4e9f8064fc02c3ac9b65f1c275bda8ef92" - -ansi-html@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - -ansi-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - dependencies: - color-convert "^1.9.0" - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -aproba@^1.0.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - -are-we-there-yet@~1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.4.tgz#bb5dca382bb94f05e15194373d16fd3ba1ca110d" - dependencies: - delegates "^1.0.0" - readable-stream "^2.0.6" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - dependencies: - sprintf-js "~1.0.2" - -aria-query@^0.7.0: - version "0.7.1" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-0.7.1.tgz#26cbb5aff64144b0a825be1846e0b16cfa00b11e" - dependencies: - ast-types-flow "0.0.7" - commander "^2.11.0" - -arr-diff@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" - dependencies: - arr-flatten "^1.0.1" - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - -arr-flatten@^1.0.1, arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - -array-find-index@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-find-index/-/array-find-index-1.0.2.tgz#df010aa1287e164bbda6f9723b0a96a1ec4187a1" - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - -array-flatten@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.1.tgz#426bb9da84090c1838d812c8150af20a8331e296" - -array-includes@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.0.3.tgz#184b48f62d92d7452bb31b323165c7f8bd02266d" - dependencies: - define-properties "^1.1.2" - es-abstract "^1.7.0" - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - dependencies: - array-uniq "^1.0.1" - -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - -array-unique@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - -asap@~2.0.3: - version "2.0.6" - resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" - -asn1.js@^4.0.0: - version "4.10.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-4.10.1.tgz#b9c2bf5805f1e64aadeed6df3a2bfafb5a73f5a0" - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -asn1@~0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - -assert-plus@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" - -assert@^1.1.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" - dependencies: - util "0.10.3" - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - -ast-types-flow@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" - -async-each@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" - -async@^1.5.2: - version "1.5.2" - resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" - -async@^2.1.2: - version "2.6.0" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.0.tgz#61a29abb6fcc026fea77e56d1c6ec53a795951f4" - dependencies: - lodash "^4.14.0" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - -atob@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.0.3.tgz#19c7a760473774468f20b2d2d03372ad7d4cbf5d" - -autoprefixer@^6.3.1: - version "6.7.7" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-6.7.7.tgz#1dbd1c835658e35ce3f9984099db00585c782014" - dependencies: - browserslist "^1.7.6" - caniuse-db "^1.0.30000634" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^5.2.16" - postcss-value-parser "^3.2.3" - -aws-sign2@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" - -aws4@^1.2.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" - -axobject-query@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-0.1.0.tgz#62f59dbc59c9f9242759ca349960e7a2fe3c36c0" - dependencies: - ast-types-flow "0.0.7" - -babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" - dependencies: - chalk "^1.1.3" - esutils "^2.0.2" - js-tokens "^3.0.2" - -babel-core@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.26.0.tgz#af32f78b31a6fcef119c87b0fd8d9753f03a0bb8" - dependencies: - babel-code-frame "^6.26.0" - babel-generator "^6.26.0" - babel-helpers "^6.24.1" - babel-messages "^6.23.0" - babel-register "^6.26.0" - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - convert-source-map "^1.5.0" - debug "^2.6.8" - json5 "^0.5.1" - lodash "^4.17.4" - minimatch "^3.0.4" - path-is-absolute "^1.0.1" - private "^0.1.7" - slash "^1.0.0" - source-map "^0.5.6" - -babel-eslint@^8.2.1: - version "8.2.2" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-8.2.2.tgz#1102273354c6f0b29b4ea28a65f97d122296b68b" - dependencies: - "@babel/code-frame" "^7.0.0-beta.40" - "@babel/traverse" "^7.0.0-beta.40" - "@babel/types" "^7.0.0-beta.40" - babylon "^7.0.0-beta.40" - eslint-scope "~3.7.1" - eslint-visitor-keys "^1.0.0" - -babel-generator@^6.26.0: - version "6.26.1" - resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.26.1.tgz#1844408d3b8f0d35a404ea7ac180f087a601bd90" - dependencies: - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - detect-indent "^4.0.0" - jsesc "^1.3.0" - lodash "^4.17.4" - source-map "^0.5.7" - trim-right "^1.0.1" - -babel-helper-bindify-decorators@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-bindify-decorators/-/babel-helper-bindify-decorators-6.24.1.tgz#14c19e5f142d7b47f19a52431e52b1ccbc40a330" - dependencies: - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-builder-binary-assignment-operator-visitor@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz#cce4517ada356f4220bcae8a02c2b346f9a56664" - dependencies: - babel-helper-explode-assignable-expression "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-builder-react-jsx@^6.24.1: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-helper-builder-react-jsx/-/babel-helper-builder-react-jsx-6.26.0.tgz#39ff8313b75c8b65dceff1f31d383e0ff2a408a0" - dependencies: - babel-runtime "^6.26.0" - babel-types "^6.26.0" - esutils "^2.0.2" - -babel-helper-call-delegate@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz#ece6aacddc76e41c3461f88bfc575bd0daa2df8d" - dependencies: - babel-helper-hoist-variables "^6.24.1" - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-define-map@^6.24.1: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz#a5f56dab41a25f97ecb498c7ebaca9819f95be5f" - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-helper-explode-assignable-expression@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz#f25b82cf7dc10433c55f70592d5746400ac22caa" - dependencies: - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-explode-class@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-explode-class/-/babel-helper-explode-class-6.24.1.tgz#7dc2a3910dee007056e1e31d640ced3d54eaa9eb" - dependencies: - babel-helper-bindify-decorators "^6.24.1" - babel-runtime "^6.22.0" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-function-name@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz#d3475b8c03ed98242a25b48351ab18399d3580a9" - dependencies: - babel-helper-get-function-arity "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-get-function-arity@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz#8f7782aa93407c41d3aa50908f89b031b1b6853d" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-hoist-variables@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz#1ecb27689c9d25513eadbc9914a73f5408be7a76" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-optimise-call-expression@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz#f7a13427ba9f73f8f4fa993c54a97882d1244257" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-helper-regex@^6.24.1: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz#325c59f902f82f24b74faceed0363954f6495e72" - dependencies: - babel-runtime "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-helper-remap-async-to-generator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz#5ec581827ad723fecdd381f1c928390676e4551b" - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helper-replace-supers@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz#bf6dbfe43938d17369a213ca8a8bf74b6a90ab1a" - dependencies: - babel-helper-optimise-call-expression "^6.24.1" - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-helpers@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-helpers/-/babel-helpers-6.24.1.tgz#3471de9caec388e5c850e597e58a26ddf37602b2" - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-loader@^7.1.2: - version "7.1.3" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-7.1.3.tgz#ff5b440da716e9153abb946251a9ab7670037b16" - dependencies: - find-cache-dir "^1.0.0" - loader-utils "^1.0.2" - mkdirp "^0.5.1" - -babel-messages@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-messages/-/babel-messages-6.23.0.tgz#f3cdf4703858035b2a2951c6ec5edf6c62f2630e" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-check-es2015-constants@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-syntax-async-functions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz#cad9cad1191b5ad634bf30ae0872391e0647be95" - -babel-plugin-syntax-async-generators@^6.5.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-async-generators/-/babel-plugin-syntax-async-generators-6.13.0.tgz#6bc963ebb16eccbae6b92b596eb7f35c342a8b9a" - -babel-plugin-syntax-class-constructor-call@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-constructor-call/-/babel-plugin-syntax-class-constructor-call-6.18.0.tgz#9cb9d39fe43c8600bec8146456ddcbd4e1a76416" - -babel-plugin-syntax-class-properties@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz#d7eb23b79a317f8543962c505b827c7d6cac27de" - -babel-plugin-syntax-decorators@^6.1.18, babel-plugin-syntax-decorators@^6.13.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-decorators/-/babel-plugin-syntax-decorators-6.13.0.tgz#312563b4dbde3cc806cee3e416cceeaddd11ac0b" - -babel-plugin-syntax-do-expressions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-do-expressions/-/babel-plugin-syntax-do-expressions-6.13.0.tgz#5747756139aa26d390d09410b03744ba07e4796d" - -babel-plugin-syntax-dynamic-import@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-dynamic-import/-/babel-plugin-syntax-dynamic-import-6.18.0.tgz#8d6a26229c83745a9982a441051572caa179b1da" - -babel-plugin-syntax-exponentiation-operator@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz#9ee7e8337290da95288201a6a57f4170317830de" - -babel-plugin-syntax-export-extensions@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-export-extensions/-/babel-plugin-syntax-export-extensions-6.13.0.tgz#70a1484f0f9089a4e84ad44bac353c95b9b12721" - -babel-plugin-syntax-flow@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-flow/-/babel-plugin-syntax-flow-6.18.0.tgz#4c3ab20a2af26aa20cd25995c398c4eb70310c8d" - -babel-plugin-syntax-function-bind@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-function-bind/-/babel-plugin-syntax-function-bind-6.13.0.tgz#48c495f177bdf31a981e732f55adc0bdd2601f46" - -babel-plugin-syntax-jsx@^6.3.13, babel-plugin-syntax-jsx@^6.8.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz#0af32a9a6e13ca7a3fd5069e62d7b0f58d0d8946" - -babel-plugin-syntax-object-rest-spread@^6.8.0: - version "6.13.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5" - -babel-plugin-syntax-trailing-function-commas@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3" - -babel-plugin-transform-async-generator-functions@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-generator-functions/-/babel-plugin-transform-async-generator-functions-6.24.1.tgz#f058900145fd3e9907a6ddf28da59f215258a5db" - dependencies: - babel-helper-remap-async-to-generator "^6.24.1" - babel-plugin-syntax-async-generators "^6.5.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-async-to-generator@^6.22.0, babel-plugin-transform-async-to-generator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz#6536e378aff6cb1d5517ac0e40eb3e9fc8d08761" - dependencies: - babel-helper-remap-async-to-generator "^6.24.1" - babel-plugin-syntax-async-functions "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-class-constructor-call@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-constructor-call/-/babel-plugin-transform-class-constructor-call-6.24.1.tgz#80dc285505ac067dcb8d6c65e2f6f11ab7765ef9" - dependencies: - babel-plugin-syntax-class-constructor-call "^6.18.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-class-properties@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-class-properties/-/babel-plugin-transform-class-properties-6.24.1.tgz#6a79763ea61d33d36f37b611aa9def81a81b46ac" - dependencies: - babel-helper-function-name "^6.24.1" - babel-plugin-syntax-class-properties "^6.8.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-decorators-legacy@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators-legacy/-/babel-plugin-transform-decorators-legacy-1.3.4.tgz#741b58f6c5bce9e6027e0882d9c994f04f366925" - dependencies: - babel-plugin-syntax-decorators "^6.1.18" - babel-runtime "^6.2.0" - babel-template "^6.3.0" - -babel-plugin-transform-decorators@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-decorators/-/babel-plugin-transform-decorators-6.24.1.tgz#788013d8f8c6b5222bdf7b344390dfd77569e24d" - dependencies: - babel-helper-explode-class "^6.24.1" - babel-plugin-syntax-decorators "^6.13.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-do-expressions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-do-expressions/-/babel-plugin-transform-do-expressions-6.22.0.tgz#28ccaf92812d949c2cd1281f690c8fdc468ae9bb" - dependencies: - babel-plugin-syntax-do-expressions "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-arrow-functions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz#452692cb711d5f79dc7f85e440ce41b9f244d221" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-block-scoped-functions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz#bbc51b49f964d70cb8d8e0b94e820246ce3a6141" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-block-scoping@^6.23.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz#d70f5299c1308d05c12f463813b0a09e73b1895f" - dependencies: - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - lodash "^4.17.4" - -babel-plugin-transform-es2015-classes@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz#5a4c58a50c9c9461e564b4b2a3bfabc97a2584db" - dependencies: - babel-helper-define-map "^6.24.1" - babel-helper-function-name "^6.24.1" - babel-helper-optimise-call-expression "^6.24.1" - babel-helper-replace-supers "^6.24.1" - babel-messages "^6.23.0" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-computed-properties@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz#6fe2a8d16895d5634f4cd999b6d3480a308159b3" - dependencies: - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-destructuring@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz#997bb1f1ab967f682d2b0876fe358d60e765c56d" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-duplicate-keys@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz#73eb3d310ca969e3ef9ec91c53741a6f1576423e" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-for-of@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz#f47c95b2b613df1d3ecc2fdb7573623c75248691" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-function-name@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz#834c89853bc36b1af0f3a4c5dbaa94fd8eacaa8b" - dependencies: - babel-helper-function-name "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-literals@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz#4f54a02d6cd66cf915280019a31d31925377ca2e" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-modules-amd@^6.22.0, babel-plugin-transform-es2015-modules-amd@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz#3b3e54017239842d6d19c3011c4bd2f00a00d154" - dependencies: - babel-plugin-transform-es2015-modules-commonjs "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-modules-commonjs@^6.23.0, babel-plugin-transform-es2015-modules-commonjs@^6.24.1: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.0.tgz#0d8394029b7dc6abe1a97ef181e00758dd2e5d8a" - dependencies: - babel-plugin-transform-strict-mode "^6.24.1" - babel-runtime "^6.26.0" - babel-template "^6.26.0" - babel-types "^6.26.0" - -babel-plugin-transform-es2015-modules-systemjs@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz#ff89a142b9119a906195f5f106ecf305d9407d23" - dependencies: - babel-helper-hoist-variables "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-modules-umd@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz#ac997e6285cd18ed6176adb607d602344ad38468" - dependencies: - babel-plugin-transform-es2015-modules-amd "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - -babel-plugin-transform-es2015-object-super@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz#24cef69ae21cb83a7f8603dad021f572eb278f8d" - dependencies: - babel-helper-replace-supers "^6.24.1" - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-parameters@^6.23.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz#57ac351ab49caf14a97cd13b09f66fdf0a625f2b" - dependencies: - babel-helper-call-delegate "^6.24.1" - babel-helper-get-function-arity "^6.24.1" - babel-runtime "^6.22.0" - babel-template "^6.24.1" - babel-traverse "^6.24.1" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-shorthand-properties@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz#24f875d6721c87661bbd99a4622e51f14de38aa0" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-spread@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz#d6d68a99f89aedc4536c81a542e8dd9f1746f8d1" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-sticky-regex@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz#00c1cdb1aca71112cdf0cf6126c2ed6b457ccdbc" - dependencies: - babel-helper-regex "^6.24.1" - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-plugin-transform-es2015-template-literals@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz#a84b3450f7e9f8f1f6839d6d687da84bb1236d8d" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-typeof-symbol@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz#dec09f1cddff94b52ac73d505c84df59dcceb372" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-es2015-unicode-regex@^6.22.0: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz#d38b12f42ea7323f729387f18a7c5ae1faeb35e9" - dependencies: - babel-helper-regex "^6.24.1" - babel-runtime "^6.22.0" - regexpu-core "^2.0.0" - -babel-plugin-transform-exponentiation-operator@^6.22.0, babel-plugin-transform-exponentiation-operator@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz#2ab0c9c7f3098fa48907772bb813fe41e8de3a0e" - dependencies: - babel-helper-builder-binary-assignment-operator-visitor "^6.24.1" - babel-plugin-syntax-exponentiation-operator "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-export-extensions@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-export-extensions/-/babel-plugin-transform-export-extensions-6.22.0.tgz#53738b47e75e8218589eea946cbbd39109bbe653" - dependencies: - babel-plugin-syntax-export-extensions "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-flow-strip-types@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf" - dependencies: - babel-plugin-syntax-flow "^6.18.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-function-bind@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-function-bind/-/babel-plugin-transform-function-bind-6.22.0.tgz#c6fb8e96ac296a310b8cf8ea401462407ddf6a97" - dependencies: - babel-plugin-syntax-function-bind "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-object-rest-spread@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.26.0.tgz#0f36692d50fef6b7e2d4b3ac1478137a963b7b06" - dependencies: - babel-plugin-syntax-object-rest-spread "^6.8.0" - babel-runtime "^6.26.0" - -babel-plugin-transform-react-display-name@^6.23.0: - version "6.25.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-display-name/-/babel-plugin-transform-react-display-name-6.25.0.tgz#67e2bf1f1e9c93ab08db96792e05392bf2cc28d1" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-react-jsx-self@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-self/-/babel-plugin-transform-react-jsx-self-6.22.0.tgz#df6d80a9da2612a121e6ddd7558bcbecf06e636e" - dependencies: - babel-plugin-syntax-jsx "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-react-jsx-source@^6.22.0: - version "6.22.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx-source/-/babel-plugin-transform-react-jsx-source-6.22.0.tgz#66ac12153f5cd2d17b3c19268f4bf0197f44ecd6" - dependencies: - babel-plugin-syntax-jsx "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-react-jsx@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-react-jsx/-/babel-plugin-transform-react-jsx-6.24.1.tgz#840a028e7df460dfc3a2d29f0c0d91f6376e66a3" - dependencies: - babel-helper-builder-react-jsx "^6.24.1" - babel-plugin-syntax-jsx "^6.8.0" - babel-runtime "^6.22.0" - -babel-plugin-transform-regenerator@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz#e0703696fbde27f0a3efcacf8b4dca2f7b3a8f2f" - dependencies: - regenerator-transform "^0.10.0" - -babel-plugin-transform-runtime@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz#88490d446502ea9b8e7efb0fe09ec4d99479b1ee" - dependencies: - babel-runtime "^6.22.0" - -babel-plugin-transform-strict-mode@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz#d5faf7aa578a65bbe591cf5edae04a0c67020758" - dependencies: - babel-runtime "^6.22.0" - babel-types "^6.24.1" - -babel-polyfill@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.26.0.tgz#379937abc67d7895970adc621f284cd966cf2153" - dependencies: - babel-runtime "^6.26.0" - core-js "^2.5.0" - regenerator-runtime "^0.10.5" - -babel-preset-env@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/babel-preset-env/-/babel-preset-env-1.6.1.tgz#a18b564cc9b9afdf4aae57ae3c1b0d99188e6f48" - dependencies: - babel-plugin-check-es2015-constants "^6.22.0" - babel-plugin-syntax-trailing-function-commas "^6.22.0" - babel-plugin-transform-async-to-generator "^6.22.0" - babel-plugin-transform-es2015-arrow-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoped-functions "^6.22.0" - babel-plugin-transform-es2015-block-scoping "^6.23.0" - babel-plugin-transform-es2015-classes "^6.23.0" - babel-plugin-transform-es2015-computed-properties "^6.22.0" - babel-plugin-transform-es2015-destructuring "^6.23.0" - babel-plugin-transform-es2015-duplicate-keys "^6.22.0" - babel-plugin-transform-es2015-for-of "^6.23.0" - babel-plugin-transform-es2015-function-name "^6.22.0" - babel-plugin-transform-es2015-literals "^6.22.0" - babel-plugin-transform-es2015-modules-amd "^6.22.0" - babel-plugin-transform-es2015-modules-commonjs "^6.23.0" - babel-plugin-transform-es2015-modules-systemjs "^6.23.0" - babel-plugin-transform-es2015-modules-umd "^6.23.0" - babel-plugin-transform-es2015-object-super "^6.22.0" - babel-plugin-transform-es2015-parameters "^6.23.0" - babel-plugin-transform-es2015-shorthand-properties "^6.22.0" - babel-plugin-transform-es2015-spread "^6.22.0" - babel-plugin-transform-es2015-sticky-regex "^6.22.0" - babel-plugin-transform-es2015-template-literals "^6.22.0" - babel-plugin-transform-es2015-typeof-symbol "^6.23.0" - babel-plugin-transform-es2015-unicode-regex "^6.22.0" - babel-plugin-transform-exponentiation-operator "^6.22.0" - babel-plugin-transform-regenerator "^6.22.0" - browserslist "^2.1.2" - invariant "^2.2.2" - semver "^5.3.0" - -babel-preset-flow@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-preset-flow/-/babel-preset-flow-6.23.0.tgz#e71218887085ae9a24b5be4169affb599816c49d" - dependencies: - babel-plugin-transform-flow-strip-types "^6.22.0" - -babel-preset-react@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.24.1.tgz#ba69dfaea45fc3ec639b6a4ecea6e17702c91380" - dependencies: - babel-plugin-syntax-jsx "^6.3.13" - babel-plugin-transform-react-display-name "^6.23.0" - babel-plugin-transform-react-jsx "^6.24.1" - babel-plugin-transform-react-jsx-self "^6.22.0" - babel-plugin-transform-react-jsx-source "^6.22.0" - babel-preset-flow "^6.23.0" - -babel-preset-stage-0@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-0/-/babel-preset-stage-0-6.24.1.tgz#5642d15042f91384d7e5af8bc88b1db95b039e6a" - dependencies: - babel-plugin-transform-do-expressions "^6.22.0" - babel-plugin-transform-function-bind "^6.22.0" - babel-preset-stage-1 "^6.24.1" - -babel-preset-stage-1@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-1/-/babel-preset-stage-1-6.24.1.tgz#7692cd7dcd6849907e6ae4a0a85589cfb9e2bfb0" - dependencies: - babel-plugin-transform-class-constructor-call "^6.24.1" - babel-plugin-transform-export-extensions "^6.22.0" - babel-preset-stage-2 "^6.24.1" - -babel-preset-stage-2@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-2/-/babel-preset-stage-2-6.24.1.tgz#d9e2960fb3d71187f0e64eec62bc07767219bdc1" - dependencies: - babel-plugin-syntax-dynamic-import "^6.18.0" - babel-plugin-transform-class-properties "^6.24.1" - babel-plugin-transform-decorators "^6.24.1" - babel-preset-stage-3 "^6.24.1" - -babel-preset-stage-3@^6.24.1: - version "6.24.1" - resolved "https://registry.yarnpkg.com/babel-preset-stage-3/-/babel-preset-stage-3-6.24.1.tgz#836ada0a9e7a7fa37cb138fb9326f87934a48395" - dependencies: - babel-plugin-syntax-trailing-function-commas "^6.22.0" - babel-plugin-transform-async-generator-functions "^6.24.1" - babel-plugin-transform-async-to-generator "^6.24.1" - babel-plugin-transform-exponentiation-operator "^6.24.1" - babel-plugin-transform-object-rest-spread "^6.22.0" - -babel-register@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-register/-/babel-register-6.26.0.tgz#6ed021173e2fcb486d7acb45c6009a856f647071" - dependencies: - babel-core "^6.26.0" - babel-runtime "^6.26.0" - core-js "^2.5.0" - home-or-tmp "^2.0.0" - lodash "^4.17.4" - mkdirp "^0.5.1" - source-map-support "^0.4.15" - -babel-runtime@^6.18.0, babel-runtime@^6.2.0, babel-runtime@^6.22.0, babel-runtime@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" - -babel-template@^6.24.1, babel-template@^6.26.0, babel-template@^6.3.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-template/-/babel-template-6.26.0.tgz#de03e2d16396b069f46dd9fff8521fb1a0e35e02" - dependencies: - babel-runtime "^6.26.0" - babel-traverse "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - lodash "^4.17.4" - -babel-traverse@^6.24.1, babel-traverse@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.26.0.tgz#46a9cbd7edcc62c8e5c064e2d2d8d0f4035766ee" - dependencies: - babel-code-frame "^6.26.0" - babel-messages "^6.23.0" - babel-runtime "^6.26.0" - babel-types "^6.26.0" - babylon "^6.18.0" - debug "^2.6.8" - globals "^9.18.0" - invariant "^2.2.2" - lodash "^4.17.4" - -babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.26.0.tgz#a3b073f94ab49eb6fa55cd65227a334380632497" - dependencies: - babel-runtime "^6.26.0" - esutils "^2.0.2" - lodash "^4.17.4" - to-fast-properties "^1.0.3" - -babylon@7.0.0-beta.40, babylon@^7.0.0-beta.40: - version "7.0.0-beta.40" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.40.tgz#91fc8cd56d5eb98b28e6fde41045f2957779940a" - -babylon@^6.18.0: - version "6.18.0" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" - -balanced-match@^0.4.2: - version "0.4.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - -base64-js@^1.0.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.2.3.tgz#fb13668233d9614cf5fb4bce95a9ba4096cdf801" - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - -bcrypt-pbkdf@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" - dependencies: - tweetnacl "^0.14.3" - -big.js@^3.1.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-3.2.0.tgz#a5fc298b81b9e0dca2e458824784b65c52ba588e" - -binary-extensions@^1.0.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.11.0.tgz#46aa1751fb6a2f93ee5e689bb1087d4b14c6c205" - -"binary@>= 0.3.0 < 1": - version "0.3.0" - resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79" - dependencies: - buffers "~0.1.1" - chainsaw "~0.1.0" - -block-stream@*: - version "0.0.9" - resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" - dependencies: - inherits "~2.0.0" - -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0: - version "4.11.8" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f" - -body-parser@1.18.2: - version "1.18.2" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.18.2.tgz#87678a19d84b47d859b83199bd59bce222b10454" - dependencies: - bytes "3.0.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.1" - http-errors "~1.6.2" - iconv-lite "0.4.19" - on-finished "~2.3.0" - qs "6.5.1" - raw-body "2.3.2" - type-is "~1.6.15" - -bonjour@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" - dependencies: - array-flatten "^2.1.0" - deep-equal "^1.0.1" - dns-equal "^1.0.0" - dns-txt "^2.0.2" - multicast-dns "^6.0.1" - multicast-dns-service-types "^1.1.0" - -boom@2.x.x: - version "2.10.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" - dependencies: - hoek "2.x.x" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^1.8.2: - version "1.8.5" - resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" - dependencies: - expand-range "^1.8.1" - preserve "^0.2.0" - repeat-element "^1.1.2" - -braces@^2.3.0, braces@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.1.tgz#7086c913b4e5a08dbe37ac0ee6a2500c4ba691bb" - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - define-property "^1.0.0" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - kind-of "^6.0.2" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -brcast@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/brcast/-/brcast-3.0.1.tgz#6256a8349b20de9eed44257a9b24d71493cd48dd" - -brorand@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.1.1.tgz#38b7ab55edb806ff2dcda1a7f1620773a477c49f" - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.0.tgz#9988244874bf5ed4e28da95666dcd66ac8fc363a" - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.0.tgz#daa277717470922ed2fe18594118a175439721dd" - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - -browserify-rsa@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.0.1.tgz#21e0abfaf6f2029cf2fafb133567a701d4135524" - dependencies: - bn.js "^4.1.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.0.4.tgz#aa4eb68e5d7b658baa6bf6a57e630cbd7a93d298" - dependencies: - bn.js "^4.1.1" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.2" - elliptic "^6.0.0" - inherits "^2.0.1" - parse-asn1 "^5.0.0" - -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" - dependencies: - pako "~1.0.5" - -browserslist@^1.3.6, browserslist@^1.5.2, browserslist@^1.7.6: - version "1.7.7" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-1.7.7.tgz#0bd76704258be829b2398bb50e4b62d1a166b0b9" - dependencies: - caniuse-db "^1.0.30000639" - electron-to-chromium "^1.2.7" - -browserslist@^2.1.2: - version "2.11.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-2.11.3.tgz#fe36167aed1bbcde4827ebfe71347a2cc70b99b2" - dependencies: - caniuse-lite "^1.0.30000792" - electron-to-chromium "^1.3.30" - -buffer-indexof@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" - -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - -buffer@^4.3.0: - version "4.9.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.1.tgz#6d1bb601b07a4efced97094132093027c95bc298" - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -buffers@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb" - -builtin-modules@^1.0.0, builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -caller-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-0.1.0.tgz#94085ef63581ecd3daa92444a8fe94e82577751f" - dependencies: - callsites "^0.2.0" - -callsites@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-0.2.0.tgz#afab96262910a7f33c19a5775825c69f34e350ca" - -camelcase-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/camelcase-keys/-/camelcase-keys-2.1.0.tgz#308beeaffdf28119051efa1d932213c91b8f92e7" - dependencies: - camelcase "^2.0.0" - map-obj "^1.0.0" - -camelcase@^1.0.2: - version "1.2.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" - -camelcase@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" - -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - -camelcase@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" - -caniuse-api@^1.5.2: - version "1.6.1" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-1.6.1.tgz#b534e7c734c4f81ec5fbe8aca2ad24354b962c6c" - dependencies: - browserslist "^1.3.6" - caniuse-db "^1.0.30000529" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - -caniuse-db@^1.0.30000529, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000811" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000811.tgz#19efb9238393d40078332c34485c818d641c4305" - -caniuse-lite@^1.0.30000792: - version "1.0.30000811" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000811.tgz#0b6e40f2efccc27bd3cb52f91ee7ca4673d77d10" - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - -center-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" - dependencies: - align-text "^0.1.3" - lazy-cache "^1.0.3" - -chain-function@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc" - -chainsaw@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98" - dependencies: - traverse ">=0.3.0 <0.4" - -chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.1.0, chalk@^2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.3.2.tgz#250dc96b07491bfd601e648d66ddf5f60c7a5c65" - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -change-emitter@^0.1.2: - version "0.1.6" - resolved "https://registry.yarnpkg.com/change-emitter/-/change-emitter-0.1.6.tgz#e8b2fe3d7f1ab7d69a32199aff91ea6931409515" - -chardet@^0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" - -charenc@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - -chokidar@^2.0.0, chokidar@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.2.tgz#4dc65139eeb2714977735b6a35d06e97b494dfd7" - dependencies: - anymatch "^2.0.0" - async-each "^1.0.0" - braces "^2.3.0" - glob-parent "^3.1.0" - inherits "^2.0.1" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^2.1.1" - path-is-absolute "^1.0.0" - readdirp "^2.0.0" - upath "^1.0.0" - optionalDependencies: - fsevents "^1.0.0" - -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -circular-json@^0.3.1: - version "0.3.3" - resolved "https://registry.yarnpkg.com/circular-json/-/circular-json-0.3.3.tgz#815c99ea84f6809529d2f45791bdf82711352d66" - -clap@^1.0.9: - version "1.2.3" - resolved "https://registry.yarnpkg.com/clap/-/clap-1.2.3.tgz#4f36745b32008492557f46412d66d50cb99bce51" - dependencies: - chalk "^1.1.3" - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -classnames@2.2.5, classnames@^2.2.5: - version "2.2.5" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d" - -cli-cursor@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5" - dependencies: - restore-cursor "^2.0.0" - -cli-width@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.0.tgz#ff19ede8a9a5e579324147b0c11f0fbcbabed639" - -cliui@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" - dependencies: - center-align "^0.1.1" - right-align "^0.1.1" - wordwrap "0.0.2" - -cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - -clone@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.3.tgz#298d7e2231660f40c003c2ed3140decf3f53085f" - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - -coa@~1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/coa/-/coa-1.0.4.tgz#a9ef153660d6a86a8bdec0289a5c684d217432fd" - dependencies: - q "^1.1.2" - -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.3.0, color-convert@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed" - dependencies: - color-name "^1.1.1" - -color-name@^1.0.0, color-name@^1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - -color-string@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991" - dependencies: - color-name "^1.0.0" - -color@^0.11.0: - version "0.11.4" - resolved "https://registry.yarnpkg.com/color/-/color-0.11.4.tgz#6d7b5c74fb65e841cd48792ad1ed5e07b904d764" - dependencies: - clone "^1.0.2" - color-convert "^1.3.0" - color-string "^0.3.0" - -colormin@^1.0.5: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colormin/-/colormin-1.1.2.tgz#ea2f7420a72b96881a38aae59ec124a6f7298133" - dependencies: - color "^0.11.0" - css-color-names "0.0.4" - has "^1.0.1" - -colors@^1.1.2, colors@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63" - -combined-stream@^1.0.5, combined-stream@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.6.tgz#723e7df6e801ac5613113a7e445a9b69cb632818" - dependencies: - delayed-stream "~1.0.0" - -commander@^2.11.0: - version "2.14.1" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa" - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - -component-emitter@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" - -compressible@~2.0.13: - version "2.0.13" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.13.tgz#0d1020ab924b2fdb4d6279875c7d6daba6baa7a9" - dependencies: - mime-db ">= 1.33.0 < 2" - -compression@^1.5.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69" - dependencies: - accepts "~1.3.4" - bytes "3.0.0" - compressible "~2.0.13" - debug "2.6.9" - on-headers "~1.0.1" - safe-buffer "5.1.1" - vary "~1.1.2" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - -concat-stream@^1.6.0: - version "1.6.1" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.1.tgz#261b8f518301f1d834e36342b9fea095d2620a26" - dependencies: - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -connect-history-api-fallback@^1.3.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.5.0.tgz#b06873934bc5e344fef611a196a6faae0aee015a" - -console-browserify@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.1.0.tgz#f0241c45730a9fc6323b206dbf38edc741d0bb10" - dependencies: - date-now "^0.1.4" - -console-control-strings@^1.0.0, console-control-strings@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - -contains-path@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/contains-path/-/contains-path-0.1.0.tgz#fe8cf184ff6670b6baef01a9d4861a5cbec4120a" - -content-disposition@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.2.tgz#0cf68bb9ddf5f2be7961c3a85178cb85dba78cb4" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - -convert-source-map@^1.5.0: - version "1.5.1" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.5.1.tgz#b8278097b9bc229365de5c62cf5fcaed8b5599e5" - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - -cookie@0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - -core-js@2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.1.tgz#ae6874dc66937789b80754ff5428df66819ca50b" - -core-js@^1.0.0: - version "1.2.7" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636" - -core-js@^2.4.0, core-js@^2.5.0: - version "2.5.3" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.3.tgz#8acc38345824f16d8365b7c9b4259168e8ed603e" - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - -create-ecdh@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.0.tgz#888c723596cdf7612f6498233eebd7a35301737d" - dependencies: - bn.js "^4.1.0" - elliptic "^6.0.0" - -create-hash@^1.1.0, create-hash@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.1.3.tgz#606042ac8b9262750f483caddab0f5819172d8fd" - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - ripemd160 "^2.0.0" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.2, create-hmac@^1.1.4: - version "1.1.6" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.6.tgz#acb9e221a4e17bdb076e90657c42b93e3726cf06" - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -cross-spawn@^5.0.1, cross-spawn@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" - dependencies: - lru-cache "^4.0.1" - shebang-command "^1.2.0" - which "^1.2.9" - -crypt@~0.0.1: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - -cryptiles@2.x.x: - version "2.0.5" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" - dependencies: - boom "2.x.x" - -crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -css-color-names@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - -css-loader@^0.28.9: - version "0.28.10" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-0.28.10.tgz#40282e79230f7bcb4e483efa631d670b735ebf42" - dependencies: - babel-code-frame "^6.26.0" - css-selector-tokenizer "^0.7.0" - cssnano "^3.10.0" - icss-utils "^2.1.0" - loader-utils "^1.0.2" - lodash.camelcase "^4.3.0" - object-assign "^4.1.1" - postcss "^5.0.6" - postcss-modules-extract-imports "^1.2.0" - postcss-modules-local-by-default "^1.2.0" - postcss-modules-scope "^1.1.0" - postcss-modules-values "^1.3.0" - postcss-value-parser "^3.3.0" - source-list-map "^2.0.0" - -css-selector-tokenizer@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.0.tgz#e6988474ae8c953477bf5e7efecfceccd9cf4c86" - dependencies: - cssesc "^0.1.0" - fastparse "^1.1.1" - regexpu-core "^1.0.0" - -css-vendor@^0.3.8: - version "0.3.8" - resolved "https://registry.yarnpkg.com/css-vendor/-/css-vendor-0.3.8.tgz#6421cfd3034ce664fe7673972fd0119fc28941fa" - dependencies: - is-in-browser "^1.0.2" - -cssesc@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-0.1.0.tgz#c814903e45623371a0477b40109aaafbeeaddbb4" - -cssnano@^3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-3.10.0.tgz#4f38f6cea2b9b17fa01490f23f1dc68ea65c1c38" - dependencies: - autoprefixer "^6.3.1" - decamelize "^1.1.2" - defined "^1.0.0" - has "^1.0.1" - object-assign "^4.0.1" - postcss "^5.0.14" - postcss-calc "^5.2.0" - postcss-colormin "^2.1.8" - postcss-convert-values "^2.3.4" - postcss-discard-comments "^2.0.4" - postcss-discard-duplicates "^2.0.1" - postcss-discard-empty "^2.0.1" - postcss-discard-overridden "^0.1.1" - postcss-discard-unused "^2.2.1" - postcss-filter-plugins "^2.0.0" - postcss-merge-idents "^2.1.5" - postcss-merge-longhand "^2.0.1" - postcss-merge-rules "^2.0.3" - postcss-minify-font-values "^1.0.2" - postcss-minify-gradients "^1.0.1" - postcss-minify-params "^1.0.4" - postcss-minify-selectors "^2.0.4" - postcss-normalize-charset "^1.1.0" - postcss-normalize-url "^3.0.7" - postcss-ordered-values "^2.1.0" - postcss-reduce-idents "^2.2.2" - postcss-reduce-initial "^1.0.0" - postcss-reduce-transforms "^1.0.3" - postcss-svgo "^2.1.1" - postcss-unique-selectors "^2.0.2" - postcss-value-parser "^3.2.3" - postcss-zindex "^2.0.1" - -csso@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/csso/-/csso-2.3.2.tgz#ddd52c587033f49e94b71fc55569f252e8ff5f85" - dependencies: - clap "^1.0.9" - source-map "^0.5.3" - -csstype@^1.6.0: - version "1.8.2" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-1.8.2.tgz#2c0f16da08b99f13fe7fbb242e87d1a19dbe77a7" - -currently-unhandled@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" - dependencies: - array-find-index "^1.0.1" - -d3-array@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/d3-array/-/d3-array-1.2.1.tgz#d1ca33de2f6ac31efadb8e050a021d7e2396d5dc" - -d3-collection@1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/d3-collection/-/d3-collection-1.0.4.tgz#342dfd12837c90974f33f1cc0a785aea570dcdc2" - -d3-color@1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/d3-color/-/d3-color-1.0.3.tgz#bc7643fca8e53a8347e2fbdaffa236796b58509b" - -d3-format@1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/d3-format/-/d3-format-1.2.2.tgz#1a39c479c8a57fe5051b2e67a3bee27061a74e7a" - -d3-interpolate@1, d3-interpolate@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/d3-interpolate/-/d3-interpolate-1.1.6.tgz#2cf395ae2381804df08aa1bf766b7f97b5f68fb6" - dependencies: - d3-color "1" - -d3-path@1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/d3-path/-/d3-path-1.0.5.tgz#241eb1849bd9e9e8021c0d0a799f8a0e8e441764" - -d3-scale@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/d3-scale/-/d3-scale-1.0.6.tgz#bce19da80d3a0cf422c9543ae3322086220b34ed" - dependencies: - d3-array "^1.2.0" - d3-collection "1" - d3-color "1" - d3-format "1" - d3-interpolate "1" - d3-time "1" - d3-time-format "2" - -d3-shape@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/d3-shape/-/d3-shape-1.2.0.tgz#45d01538f064bafd05ea3d6d2cb748fd8c41f777" - dependencies: - d3-path "1" - -d3-time-format@2: - version "2.1.1" - resolved "https://registry.yarnpkg.com/d3-time-format/-/d3-time-format-2.1.1.tgz#85b7cdfbc9ffca187f14d3c456ffda268081bb31" - dependencies: - d3-time "1" - -d3-time@1: - version "1.0.8" - resolved "https://registry.yarnpkg.com/d3-time/-/d3-time-1.0.8.tgz#dbd2d6007bf416fe67a76d17947b784bffea1e84" - -d@1: - version "1.0.0" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.0.tgz#754bb5bfe55451da69a58b94d45f4c5b0462d58f" - dependencies: - es5-ext "^0.10.9" - -damerau-levenshtein@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.4.tgz#03191c432cb6eea168bb77f3a55ffdccb8978514" - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - dependencies: - assert-plus "^1.0.0" - -date-now@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/date-now/-/date-now-0.1.4.tgz#eaf439fd4d4848ad74e5cc7dbef200672b9e345b" - -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.6, debug@^2.6.8, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - dependencies: - ms "2.0.0" - -debug@^3.0.1, debug@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - dependencies: - ms "2.0.0" - -decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - -decompress-response@^3.2.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - dependencies: - mimic-response "^1.0.0" - -deep-equal@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - -deep-extend@~0.4.0: - version "0.4.2" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.2.tgz#48b699c27e334bf89f10892be432f6e4c7d34a7f" - -deep-is@~0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" - -deepmerge@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.0.1.tgz#25c1c24f110fb914f80001b925264dd77f3f4312" - -define-properties@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94" - dependencies: - foreach "^2.0.5" - object-keys "^1.0.8" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -defined@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - -del@^2.0.2: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" - dependencies: - globby "^5.0.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - rimraf "^2.2.8" - -del@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/del/-/del-3.0.0.tgz#53ecf699ffcbcb39637691ab13baf160819766e5" - dependencies: - globby "^6.1.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - p-map "^1.1.1" - pify "^3.0.0" - rimraf "^2.2.8" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - -depd@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.1.tgz#5783b4e1c459f06fa5ca27f991f3d06e7a310359" - -depd@~1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - -des.js@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.0.tgz#c074d2e2aa6a8a9a07dbd61f9a15c2cd83ec8ecc" - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - -detect-indent@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" - dependencies: - repeating "^2.0.0" - -detect-libc@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-1.0.3.tgz#fa137c4bd698edf55cd5cd02ac559f91a4c4ba9b" - -detect-node@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.3.tgz#a2033c09cc8e158d37748fbde7507832bd6ce127" - -diffie-hellman@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - -dns-packet@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" - dependencies: - ip "^1.1.0" - safe-buffer "^5.0.1" - -dns-txt@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" - dependencies: - buffer-indexof "^1.0.0" - -doctrine@1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-1.5.0.tgz#379dce730f6166f76cefa4e6707a159b02c5a6fa" - dependencies: - esutils "^2.0.2" - isarray "^1.0.0" - -doctrine@^2.0.2, doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - dependencies: - esutils "^2.0.2" - -dom-helpers@^3.2.0, dom-helpers@^3.2.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.3.1.tgz#fc1a4e15ffdf60ddde03a480a9c0fece821dd4a6" - -dom-walk@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-walk/-/dom-walk-0.1.1.tgz#672226dc74c8f799ad35307df936aba11acd6018" - -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - -ecc-jsbn@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" - dependencies: - jsbn "~0.1.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - -electron-to-chromium@^1.2.7, electron-to-chromium@^1.3.30: - version "1.3.34" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.34.tgz#d93498f40391bb0c16a603d8241b9951404157ed" - -elliptic@^6.0.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.4.0.tgz#cac9af8762c85836187003c8dfe193e5e2eae5df" - dependencies: - bn.js "^4.4.0" - brorand "^1.0.1" - hash.js "^1.0.0" - hmac-drbg "^1.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.0" - -emoji-regex@^6.1.0: - version "6.5.1" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2" - -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - -encodeurl@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - -encoding@^0.1.11: - version "0.1.12" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb" - dependencies: - iconv-lite "~0.4.13" - -enhanced-resolve@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-3.4.1.tgz#0421e339fd71419b3da13d129b3979040230476e" - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.4.0" - object-assign "^4.0.1" - tapable "^0.2.7" - -errno@^0.1.3: - version "0.1.7" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" - dependencies: - prr "~1.0.1" - -error-ex@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.1.tgz#f855a86ce61adc4e8621c3cda21e7a7612c3a8dc" - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.7.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.10.0.tgz#1ecb36c197842a00d8ee4c2dfd8646bb97d60864" - dependencies: - es-to-primitive "^1.1.1" - function-bind "^1.1.1" - has "^1.0.1" - is-callable "^1.1.3" - is-regex "^1.0.4" - -es-to-primitive@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.1.1.tgz#45355248a88979034b6792e19bb81f2b7975dd0d" - dependencies: - is-callable "^1.1.1" - is-date-object "^1.0.1" - is-symbol "^1.0.1" - -es5-ext@^0.10.14, es5-ext@^0.10.35, es5-ext@^0.10.9, es5-ext@~0.10.14: - version "0.10.39" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.39.tgz#fca21b67559277ca4ac1a1ed7048b107b6f76d87" - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.1" - -es6-iterator@^2.0.1, es6-iterator@~2.0.1, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-map@^0.1.3: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-map/-/es6-map-0.1.5.tgz#9136e0503dcc06a301690f0bb14ff4e364e949f0" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-set "~0.1.5" - es6-symbol "~3.1.1" - event-emitter "~0.3.5" - -es6-set@~0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/es6-set/-/es6-set-0.1.5.tgz#d2b3ec5d4d800ced818db538d28974db0a73ccb1" - dependencies: - d "1" - es5-ext "~0.10.14" - es6-iterator "~2.0.1" - es6-symbol "3.1.1" - event-emitter "~0.3.5" - -es6-symbol@3.1.1, es6-symbol@^3.1.1, es6-symbol@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.1.tgz#bf00ef4fdab6ba1b46ecb7b629b4c7ed5715cc77" - dependencies: - d "1" - es5-ext "~0.10.14" - -es6-weak-map@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.2.tgz#5e3ab32251ffd1538a1f8e5ffa1357772f92d96f" - dependencies: - d "1" - es5-ext "^0.10.14" - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - -escope@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/escope/-/escope-3.6.0.tgz#e01975e812781a163a6dadfdd80398dc64c889c3" - dependencies: - es6-map "^0.1.3" - es6-weak-map "^2.0.1" - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-config-airbnb-base@^12.1.0: - version "12.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb-base/-/eslint-config-airbnb-base-12.1.0.tgz#386441e54a12ccd957b0a92564a4bafebd747944" - dependencies: - eslint-restricted-globals "^0.1.1" - -eslint-config-airbnb@^16.1.0: - version "16.1.0" - resolved "https://registry.yarnpkg.com/eslint-config-airbnb/-/eslint-config-airbnb-16.1.0.tgz#2546bfb02cc9fe92284bf1723ccf2e87bc45ca46" - dependencies: - eslint-config-airbnb-base "^12.1.0" - -eslint-import-resolver-node@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.2.tgz#58f15fb839b8d0576ca980413476aab2472db66a" - dependencies: - debug "^2.6.9" - resolve "^1.5.0" - -eslint-loader@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/eslint-loader/-/eslint-loader-2.0.0.tgz#d136619b5c684e36531ffc28c60a56e404608f5d" - dependencies: - loader-fs-cache "^1.0.0" - loader-utils "^1.0.2" - object-assign "^4.0.1" - object-hash "^1.1.4" - rimraf "^2.6.1" - -eslint-module-utils@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.1.1.tgz#abaec824177613b8a95b299639e1b6facf473449" - dependencies: - debug "^2.6.8" - pkg-dir "^1.0.0" - -eslint-plugin-flowtype@^2.41.0: - version "2.46.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.46.1.tgz#c4f81d580cd89c82bc3a85a1ccf4ae3a915143a4" - dependencies: - lodash "^4.15.0" - -eslint-plugin-import@^2.8.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.9.0.tgz#26002efbfca5989b7288ac047508bd24f217b169" - dependencies: - builtin-modules "^1.1.1" - contains-path "^0.1.0" - debug "^2.6.8" - doctrine "1.5.0" - eslint-import-resolver-node "^0.3.1" - eslint-module-utils "^2.1.1" - has "^1.0.1" - lodash "^4.17.4" - minimatch "^3.0.3" - read-pkg-up "^2.0.0" - -eslint-plugin-jsx-a11y@^6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.0.3.tgz#54583d1ae442483162e040e13cc31865465100e5" - dependencies: - aria-query "^0.7.0" - array-includes "^3.0.3" - ast-types-flow "0.0.7" - axobject-query "^0.1.0" - damerau-levenshtein "^1.0.0" - emoji-regex "^6.1.0" - jsx-ast-utils "^2.0.0" - -eslint-plugin-react@^7.5.1: - version "7.7.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.7.0.tgz#f606c719dbd8a1a2b3d25c16299813878cca0160" - dependencies: - doctrine "^2.0.2" - has "^1.0.1" - jsx-ast-utils "^2.0.1" - prop-types "^15.6.0" - -eslint-restricted-globals@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/eslint-restricted-globals/-/eslint-restricted-globals-0.1.1.tgz#35f0d5cbc64c2e3ed62e93b4b1a7af05ba7ed4d7" - -eslint-scope@^3.7.1, eslint-scope@~3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.1.tgz#3d63c3edfda02e06e01a452ad88caacc7cdcb6e8" - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -eslint-visitor-keys@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" - -eslint@^4.16.0: - version "4.18.2" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.18.2.tgz#0f81267ad1012e7d2051e186a9004cc2267b8d45" - dependencies: - ajv "^5.3.0" - babel-code-frame "^6.22.0" - chalk "^2.1.0" - concat-stream "^1.6.0" - cross-spawn "^5.1.0" - debug "^3.1.0" - doctrine "^2.1.0" - eslint-scope "^3.7.1" - eslint-visitor-keys "^1.0.0" - espree "^3.5.2" - esquery "^1.0.0" - esutils "^2.0.2" - file-entry-cache "^2.0.0" - functional-red-black-tree "^1.0.1" - glob "^7.1.2" - globals "^11.0.1" - ignore "^3.3.3" - imurmurhash "^0.1.4" - inquirer "^3.0.6" - is-resolvable "^1.0.0" - js-yaml "^3.9.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.4" - minimatch "^3.0.2" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - path-is-inside "^1.0.2" - pluralize "^7.0.0" - progress "^2.0.0" - require-uncached "^1.0.3" - semver "^5.3.0" - strip-ansi "^4.0.0" - strip-json-comments "~2.0.1" - table "4.0.2" - text-table "~0.2.0" - -espree@^3.5.2: - version "3.5.3" - resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.3.tgz#931e0af64e7fbbed26b050a29daad1fc64799fa6" - dependencies: - acorn "^5.4.0" - acorn-jsx "^3.0.0" - -esprima@^2.6.0: - version "2.7.3" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" - -esprima@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.0.tgz#4499eddcd1110e0b218bacf2fa7f7f59f55ca804" - -esquery@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.0.0.tgz#cfba8b57d7fba93f17298a8a006a04cda13d80fa" - dependencies: - estraverse "^4.0.0" - -esrecurse@^4.1.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.2.1.tgz#007a3b9fdbc2b3bb87e4879ea19c92fdbd3942cf" - dependencies: - estraverse "^4.1.0" - -estraverse@^4.0.0, estraverse@^4.1.0, estraverse@^4.1.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.2.0.tgz#0dee3fed31fcd469618ce7342099fc1afa0bdb13" - -esutils@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - -event-emitter@~0.3.5: - version "0.3.5" - resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" - dependencies: - d "1" - es5-ext "~0.10.14" - -eventemitter3@1.x.x: - version "1.2.0" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-1.2.0.tgz#1c86991d816ad1e504750e73874224ecf3bec508" - -events@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" - -eventsource@0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-0.1.6.tgz#0acede849ed7dd1ccc32c811bb11b944d4f29232" - dependencies: - original ">=0.0.5" - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - -execa@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-0.7.0.tgz#944becd34cc41ee32a63a9faf27ad5a65fc59777" - dependencies: - cross-spawn "^5.0.1" - get-stream "^3.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -expand-brackets@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" - dependencies: - is-posix-bracket "^0.1.0" - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -expand-range@^1.8.1: - version "1.8.2" - resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" - dependencies: - fill-range "^2.1.0" - -express@^4.16.2: - version "4.16.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c" - dependencies: - accepts "~1.3.4" - array-flatten "1.1.1" - body-parser "1.18.2" - content-disposition "0.5.2" - content-type "~1.0.4" - cookie "0.3.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.1" - encodeurl "~1.0.1" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.1.0" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.2" - path-to-regexp "0.1.7" - proxy-addr "~2.0.2" - qs "6.5.1" - range-parser "~1.2.0" - safe-buffer "5.1.1" - send "0.16.1" - serve-static "1.13.1" - setprototypeof "1.1.0" - statuses "~1.3.1" - type-is "~1.6.15" - utils-merge "1.0.1" - vary "~1.1.2" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@~3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" - -external-editor@^2.0.4: - version "2.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.1.0.tgz#3d026a21b7f95b5726387d4200ac160d372c3b48" - dependencies: - chardet "^0.4.0" - iconv-lite "^0.4.17" - tmp "^0.0.33" - -extglob@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" - dependencies: - is-extglob "^1.0.0" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - -fast-deep-equal@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz#c053477817c86b51daa853c81e059b733d023614" - -fast-json-stable-stringify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" - -fast-levenshtein@~2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - -fastparse@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.1.tgz#d1e2643b38a94d7583b479060e6c4affc94071f8" - -faye-websocket@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" - dependencies: - websocket-driver ">=0.5.1" - -faye-websocket@~0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.1.tgz#f0efe18c4f56e4f40afc7e06c719fd5ee6188f38" - dependencies: - websocket-driver ">=0.5.1" - -fbjs@^0.8.1, fbjs@^0.8.16: - version "0.8.16" - resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" - dependencies: - core-js "^1.0.0" - isomorphic-fetch "^2.1.1" - loose-envify "^1.0.0" - object-assign "^4.1.0" - promise "^7.1.1" - setimmediate "^1.0.5" - ua-parser-js "^0.7.9" - -figures@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962" - dependencies: - escape-string-regexp "^1.0.5" - -file-entry-cache@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-2.0.0.tgz#c392990c3e684783d838b8c84a45d8a048458361" - dependencies: - flat-cache "^1.2.1" - object-assign "^4.0.1" - -file-loader@^1.1.6: - version "1.1.11" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-1.1.11.tgz#6fe886449b0f2a936e43cabaac0cdbfb369506f8" - dependencies: - loader-utils "^1.0.2" - schema-utils "^0.4.5" - -filename-regex@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.1.tgz#c1c4b9bee3e09725ddb106b75c1e301fe2f18b26" - -fill-range@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" - dependencies: - is-number "^2.1.0" - isobject "^2.0.0" - randomatic "^1.1.3" - repeat-element "^1.1.2" - repeat-string "^1.5.2" - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -finalhandler@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" - dependencies: - debug "2.6.9" - encodeurl "~1.0.1" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.2" - statuses "~1.3.1" - unpipe "~1.0.0" - -find-cache-dir@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-0.1.1.tgz#c8defae57c8a52a8a784f9e31c57c742e993a0b9" - dependencies: - commondir "^1.0.1" - mkdirp "^0.5.1" - pkg-dir "^1.0.0" - -find-cache-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-1.0.0.tgz#9288e3e9e3cc3748717d39eade17cf71fc30ee6f" - dependencies: - commondir "^1.0.1" - make-dir "^1.0.0" - pkg-dir "^2.0.0" - -find-up@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-1.1.2.tgz#6b2e9822b1a2ce0a60ab64d610eccad53cb24d0f" - dependencies: - path-exists "^2.0.0" - pinkie-promise "^2.0.0" - -find-up@^2.0.0, find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - dependencies: - locate-path "^2.0.0" - -flat-cache@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-1.3.0.tgz#d3030b32b38154f4e3b7e9c709f490f7ef97c481" - dependencies: - circular-json "^0.3.1" - del "^2.0.2" - graceful-fs "^4.1.2" - write "^0.2.1" - -flatten@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.2.tgz#dae46a9d78fbe25292258cc1e780a41d95c03782" - -flow-bin-loader@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/flow-bin-loader/-/flow-bin-loader-1.0.3.tgz#1b95260437bea24786a5abc022ef3efa02df77c5" - -flow-bin@^0.63.1: - version "0.63.1" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.63.1.tgz#ab00067c197169a5fb5b4996c8f6927b06694828" - -flow-typed@^2.2.3: - version "2.3.0" - resolved "https://registry.yarnpkg.com/flow-typed/-/flow-typed-2.3.0.tgz#0f8604faab60691b885024e16ec0e3256e3b680e" - dependencies: - babel-polyfill "^6.26.0" - colors "^1.1.2" - fs-extra "^5.0.0" - github "0.2.4" - glob "^7.1.2" - got "^7.1.0" - md5 "^2.1.0" - mkdirp "^0.5.1" - rimraf "^2.6.2" - semver "^5.5.0" - table "^4.0.2" - through "^2.3.8" - unzip "^0.1.11" - which "^1.3.0" - yargs "^4.2.0" - -font-awesome@^4.3.0: - version "4.7.0" - resolved "https://registry.yarnpkg.com/font-awesome/-/font-awesome-4.7.0.tgz#8fa8cf0411a1a31afd07b06d2902bb9fc815a133" - -for-in@^1.0.1, for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - -for-own@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - dependencies: - for-in "^1.0.1" - -foreach@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - -form-data@~2.1.1: - version "2.1.4" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.5" - mime-types "^2.1.12" - -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - -fs-extra@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - -fsevents@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.3.tgz#11f82318f5fe7bb2cd22965a108e9306208216d8" - dependencies: - nan "^2.3.0" - node-pre-gyp "^0.6.39" - -fstream-ignore@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" - dependencies: - fstream "^1.0.0" - inherits "2" - minimatch "^3.0.0" - -"fstream@>= 0.1.30 < 1": - version "0.1.31" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-0.1.31.tgz#7337f058fbbbbefa8c9f561a28cab0849202c988" - dependencies: - graceful-fs "~3.0.2" - inherits "~2.0.0" - mkdirp "0.5" - rimraf "2" - -fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: - version "1.0.11" - resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" - dependencies: - graceful-fs "^4.1.2" - inherits "~2.0.0" - mkdirp ">=0.5 0" - rimraf "2" - -function-bind@^1.0.2, function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - -gauge@~2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.4.tgz#2c03405c7538c39d7eb37b317022e325fb018bf7" - dependencies: - aproba "^1.0.3" - console-control-strings "^1.0.0" - has-unicode "^2.0.0" - object-assign "^4.1.0" - signal-exit "^3.0.0" - string-width "^1.0.1" - strip-ansi "^3.0.1" - wide-align "^1.1.0" - -get-caller-file@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.2.tgz#f702e63127e7e231c160a80c1554acb70d5047e5" - -get-stdin@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-4.0.1.tgz#b968c6b0a04384324902e8bf1a5df32579a450fe" - -get-stream@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - dependencies: - assert-plus "^1.0.0" - -github@0.2.4: - version "0.2.4" - resolved "https://registry.yarnpkg.com/github/-/github-0.2.4.tgz#24fa7f0e13fa11b946af91134c51982a91ce538b" - dependencies: - mime "^1.2.11" - -glob-base@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" - dependencies: - glob-parent "^2.0.0" - is-glob "^2.0.0" - -glob-parent@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" - dependencies: - is-glob "^2.0.0" - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob@^7.0.3, glob@^7.0.5, glob@^7.1.2: - version "7.1.2" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.2.tgz#c19c9df9a028702d678612384a6552404c636d15" - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global@~4.3.0: - version "4.3.2" - resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f" - dependencies: - min-document "^2.19.0" - process "~0.5.1" - -globals@^11.0.1, globals@^11.1.0: - version "11.3.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.3.0.tgz#e04fdb7b9796d8adac9c8f64c14837b2313378b0" - -globals@^9.18.0: - version "9.18.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" - -globby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" - dependencies: - array-union "^1.0.1" - arrify "^1.0.0" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -globby@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" - dependencies: - array-union "^1.0.1" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -got@^7.1.0: - version "7.1.0" - resolved "https://registry.yarnpkg.com/got/-/got-7.1.0.tgz#05450fd84094e6bbea56f451a43a9c289166385a" - dependencies: - decompress-response "^3.2.0" - duplexer3 "^0.1.4" - get-stream "^3.0.0" - is-plain-obj "^1.1.0" - is-retry-allowed "^1.0.0" - is-stream "^1.0.0" - isurl "^1.0.0-alpha5" - lowercase-keys "^1.0.0" - p-cancelable "^0.3.0" - p-timeout "^1.1.1" - safe-buffer "^5.0.1" - timed-out "^4.0.0" - url-parse-lax "^1.0.0" - url-to-options "^1.0.1" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6: - version "4.1.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" - -graceful-fs@~3.0.2: - version "3.0.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" - dependencies: - natives "^1.1.0" - -handle-thing@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" - -har-schema@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" - -har-validator@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" - dependencies: - ajv "^4.9.1" - har-schema "^1.0.5" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - dependencies: - ansi-regex "^2.0.0" - -has-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" - -has-flag@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51" - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - -has-symbol-support-x@^1.4.1: - version "1.4.2" - resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455" - -has-to-string-tag-x@^1.2.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d" - dependencies: - has-symbol-support-x "^1.4.1" - -has-unicode@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.1.tgz#8461733f538b0837c9361e39a9ab9e9704dc2f28" - dependencies: - function-bind "^1.0.2" - -hash-base@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-2.0.2.tgz#66ea1d856db4e8a5470cadf6fce23ae5244ef2e1" - dependencies: - inherits "^2.0.1" - -hash-base@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918" - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.3.tgz#340dedbe6290187151c1ea1d777a3448935df846" - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.0" - -hawk@3.1.3, hawk@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" - dependencies: - boom "2.x.x" - cryptiles "2.x.x" - hoek "2.x.x" - sntp "1.x.x" - -hmac-drbg@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -hoek@2.x.x: - version "2.16.3" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" - -hoist-non-react-statics@^2.3.1: - version "2.5.0" - resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.0.tgz#d2ca2dfc19c5a91c5a6615ce8e564ef0347e2a40" - -home-or-tmp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/home-or-tmp/-/home-or-tmp-2.0.0.tgz#e36c3f2d2cae7d746a857e38d18d5f32a7882db8" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.1" - -hosted-git-info@^2.1.4: - version "2.5.0" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" - -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" - -html-comment-regex@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.1.tgz#668b93776eaae55ebde8f3ad464b307a4963625e" - -html-entities@^1.2.0: - version "1.2.1" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.2.1.tgz#0df29351f0721163515dfb9e5543e5f6eed5162f" - -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - -http-errors@1.6.2, http-errors@~1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.2.tgz#0a002cc85707192a7e7946ceedc11155f60ec736" - dependencies: - depd "1.1.1" - inherits "2.0.3" - setprototypeof "1.0.3" - statuses ">= 1.3.1 < 2" - -http-parser-js@>=0.4.0: - version "0.4.10" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" - -http-proxy-middleware@~0.17.4: - version "0.17.4" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.17.4.tgz#642e8848851d66f09d4f124912846dbaeb41b833" - dependencies: - http-proxy "^1.16.2" - is-glob "^3.1.0" - lodash "^4.17.2" - micromatch "^2.3.11" - -http-proxy@^1.16.2: - version "1.16.2" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.16.2.tgz#06dff292952bf64dbe8471fa9df73066d4f37742" - dependencies: - eventemitter3 "1.x.x" - requires-port "1.x.x" - -http-signature@~1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" - dependencies: - assert-plus "^0.2.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - -hyphenate-style-name@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.2.tgz#31160a36930adaf1fc04c6074f7eb41465d4ec4b" - -iconv-lite@0.4.19, iconv-lite@^0.4.17, iconv-lite@~0.4.13: - version "0.4.19" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" - -icss-replace-symbols@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/icss-replace-symbols/-/icss-replace-symbols-1.1.0.tgz#06ea6f83679a7749e386cfe1fe812ae5db223ded" - -icss-utils@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-2.1.0.tgz#83f0a0ec378bf3246178b6c2ad9136f135b1c962" - dependencies: - postcss "^6.0.1" - -ieee754@^1.1.4: - version "1.1.8" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.8.tgz#be33d40ac10ef1926701f6f08a2d86fbfd1ad3e4" - -ignore@^3.3.3: - version "3.3.7" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.7.tgz#612289bfb3c220e186a58118618d5be8c1bab021" - -import-local@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc" - dependencies: - pkg-dir "^2.0.0" - resolve-cwd "^2.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - -indent-string@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-2.1.0.tgz#8e2d48348742121b4a8218b7a137e9a52049dc80" - dependencies: - repeating "^2.0.0" - -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - -ini@~1.3.0: - version "1.3.5" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.5.tgz#eee25f56db1c9ec6085e0c22778083f596abf927" - -inquirer@^3.0.6: - version "3.3.0" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" - dependencies: - ansi-escapes "^3.0.0" - chalk "^2.0.0" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^2.0.4" - figures "^2.0.0" - lodash "^4.3.0" - mute-stream "0.0.7" - run-async "^2.2.0" - rx-lite "^4.0.8" - rx-lite-aggregates "^4.0.8" - string-width "^2.1.0" - strip-ansi "^4.0.0" - through "^2.3.6" - -internal-ip@1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-1.2.0.tgz#ae9fbf93b984878785d50a8de1b356956058cf5c" - dependencies: - meow "^3.3.0" - -interpret@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.1.0.tgz#7ed1b1410c6a0e0f78cf95d3b8440c63f78b8614" - -invariant@^2.2.0, invariant@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.3.tgz#1a827dfde7dcbd7c323f0ca826be8fa7c5e9d688" - dependencies: - loose-envify "^1.0.0" - -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - -ip@^1.1.0, ip@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - -ipaddr.js@1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.6.0.tgz#e3fa357b773da619f26e95f049d055c72796f86b" - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - dependencies: - kind-of "^6.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - dependencies: - binary-extensions "^1.0.0" - -is-buffer@^1.1.5, is-buffer@~1.1.1: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - -is-builtin-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-1.0.0.tgz#540572d34f7ac3119f8f76c30cbc1b1e037affbe" - dependencies: - builtin-modules "^1.0.0" - -is-callable@^1.1.1, is-callable@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.3.tgz#86eb75392805ddc33af71c92a0eedf74ee7604b2" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - dependencies: - kind-of "^6.0.0" - -is-date-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-dotfile@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.3.tgz#a6a2f32ffd2dfb04f5ca25ecd0f6b83cf798a1e1" - -is-equal-shallow@^0.1.3: - version "0.1.3" - resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" - dependencies: - is-primitive "^2.0.0" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - -is-finite@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.0.2.tgz#cc6677695602be550ef11e8b4aa6305342b6d0aa" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - dependencies: - number-is-nan "^1.0.0" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - -is-function@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-function/-/is-function-1.0.1.tgz#12cfb98b65b57dd3d193a3121f5f6e2f437602b5" - -is-glob@^2.0.0, is-glob@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" - dependencies: - is-extglob "^1.0.0" - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.0.tgz#9521c76845cc2610a85203ddf080a958c2ffabc0" - dependencies: - is-extglob "^2.1.1" - -is-in-browser@^1.0.2, is-in-browser@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/is-in-browser/-/is-in-browser-1.1.3.tgz#56ff4db683a078c6082eb95dad7dc62e1d04f835" - -is-number@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" - dependencies: - kind-of "^3.0.2" - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - dependencies: - kind-of "^3.0.2" - -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - -is-object@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-object/-/is-object-1.0.1.tgz#8952688c5ec2ffd6b03ecc85e769e02903083470" - -is-odd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-odd/-/is-odd-2.0.0.tgz#7646624671fd7ea558ccd9a2795182f2958f1b24" - dependencies: - is-number "^4.0.0" - -is-path-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" - -is-path-in-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.0.tgz#6477582b8214d602346094567003be8a9eac04dc" - dependencies: - is-path-inside "^1.0.0" - -is-path-inside@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" - dependencies: - path-is-inside "^1.0.1" - -is-plain-obj@^1.0.0, is-plain-obj@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz#71a50c8429dfca773c92a390a4a03b39fcd51d3e" - -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - dependencies: - isobject "^3.0.1" - -is-posix-bracket@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" - -is-primitive@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" - -is-promise@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa" - -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - dependencies: - has "^1.0.1" - -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - -is-retry-allowed@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.1.0.tgz#11a060568b67339444033d0125a61a20d564fb34" - -is-stream@^1.0.0, is-stream@^1.0.1, is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - -is-svg@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-2.1.0.tgz#cf61090da0d9efbcab8722deba6f032208dbb0e9" - dependencies: - html-comment-regex "^1.1.0" - -is-symbol@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - -is-utf8@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" - -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - -isarray@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-0.0.1.tgz#8a18acfca9a8f4177e09abfc6038939b05d1eedf" - -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - -isomorphic-fetch@^2.1.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz#611ae1acf14f5e81f729507472819fe9733558a9" - dependencies: - node-fetch "^1.0.1" - whatwg-fetch ">=0.10.0" - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - -isurl@^1.0.0-alpha5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67" - dependencies: - has-to-string-tag-x "^1.2.0" - is-object "^1.0.1" - -js-base64@^2.1.9: - version "2.4.3" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.4.3.tgz#2e545ec2b0f2957f41356510205214e98fad6582" - -js-tokens@^3.0.0, js-tokens@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" - -js-yaml@^3.9.1: - version "3.10.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.10.0.tgz#2e78441646bd4682e963f22b6e92823c309c62dc" - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@~3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.7.0.tgz#5c967ddd837a9bfdca5f2de84253abe8a1c03b80" - dependencies: - argparse "^1.0.7" - esprima "^2.6.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - -jsesc@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-1.3.0.tgz#46c3fec8c1892b12b0833db9bc7622176dbab34b" - -jsesc@^2.5.1: - version "2.5.1" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.1.tgz#e421a2a8e20d6b0819df28908f782526b96dd1fe" - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - -json-loader@^0.5.4: - version "0.5.7" - resolved "https://registry.yarnpkg.com/json-loader/-/json-loader-0.5.7.tgz#dca14a70235ff82f0ac9a3abeb60d337a365185d" - -json-schema-traverse@^0.3.0: - version "0.3.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz#349a6d44c53a51de89b40805c5d5e59b417d3340" - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - -json-stable-stringify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" - dependencies: - jsonify "~0.0.0" - -json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - -json3@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.2.tgz#3c0434743df93e2f5c42aee7b19bcb483575f4e1" - -json5@^0.5.0, json5@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - optionalDependencies: - graceful-fs "^4.1.6" - -jsonify@~0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -jss-camel-case@^6.0.0, jss-camel-case@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jss-camel-case/-/jss-camel-case-6.1.0.tgz#ccb1ff8d6c701c02a1fed6fb6fb6b7896e11ce44" - dependencies: - hyphenate-style-name "^1.0.2" - -jss-compose@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/jss-compose/-/jss-compose-5.0.0.tgz#ce01b2e4521d65c37ea42cf49116e5f7ab596484" - dependencies: - warning "^3.0.0" - -jss-default-unit@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/jss-default-unit/-/jss-default-unit-8.0.2.tgz#cc1e889bae4c0b9419327b314ab1c8e2826890e6" - -jss-expand@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/jss-expand/-/jss-expand-5.1.0.tgz#b1ad74ec18631f34f65a2124fcfceb6400610e3d" - -jss-extend@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/jss-extend/-/jss-extend-6.2.0.tgz#4af09d0b72fb98ee229970f8ca852fec1ca2a8dc" - dependencies: - warning "^3.0.0" - -jss-global@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/jss-global/-/jss-global-3.0.0.tgz#e19e5c91ab2b96353c227e30aa2cbd938cdaafa2" - -jss-nested@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/jss-nested/-/jss-nested-6.0.1.tgz#ef992b79d6e8f63d939c4397b9d99b5cbbe824ca" - dependencies: - warning "^3.0.0" - -jss-preset-default@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/jss-preset-default/-/jss-preset-default-4.3.0.tgz#7bc91b0b282492557d36ed4e5c6d7c8cb3154bb8" - dependencies: - jss-camel-case "^6.1.0" - jss-compose "^5.0.0" - jss-default-unit "^8.0.2" - jss-expand "^5.1.0" - jss-extend "^6.2.0" - jss-global "^3.0.0" - jss-nested "^6.0.1" - jss-props-sort "^6.0.0" - jss-template "^1.0.1" - jss-vendor-prefixer "^7.0.0" - -jss-props-sort@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/jss-props-sort/-/jss-props-sort-6.0.0.tgz#9105101a3b5071fab61e2d85ea74cc22e9b16323" - -jss-template@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/jss-template/-/jss-template-1.0.1.tgz#09aed9d86cc547b07f53ef355d7e1777f7da430a" - dependencies: - warning "^3.0.0" - -jss-vendor-prefixer@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/jss-vendor-prefixer/-/jss-vendor-prefixer-7.0.0.tgz#0166729650015ef19d9f02437c73667231605c71" - dependencies: - css-vendor "^0.3.8" - -jss@^9.3.2, jss@^9.3.3: - version "9.8.0" - resolved "https://registry.yarnpkg.com/jss/-/jss-9.8.0.tgz#77830def563870103f8671ed31ce3a3d2f32aa2b" - dependencies: - is-in-browser "^1.1.3" - symbol-observable "^1.1.0" - warning "^3.0.0" - -jsx-ast-utils@^2.0.0, jsx-ast-utils@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.0.1.tgz#e801b1b39985e20fffc87b40e3748080e2dcac7f" - dependencies: - array-includes "^3.0.3" - -keycode@^2.1.9: - version "2.1.9" - resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.1.9.tgz#964a23c54e4889405b4861a5c9f0480d45141dfa" - -killable@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.0.tgz#da8b84bd47de5395878f95d64d02f2449fe05e6b" - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - -lazy-cache@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-2.0.2.tgz#b9190a4f913354694840859f8a8f7084d8822264" - dependencies: - set-getter "^0.1.0" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - dependencies: - invert-kv "^1.0.0" - -levn@^0.3.0, levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -load-json-file@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0" - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - pinkie-promise "^2.0.0" - strip-bom "^2.0.0" - -load-json-file@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-2.0.0.tgz#7947e42149af80d696cbf797bcaabcfe1fe29ca8" - dependencies: - graceful-fs "^4.1.2" - parse-json "^2.2.0" - pify "^2.0.0" - strip-bom "^3.0.0" - -loader-fs-cache@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/loader-fs-cache/-/loader-fs-cache-1.0.1.tgz#56e0bf08bd9708b26a765b68509840c8dec9fdbc" - dependencies: - find-cache-dir "^0.1.1" - mkdirp "0.5.1" - -loader-runner@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.3.0.tgz#f482aea82d543e07921700d5a46ef26fdac6b8a2" - -loader-utils@^1.0.2, loader-utils@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.1.0.tgz#c98aef488bcceda2ffb5e2de646d6a754429f5cd" - dependencies: - big.js "^3.1.3" - emojis-list "^2.0.0" - json5 "^0.5.0" - -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - -lodash.assign@^4.0.3, lodash.assign@^4.0.6: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assign/-/lodash.assign-4.2.0.tgz#0d99f3ccd7a6d261d19bdaeb9245005d285808e7" - -lodash.camelcase@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - -lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.3.0, lodash@~4.17.4: - version "4.17.5" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.5.tgz#99a92d65c0272debe8c96b6057bc8fbfa3bed511" - -loglevel@^1.4.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.6.1.tgz#e0fc95133b6ef276cdc8887cdaf24aa6f156f8fa" - -longest@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" - -loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848" - dependencies: - js-tokens "^3.0.0" - -loud-rejection@^1.0.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/loud-rejection/-/loud-rejection-1.6.0.tgz#5b46f80147edee578870f086d04821cf998e551f" - dependencies: - currently-unhandled "^0.4.1" - signal-exit "^3.0.0" - -lowercase-keys@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" - -lru-cache@^4.0.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.1.tgz#622e32e82488b49279114a4f9ecf45e7cd6bba55" - dependencies: - pseudomap "^1.0.2" - yallist "^2.1.2" - -macaddress@^0.2.8: - version "0.2.8" - resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" - -make-dir@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-1.2.0.tgz#6d6a49eead4aae296c53bbf3a1a008bd6c89469b" - dependencies: - pify "^3.0.0" - -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - -map-obj@^1.0.0, map-obj@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/map-obj/-/map-obj-1.0.1.tgz#d933ceb9205d82bdcf4886f6742bdc2b4dea146d" - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - dependencies: - object-visit "^1.0.0" - -"match-stream@>= 0.0.2 < 1": - version "0.0.2" - resolved "https://registry.yarnpkg.com/match-stream/-/match-stream-0.0.2.tgz#99eb050093b34dffade421b9ac0b410a9cfa17cf" - dependencies: - buffers "~0.1.1" - readable-stream "~1.0.0" - -material-ui-icons@^1.0.0-beta.17: - version "1.0.0-beta.35" - resolved "https://registry.yarnpkg.com/material-ui-icons/-/material-ui-icons-1.0.0-beta.35.tgz#f795fafa385918ef7c88ee382e5e5bf4401f008c" - dependencies: - recompose "^0.26.0" - -material-ui@^1.0.0-beta.30: - version "1.0.0-beta.35" - resolved "https://registry.yarnpkg.com/material-ui/-/material-ui-1.0.0-beta.35.tgz#eeac6be307c0469943c7686e5c6bd4eaa6b1b563" - dependencies: - "@types/jss" "^9.3.0" - "@types/react-transition-group" "^2.0.6" - babel-runtime "^6.26.0" - brcast "^3.0.1" - classnames "^2.2.5" - deepmerge "^2.0.1" - dom-helpers "^3.2.1" - hoist-non-react-statics "^2.3.1" - jss "^9.3.3" - jss-camel-case "^6.0.0" - jss-default-unit "^8.0.2" - jss-global "^3.0.0" - jss-nested "^6.0.1" - jss-props-sort "^6.0.0" - jss-vendor-prefixer "^7.0.0" - keycode "^2.1.9" - lodash "^4.2.0" - normalize-scroll-left "^0.1.2" - prop-types "^15.6.0" - react-event-listener "^0.5.1" - react-jss "^8.1.0" - react-popper "^0.8.0" - react-scrollbar-size "^2.0.2" - react-transition-group "^2.2.1" - recompose "^0.26.0" - scroll "^2.0.3" - warning "^3.0.0" - -math-expression-evaluator@^1.2.14: - version "1.2.17" - resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" - -md5.js@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.4.tgz#e9bdbde94a20a5ac18b04340fc5764d5b09d901d" - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -md5@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9" - dependencies: - charenc "~0.0.1" - crypt "~0.0.1" - is-buffer "~1.1.1" - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - -mem@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/mem/-/mem-1.1.0.tgz#5edd52b485ca1d900fe64895505399a0dfa45f76" - dependencies: - mimic-fn "^1.0.0" - -memory-fs@^0.4.0, memory-fs@~0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -meow@^3.3.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/meow/-/meow-3.7.0.tgz#72cb668b425228290abbfa856892587308a801fb" - dependencies: - camelcase-keys "^2.0.0" - decamelize "^1.1.2" - loud-rejection "^1.0.0" - map-obj "^1.0.1" - minimist "^1.1.3" - normalize-package-data "^2.3.4" - object-assign "^4.0.1" - read-pkg-up "^1.0.1" - redent "^1.0.0" - trim-newlines "^1.0.0" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - -micromatch@^2.3.11: - version "2.3.11" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" - dependencies: - arr-diff "^2.0.0" - array-unique "^0.2.1" - braces "^1.8.2" - expand-brackets "^0.1.4" - extglob "^0.3.1" - filename-regex "^2.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.1" - kind-of "^3.0.2" - normalize-path "^2.0.1" - object.omit "^2.0.0" - parse-glob "^3.0.4" - regex-cache "^0.4.2" - -micromatch@^3.1.4: - version "3.1.9" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.9.tgz#15dc93175ae39e52e93087847096effc73efcf89" - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - -"mime-db@>= 1.33.0 < 2", mime-db@~1.33.0: - version "1.33.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" - -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18, mime-types@~2.1.7: - version "2.1.18" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" - dependencies: - mime-db "~1.33.0" - -mime@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" - -mime@^1.2.11, mime@^1.4.1, mime@^1.5.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - -mimic-fn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" - -mimic-response@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.0.tgz#df3d3652a73fded6b9b0b24146e6fd052353458e" - -min-document@^2.19.0: - version "2.19.0" - resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685" - dependencies: - dom-walk "^0.1.0" - -minimalistic-assert@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.0.tgz#702be2dda6b37f4836bcb3f5db56641b64a1d3d3" - -minimalistic-crypto-utils@^1.0.0, minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - -minimatch@^3.0.0, minimatch@^3.0.2, minimatch@^3.0.3, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - dependencies: - brace-expansion "^1.1.7" - -minimist@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" - -minimist@^1.1.3, minimist@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" - -mixin-deep@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@0.5, mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" - dependencies: - minimist "0.0.8" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - -multicast-dns-service-types@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" - -multicast-dns@^6.0.1: - version "6.2.3" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" - dependencies: - dns-packet "^1.3.1" - thunky "^1.0.2" - -mute-stream@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" - -nan@^2.3.0: - version "2.9.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.9.2.tgz#f564d75f5f8f36a6d9456cca7a6c4fe488ab7866" - -nanomatch@^1.2.9: - version "1.2.9" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.9.tgz#879f7150cb2dab7a471259066c104eee6e0fa7c2" - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-odd "^2.0.0" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -natives@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.1.tgz#011acce1f7cbd87f7ba6b3093d6cd9392be1c574" - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - -negotiator@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" - -neo-async@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.5.0.tgz#76b1c823130cca26acfbaccc8fbaf0a2fa33b18f" - -node-fetch@^1.0.1: - version "1.7.3" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-1.7.3.tgz#980f6f72d85211a5347c6b2bc18c5b84c3eb47ef" - dependencies: - encoding "^0.1.11" - is-stream "^1.0.1" - -node-forge@0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.1.tgz#9da611ea08982f4b94206b3beb4cc9665f20c300" - -node-libs-browser@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.1.0.tgz#5f94263d404f6e44767d726901fff05478d600df" - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^1.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.0" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.10.3" - vm-browserify "0.0.4" - -node-pre-gyp@^0.6.39: - version "0.6.39" - resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.39.tgz#c00e96860b23c0e1420ac7befc5044e1d78d8649" - dependencies: - detect-libc "^1.0.2" - hawk "3.1.3" - mkdirp "^0.5.1" - nopt "^4.0.1" - npmlog "^4.0.2" - rc "^1.1.7" - request "2.81.0" - rimraf "^2.6.1" - semver "^5.3.0" - tar "^2.2.1" - tar-pack "^3.4.0" - -nopt@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" - dependencies: - abbrev "1" - osenv "^0.1.4" - -normalize-package-data@^2.3.2, normalize-package-data@^2.3.4: - version "2.4.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" - dependencies: - hosted-git-info "^2.1.4" - is-builtin-module "^1.0.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.0.1, normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - -normalize-scroll-left@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-scroll-left/-/normalize-scroll-left-0.1.2.tgz#6b79691ba79eb5fb107fa5edfbdc06b55caee2aa" - -normalize-url@^1.4.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-1.9.1.tgz#2cc0d66b31ea23036458436e3620d85954c66c3c" - dependencies: - object-assign "^4.0.1" - prepend-http "^1.0.0" - query-string "^4.1.0" - sort-keys "^1.0.0" - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - dependencies: - path-key "^2.0.0" - -npmlog@^4.0.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" - dependencies: - are-we-there-yet "~1.1.2" - console-control-strings "~1.1.0" - gauge "~2.7.3" - set-blocking "~2.0.0" - -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - -oauth-sign@~0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" - -object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-hash@^1.1.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-1.2.0.tgz#e96af0e96981996a1d47f88ead8f74f1ebc4422b" - -object-keys@^1.0.8: - version "1.0.11" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.0.11.tgz#c54601778ad560f1142ce0e01bcca8b56d13426d" - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - dependencies: - isobject "^3.0.0" - -object.omit@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" - dependencies: - for-own "^0.1.4" - is-extendable "^0.1.1" - -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - dependencies: - isobject "^3.0.1" - -obuf@^1.0.0, obuf@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.1.tgz#104124b6c602c6796881a042541d36db43a5264e" - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.1.tgz#928f5d0f470d49342651ea6794b0857c100693f7" - -once@^1.3.0, once@^1.3.3: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - dependencies: - wrappy "1" - -onetime@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4" - dependencies: - mimic-fn "^1.0.0" - -opn@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.2.0.tgz#71fdf934d6827d676cecbea1531f95d354641225" - dependencies: - is-wsl "^1.1.0" - -optionator@^0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.4" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - wordwrap "~1.0.0" - -original@>=0.0.5: - version "1.0.0" - resolved "https://registry.yarnpkg.com/original/-/original-1.0.0.tgz#9147f93fa1696d04be61e01bd50baeaca656bd3b" - dependencies: - url-parse "1.0.x" - -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - dependencies: - lcid "^1.0.0" - -os-locale@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-2.1.0.tgz#42bc2900a6b5b8bd17376c8e882b65afccf24bf2" - dependencies: - execa "^0.7.0" - lcid "^1.0.0" - mem "^1.1.0" - -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - -osenv@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -"over@>= 0.0.5 < 1": - version "0.0.5" - resolved "https://registry.yarnpkg.com/over/-/over-0.0.5.tgz#f29852e70fd7e25f360e013a8ec44c82aedb5708" - -p-cancelable@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.3.0.tgz#b9e123800bcebb7ac13a479be195b507b98d30fa" - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - -p-limit@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.2.0.tgz#0e92b6bedcb59f022c13d0f1949dc82d15909f1c" - dependencies: - p-try "^1.0.0" - -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - dependencies: - p-limit "^1.1.0" - -p-map@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-1.2.0.tgz#e4e94f311eabbc8633a1e79908165fca26241b6b" - -p-timeout@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-1.2.1.tgz#5eb3b353b7fce99f101a1038880bb054ebbea386" - dependencies: - p-finally "^1.0.0" - -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - -pako@~1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.6.tgz#0101211baa70c4bca4a0f63f2206e97b7dfaf258" - -parse-asn1@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.0.tgz#37c4f9b7ed3ab65c74817b5f2480937fbf97c712" - dependencies: - asn1.js "^4.0.0" - browserify-aes "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - -parse-glob@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" - dependencies: - glob-base "^0.3.0" - is-dotfile "^1.0.0" - is-extglob "^1.0.0" - is-glob "^2.0.0" - -parse-json@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" - dependencies: - error-ex "^1.2.0" - -parseurl@~1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.2.tgz#fc289d4ed8993119460c156253262cdc8de65bf3" - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - -path-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.0.tgz#a0b870729aae214005b7d5032ec2cbbb0fb4451a" - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - -path-exists@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-2.1.0.tgz#0feb6c64f0fc518d9a754dd5efb62c7022761f4b" - dependencies: - pinkie-promise "^2.0.0" - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - -path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - -path-is-inside@^1.0.1, path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - -path-key@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - -path-parse@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - -path-type@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" - dependencies: - graceful-fs "^4.1.2" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -path-type@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-2.0.0.tgz#f012ccb8415b7096fc2daa1054c3d72389594c73" - dependencies: - pify "^2.0.0" - -path@^0.12.7: - version "0.12.7" - resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" - dependencies: - process "^0.11.1" - util "^0.10.3" - -pbkdf2@^3.0.3: - version "3.0.14" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.0.14.tgz#a35e13c64799b06ce15320f459c230e68e73bade" - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -performance-now@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - -pify@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - -pkg-dir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-1.0.0.tgz#7a4b508a8d5bb2d629d447056ff4e9c9314cf3d4" - dependencies: - find-up "^1.0.0" - -pkg-dir@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-2.0.0.tgz#f6d5d1109e19d63edf428e0bd57e12777615334b" - dependencies: - find-up "^2.1.0" - -pluralize@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" - -popper.js@^1.12.9: - version "1.12.9" - resolved "https://registry.yarnpkg.com/popper.js/-/popper.js-1.12.9.tgz#0dfbc2dff96c451bb332edcfcfaaf566d331d5b3" - -portfinder@^1.0.9: - version "1.0.13" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.13.tgz#bb32ecd87c27104ae6ee44b5a3ccbf0ebb1aede9" - dependencies: - async "^1.5.2" - debug "^2.2.0" - mkdirp "0.5.x" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - -postcss-calc@^5.2.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-5.3.1.tgz#77bae7ca928ad85716e2fda42f261bf7c1d65b5e" - dependencies: - postcss "^5.0.2" - postcss-message-helpers "^2.0.0" - reduce-css-calc "^1.2.6" - -postcss-colormin@^2.1.8: - version "2.2.2" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-2.2.2.tgz#6631417d5f0e909a3d7ec26b24c8a8d1e4f96e4b" - dependencies: - colormin "^1.0.5" - postcss "^5.0.13" - postcss-value-parser "^3.2.3" - -postcss-convert-values@^2.3.4: - version "2.6.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-2.6.1.tgz#bbd8593c5c1fd2e3d1c322bb925dcae8dae4d62d" - dependencies: - postcss "^5.0.11" - postcss-value-parser "^3.1.2" - -postcss-discard-comments@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-2.0.4.tgz#befe89fafd5b3dace5ccce51b76b81514be00e3d" - dependencies: - postcss "^5.0.14" - -postcss-discard-duplicates@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-2.1.0.tgz#b9abf27b88ac188158a5eb12abcae20263b91932" - dependencies: - postcss "^5.0.4" - -postcss-discard-empty@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-2.1.0.tgz#d2b4bd9d5ced5ebd8dcade7640c7d7cd7f4f92b5" - dependencies: - postcss "^5.0.14" - -postcss-discard-overridden@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-0.1.1.tgz#8b1eaf554f686fb288cd874c55667b0aa3668d58" - dependencies: - postcss "^5.0.16" - -postcss-discard-unused@^2.2.1: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-discard-unused/-/postcss-discard-unused-2.2.3.tgz#bce30b2cc591ffc634322b5fb3464b6d934f4433" - dependencies: - postcss "^5.0.14" - uniqs "^2.0.0" - -postcss-filter-plugins@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-filter-plugins/-/postcss-filter-plugins-2.0.2.tgz#6d85862534d735ac420e4a85806e1f5d4286d84c" - dependencies: - postcss "^5.0.4" - uniqid "^4.0.0" - -postcss-merge-idents@^2.1.5: - version "2.1.7" - resolved "https://registry.yarnpkg.com/postcss-merge-idents/-/postcss-merge-idents-2.1.7.tgz#4c5530313c08e1d5b3bbf3d2bbc747e278eea270" - dependencies: - has "^1.0.1" - postcss "^5.0.10" - postcss-value-parser "^3.1.1" - -postcss-merge-longhand@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-2.0.2.tgz#23d90cd127b0a77994915332739034a1a4f3d658" - dependencies: - postcss "^5.0.4" - -postcss-merge-rules@^2.0.3: - version "2.1.2" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-2.1.2.tgz#d1df5dfaa7b1acc3be553f0e9e10e87c61b5f721" - dependencies: - browserslist "^1.5.2" - caniuse-api "^1.5.2" - postcss "^5.0.4" - postcss-selector-parser "^2.2.2" - vendors "^1.0.0" - -postcss-message-helpers@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-message-helpers/-/postcss-message-helpers-2.0.0.tgz#a4f2f4fab6e4fe002f0aed000478cdf52f9ba60e" - -postcss-minify-font-values@^1.0.2: - version "1.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-1.0.5.tgz#4b58edb56641eba7c8474ab3526cafd7bbdecb69" - dependencies: - object-assign "^4.0.1" - postcss "^5.0.4" - postcss-value-parser "^3.0.2" - -postcss-minify-gradients@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-1.0.5.tgz#5dbda11373703f83cfb4a3ea3881d8d75ff5e6e1" - dependencies: - postcss "^5.0.12" - postcss-value-parser "^3.3.0" - -postcss-minify-params@^1.0.4: - version "1.2.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-1.2.2.tgz#ad2ce071373b943b3d930a3fa59a358c28d6f1f3" - dependencies: - alphanum-sort "^1.0.1" - postcss "^5.0.2" - postcss-value-parser "^3.0.2" - uniqs "^2.0.0" - -postcss-minify-selectors@^2.0.4: - version "2.1.1" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-2.1.1.tgz#b2c6a98c0072cf91b932d1a496508114311735bf" - dependencies: - alphanum-sort "^1.0.2" - has "^1.0.1" - postcss "^5.0.14" - postcss-selector-parser "^2.0.0" - -postcss-modules-extract-imports@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-1.2.0.tgz#66140ecece38ef06bf0d3e355d69bf59d141ea85" - dependencies: - postcss "^6.0.1" - -postcss-modules-local-by-default@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-1.2.0.tgz#f7d80c398c5a393fa7964466bd19500a7d61c069" - dependencies: - css-selector-tokenizer "^0.7.0" - postcss "^6.0.1" - -postcss-modules-scope@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-1.1.0.tgz#d6ea64994c79f97b62a72b426fbe6056a194bb90" - dependencies: - css-selector-tokenizer "^0.7.0" - postcss "^6.0.1" - -postcss-modules-values@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-1.3.0.tgz#ecffa9d7e192518389f42ad0e83f72aec456ea20" - dependencies: - icss-replace-symbols "^1.1.0" - postcss "^6.0.1" - -postcss-normalize-charset@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-1.1.1.tgz#ef9ee71212d7fe759c78ed162f61ed62b5cb93f1" - dependencies: - postcss "^5.0.5" - -postcss-normalize-url@^3.0.7: - version "3.0.8" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-3.0.8.tgz#108f74b3f2fcdaf891a2ffa3ea4592279fc78222" - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^1.4.0" - postcss "^5.0.14" - postcss-value-parser "^3.2.3" - -postcss-ordered-values@^2.1.0: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-2.2.3.tgz#eec6c2a67b6c412a8db2042e77fe8da43f95c11d" - dependencies: - postcss "^5.0.4" - postcss-value-parser "^3.0.1" - -postcss-reduce-idents@^2.2.2: - version "2.4.0" - resolved "https://registry.yarnpkg.com/postcss-reduce-idents/-/postcss-reduce-idents-2.4.0.tgz#c2c6d20cc958284f6abfbe63f7609bf409059ad3" - dependencies: - postcss "^5.0.4" - postcss-value-parser "^3.0.2" - -postcss-reduce-initial@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-1.0.1.tgz#68f80695f045d08263a879ad240df8dd64f644ea" - dependencies: - postcss "^5.0.4" - -postcss-reduce-transforms@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-1.0.4.tgz#ff76f4d8212437b31c298a42d2e1444025771ae1" - dependencies: - has "^1.0.1" - postcss "^5.0.8" - postcss-value-parser "^3.0.1" - -postcss-selector-parser@^2.0.0, postcss-selector-parser@^2.2.2: - version "2.2.3" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-2.2.3.tgz#f9437788606c3c9acee16ffe8d8b16297f27bb90" - dependencies: - flatten "^1.0.2" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-svgo@^2.1.1: - version "2.1.6" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-2.1.6.tgz#b6df18aa613b666e133f08adb5219c2684ac108d" - dependencies: - is-svg "^2.0.0" - postcss "^5.0.14" - postcss-value-parser "^3.2.3" - svgo "^0.7.0" - -postcss-unique-selectors@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-2.0.2.tgz#981d57d29ddcb33e7b1dfe1fd43b8649f933ca1d" - dependencies: - alphanum-sort "^1.0.1" - postcss "^5.0.4" - uniqs "^2.0.0" - -postcss-value-parser@^3.0.1, postcss-value-parser@^3.0.2, postcss-value-parser@^3.1.1, postcss-value-parser@^3.1.2, postcss-value-parser@^3.2.3, postcss-value-parser@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.0.tgz#87f38f9f18f774a4ab4c8a232f5c5ce8872a9d15" - -postcss-zindex@^2.0.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-zindex/-/postcss-zindex-2.2.0.tgz#d2109ddc055b91af67fc4cb3b025946639d2af22" - dependencies: - has "^1.0.1" - postcss "^5.0.4" - uniqs "^2.0.0" - -postcss@^5.0.10, postcss@^5.0.11, postcss@^5.0.12, postcss@^5.0.13, postcss@^5.0.14, postcss@^5.0.16, postcss@^5.0.2, postcss@^5.0.4, postcss@^5.0.5, postcss@^5.0.6, postcss@^5.0.8, postcss@^5.2.16: - version "5.2.18" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-5.2.18.tgz#badfa1497d46244f6390f58b319830d9107853c5" - dependencies: - chalk "^1.1.3" - js-base64 "^2.1.9" - source-map "^0.5.6" - supports-color "^3.2.3" - -postcss@^6.0.1: - version "6.0.19" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-6.0.19.tgz#76a78386f670b9d9494a655bf23ac012effd1555" - dependencies: - chalk "^2.3.1" - source-map "^0.6.1" - supports-color "^5.2.0" - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - -prepend-http@^1.0.0, prepend-http@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc" - -preserve@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" - -private@^0.1.6, private@^0.1.7: - version "0.1.8" - resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" - -process-nextick-args@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" - -process@^0.11.1, process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - -process@~0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf" - -progress@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" - -promise@^7.1.1: - version "7.3.1" - resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" - dependencies: - asap "~2.0.3" - -prop-types@^15.5.10, prop-types@^15.5.8, prop-types@^15.6.0: - version "15.6.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.1.tgz#36644453564255ddda391191fb3a125cbdf654ca" - dependencies: - fbjs "^0.8.16" - loose-envify "^1.3.1" - object-assign "^4.1.1" - -proxy-addr@~2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.3.tgz#355f262505a621646b3130a728eb647e22055341" - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.6.0" - -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" - -pseudomap@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" - -public-encrypt@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.0.tgz#39f699f3a46560dd5ebacbca693caf7c65c18cc6" - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - -"pullstream@>= 0.4.1 < 1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/pullstream/-/pullstream-0.4.1.tgz#d6fb3bf5aed697e831150eb1002c25a3f8ae1314" - dependencies: - over ">= 0.0.5 < 1" - readable-stream "~1.0.31" - setimmediate ">= 1.0.2 < 2" - slice-stream ">= 1.0.0 < 2" - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - -punycode@^1.2.4, punycode@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - -q@^1.1.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - -qs@6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" - -qs@~6.4.0: - version "6.4.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" - -query-string@^4.1.0: - version "4.3.4" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.3.4.tgz#bbb693b9ca915c232515b228b1a02b609043dbeb" - dependencies: - object-assign "^4.1.0" - strict-uri-encode "^1.0.0" - -querystring-es3@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - -querystringify@0.0.x: - version "0.0.4" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-0.0.4.tgz#0cf7f84f9463ff0ae51c4c4b142d95be37724d9c" - -querystringify@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-1.0.0.tgz#6286242112c5b712fa654e526652bf6a13ff05cb" - -raf@^3.2.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.0.tgz#a28876881b4bc2ca9117d4138163ddb80f781575" - dependencies: - performance-now "^2.1.0" - -rafl@~1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/rafl/-/rafl-1.2.2.tgz#fe930f758211020d47e38815f5196a8be4150740" - dependencies: - global "~4.3.0" - -randomatic@^1.1.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.7.tgz#c7abe9cc8b87c0baa876b19fde83fd464797e38c" - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.0.6.tgz#d302c522948588848a8d300c932b44c24231da80" - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@^1.0.3, range-parser@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.0.tgz#f49be6b487894ddc40dcc94a322f611092e00d5e" - -raw-body@2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.3.2.tgz#bcd60c77d3eb93cde0050295c3f379389bc88f89" - dependencies: - bytes "3.0.0" - http-errors "1.6.2" - iconv-lite "0.4.19" - unpipe "1.0.0" - -rc@^1.1.7: - version "1.2.5" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.5.tgz#275cd687f6e3b36cc756baa26dfee80a790301fd" - dependencies: - deep-extend "~0.4.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -react-dom@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044" - dependencies: - fbjs "^0.8.16" - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.0" - -react-event-listener@^0.5.1: - version "0.5.3" - resolved "https://registry.yarnpkg.com/react-event-listener/-/react-event-listener-0.5.3.tgz#a8b492596ad601865314fcc2c18cb87b6ce3876e" - dependencies: - babel-runtime "^6.26.0" - fbjs "^0.8.16" - prop-types "^15.6.0" - warning "^3.0.0" - -react-fa@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/react-fa/-/react-fa-5.0.0.tgz#d571732856c6cb2c155c46daef018ba67a75b973" - dependencies: - font-awesome "^4.3.0" - prop-types "^15.5.8" - -react-jss@^8.1.0: - version "8.3.3" - resolved "https://registry.yarnpkg.com/react-jss/-/react-jss-8.3.3.tgz#677a57569d3e4f5099fcdeafeddd8d2c62ab5977" - dependencies: - hoist-non-react-statics "^2.3.1" - jss "^9.3.2" - jss-preset-default "^4.3.0" - prop-types "^15.6.0" - theming "^1.3.0" - -react-popper@^0.8.0: - version "0.8.2" - resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-0.8.2.tgz#092095ff13933211d3856d9f325511ec3a42f12c" - dependencies: - popper.js "^1.12.9" - prop-types "^15.6.0" - -react-resize-detector@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/react-resize-detector/-/react-resize-detector-1.1.0.tgz#4a9831fa3caad32230478dd0185cbd2aa91a5ebf" - dependencies: - prop-types "^15.5.10" - -react-scrollbar-size@^2.0.2: - version "2.1.0" - resolved "https://registry.yarnpkg.com/react-scrollbar-size/-/react-scrollbar-size-2.1.0.tgz#105e797135cab92b1f9e16f00071db7f29f80754" - dependencies: - babel-runtime "^6.26.0" - prop-types "^15.6.0" - react-event-listener "^0.5.1" - stifle "^1.0.2" - -react-smooth@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-1.0.0.tgz#b29dbebddddb06d21b5b08962167fb9eac1897d8" - dependencies: - lodash "~4.17.4" - prop-types "^15.6.0" - raf "^3.2.0" - react-transition-group "^2.2.1" - -react-transition-group@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.2.1.tgz#e9fb677b79e6455fd391b03823afe84849df4a10" - dependencies: - chain-function "^1.0.0" - classnames "^2.2.5" - dom-helpers "^3.2.0" - loose-envify "^1.3.1" - prop-types "^15.5.8" - warning "^3.0.0" - -react@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba" - dependencies: - fbjs "^0.8.16" - loose-envify "^1.1.0" - object-assign "^4.1.1" - prop-types "^15.6.0" - -read-pkg-up@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-1.0.1.tgz#9d63c13276c065918d57f002a57f40a1b643fb02" - dependencies: - find-up "^1.0.0" - read-pkg "^1.0.0" - -read-pkg-up@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-2.0.0.tgz#6b72a8048984e0c41e79510fd5e9fa99b3b549be" - dependencies: - find-up "^2.0.0" - read-pkg "^2.0.0" - -read-pkg@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-1.1.0.tgz#f5ffaa5ecd29cb31c0474bca7d756b6bb29e3f28" - dependencies: - load-json-file "^1.0.0" - normalize-package-data "^2.3.2" - path-type "^1.0.0" - -read-pkg@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-2.0.0.tgz#8ef1c0623c6a6db0dc6713c4bfac46332b2368f8" - dependencies: - load-json-file "^2.0.0" - normalize-package-data "^2.3.2" - path-type "^2.0.0" - -readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.3.3: - version "2.3.4" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.4.tgz#c946c3f47fa7d8eabc0b6150f4a12f69a4574071" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.0.3" - util-deprecate "~1.0.1" - -readable-stream@^2.2.9: - version "2.3.5" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.5.tgz#b4f85003a938cbb6ecbce2a124fb1012bd1a838d" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.0.3" - util-deprecate "~1.0.1" - -readable-stream@~1.0.0, readable-stream@~1.0.31: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readdirp@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" - dependencies: - graceful-fs "^4.1.2" - minimatch "^3.0.2" - readable-stream "^2.0.2" - set-immediate-shim "^1.0.1" - -recharts-scale@0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/recharts-scale/-/recharts-scale-0.3.2.tgz#dac7621714a4765d152cb2adbc30c73b831208c9" - -recharts@^1.0.0-beta.9: - version "1.0.0-beta.10" - resolved "https://registry.yarnpkg.com/recharts/-/recharts-1.0.0-beta.10.tgz#d3cd15df6b7879d5968da3c942b5fcdaf2504fe1" - dependencies: - classnames "2.2.5" - core-js "2.5.1" - d3-interpolate "^1.1.5" - d3-scale "1.0.6" - d3-shape "1.2.0" - lodash "~4.17.4" - prop-types "^15.6.0" - react-resize-detector "1.1.0" - react-smooth "1.0.0" - recharts-scale "0.3.2" - reduce-css-calc "1.3.0" - -recompose@^0.26.0: - version "0.26.0" - resolved "https://registry.yarnpkg.com/recompose/-/recompose-0.26.0.tgz#9babff039cb72ba5bd17366d55d7232fbdfb2d30" - dependencies: - change-emitter "^0.1.2" - fbjs "^0.8.1" - hoist-non-react-statics "^2.3.1" - symbol-observable "^1.0.4" - -redent@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde" - dependencies: - indent-string "^2.1.0" - strip-indent "^1.0.1" - -reduce-css-calc@1.3.0, reduce-css-calc@^1.2.6: - version "1.3.0" - resolved "https://registry.yarnpkg.com/reduce-css-calc/-/reduce-css-calc-1.3.0.tgz#747c914e049614a4c9cfbba629871ad1d2927716" - dependencies: - balanced-match "^0.4.2" - math-expression-evaluator "^1.2.14" - reduce-function-call "^1.0.1" - -reduce-function-call@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/reduce-function-call/-/reduce-function-call-1.0.2.tgz#5a200bf92e0e37751752fe45b0ab330fd4b6be99" - dependencies: - balanced-match "^0.4.2" - -regenerate@^1.2.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.3.3.tgz#0c336d3980553d755c39b586ae3b20aa49c82b7f" - -regenerator-runtime@^0.10.5: - version "0.10.5" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.5.tgz#336c3efc1220adcedda2c9fab67b5a7955a33658" - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - -regenerator-transform@^0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd" - dependencies: - babel-runtime "^6.18.0" - babel-types "^6.19.0" - private "^0.1.6" - -regex-cache@^0.4.2: - version "0.4.4" - resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.4.tgz#75bdc58a2a1496cec48a12835bc54c8d562336dd" - dependencies: - is-equal-shallow "^0.1.3" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regexpu-core@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-1.0.0.tgz#86a763f58ee4d7c2f6b102e4764050de7ed90c6b" - dependencies: - regenerate "^1.2.1" - regjsgen "^0.2.0" - regjsparser "^0.1.4" - -regexpu-core@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" - dependencies: - regenerate "^1.2.1" - regjsgen "^0.2.0" - regjsparser "^0.1.4" - -regjsgen@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.2.0.tgz#6c016adeac554f75823fe37ac05b92d5a4edb1f7" - -regjsparser@^0.1.4: - version "0.1.5" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.1.5.tgz#7ee8f84dc6fa792d3fd0ae228d24bd949ead205c" - dependencies: - jsesc "~0.5.0" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - -repeat-element@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" - -repeat-string@^1.5.2, repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - -repeating@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/repeating/-/repeating-2.0.1.tgz#5214c53a926d3552707527fbab415dbc08d06dda" - dependencies: - is-finite "^1.0.0" - -request@2.81.0: - version "2.81.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" - dependencies: - aws-sign2 "~0.6.0" - aws4 "^1.2.1" - caseless "~0.12.0" - combined-stream "~1.0.5" - extend "~3.0.0" - forever-agent "~0.6.1" - form-data "~2.1.1" - har-validator "~4.2.1" - hawk "~3.1.3" - http-signature "~1.1.0" - is-typedarray "~1.0.0" - isstream "~0.1.2" - json-stringify-safe "~5.0.1" - mime-types "~2.1.7" - oauth-sign "~0.8.1" - performance-now "^0.2.0" - qs "~6.4.0" - safe-buffer "^5.0.1" - stringstream "~0.0.4" - tough-cookie "~2.3.0" - tunnel-agent "^0.6.0" - uuid "^3.0.0" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - -require-uncached@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" - dependencies: - caller-path "^0.1.0" - resolve-from "^1.0.0" - -requires-port@1.0.x, requires-port@1.x.x, requires-port@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" - dependencies: - resolve-from "^3.0.0" - -resolve-from@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-1.0.1.tgz#26cbfe935d1aeeeabb29bc3fe5aeb01e93d44226" - -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - -resolve@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.5.0.tgz#1f09acce796c9a762579f31b2c1cc4c3cddf9f36" - dependencies: - path-parse "^1.0.5" - -restore-cursor@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf" - dependencies: - onetime "^2.0.0" - signal-exit "^3.0.2" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - -right-align@^0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" - dependencies: - align-text "^0.1.1" - -rimraf@2, rimraf@^2.2.8, rimraf@^2.5.1, rimraf@^2.6.1, rimraf@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.2.tgz#2ed8150d24a16ea8651e6d6ef0f47c4158ce7a36" - dependencies: - glob "^7.0.5" - -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.1.tgz#0f4584295c53a3628af7e6d79aca21ce57d1c6e7" - dependencies: - hash-base "^2.0.0" - inherits "^2.0.1" - -run-async@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" - dependencies: - is-promise "^2.1.0" - -rx-lite-aggregates@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" - dependencies: - rx-lite "*" - -rx-lite@*, rx-lite@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" - -safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - dependencies: - ret "~0.1.10" - -sax@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - -schema-utils@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.3.0.tgz#f5877222ce3e931edae039f17eb3716e7137f8cf" - dependencies: - ajv "^5.0.0" - -schema-utils@^0.4.5: - version "0.4.5" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.5.tgz#21836f0608aac17b78f9e3e24daff14a5ca13a3e" - dependencies: - ajv "^6.1.0" - ajv-keywords "^3.1.0" - -scroll@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/scroll/-/scroll-2.0.3.tgz#0951b785544205fd17753bc3d294738ba16fc2ab" - dependencies: - rafl "~1.2.1" - -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - -selfsigned@^1.9.1: - version "1.10.2" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.2.tgz#b4449580d99929b65b10a48389301a6592088758" - dependencies: - node-forge "0.7.1" - -"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab" - -send@0.16.1: - version "0.16.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3" - dependencies: - debug "2.6.9" - depd "~1.1.1" - destroy "~1.0.4" - encodeurl "~1.0.1" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.3.1" - -serve-index@^1.7.2: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - dependencies: - accepts "~1.3.4" - batch "0.6.1" - debug "2.6.9" - escape-html "~1.0.3" - http-errors "~1.6.2" - mime-types "~2.1.17" - parseurl "~1.3.2" - -serve-static@1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.1.tgz#4c57d53404a761d8f2e7c1e8a18a47dbf278a719" - dependencies: - encodeurl "~1.0.1" - escape-html "~1.0.3" - parseurl "~1.3.2" - send "0.16.1" - -set-blocking@^2.0.0, set-blocking@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - -set-getter@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/set-getter/-/set-getter-0.1.0.tgz#d769c182c9d5a51f409145f2fba82e5e86e80376" - dependencies: - to-object-path "^0.3.0" - -set-immediate-shim@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - -set-value@^0.4.3: - version "0.4.3" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-0.4.3.tgz#7db08f9d3d22dc7f78e53af3c3bf4666ecdfccf1" - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.1" - to-object-path "^0.3.0" - -set-value@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.0.tgz#71ae4a88f0feefbbf52d1ea604f3fb315ebb6274" - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -"setimmediate@>= 1.0.1 < 2", "setimmediate@>= 1.0.2 < 2", setimmediate@^1.0.4, setimmediate@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - -setprototypeof@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.0.3.tgz#66567e37043eeb4f04d91bd658c0cbefb55b8e04" - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.10" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.10.tgz#b1fde5cd7d11a5626638a07c604ab909cfa31f9b" - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" - -slash@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-1.0.0.tgz#c41f2f6c39fc16d1cd17ad4b5d896114ae470d55" - -slice-ansi@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" - dependencies: - is-fullwidth-code-point "^2.0.0" - -"slice-stream@>= 1.0.0 < 2": - version "1.0.0" - resolved "https://registry.yarnpkg.com/slice-stream/-/slice-stream-1.0.0.tgz#5b33bd66f013b1a7f86460b03d463dec39ad3ea0" - dependencies: - readable-stream "~1.0.31" - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.1.tgz#e12b5487faded3e3dea0ac91e9400bf75b401370" - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^2.0.0" - -sntp@1.x.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" - dependencies: - hoek "2.x.x" - -sockjs-client@1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.4.tgz#5babe386b775e4cf14e7520911452654016c8b12" - dependencies: - debug "^2.6.6" - eventsource "0.1.6" - faye-websocket "~0.11.0" - inherits "^2.0.1" - json3 "^3.3.2" - url-parse "^1.1.8" - -sockjs@0.3.19: - version "0.3.19" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.19.tgz#d976bbe800af7bd20ae08598d582393508993c0d" - dependencies: - faye-websocket "^0.10.0" - uuid "^3.0.1" - -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" - dependencies: - is-plain-obj "^1.0.0" - -source-list-map@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.0.tgz#aaa47403f7b245a92fbc97ea08f250d6087ed085" - -source-map-resolve@^0.5.0: - version "0.5.1" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.1.tgz#7ad0f593f2281598e854df80f19aae4b92d7a11a" - dependencies: - atob "^2.0.0" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-support@^0.4.15: - version "0.4.18" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" - dependencies: - source-map "^0.5.6" - -source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" - -source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - -source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - -spdx-correct@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.0.0.tgz#05a5b4d7153a195bc92c3c425b69f3b2a9524c82" - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.1.0.tgz#2c7ae61056c714a5b9b9b2b2af7d311ef5c78fe9" - -spdx-expression-parse@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz#99e119b7a5da00e05491c9fa338b7904823b41d0" - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.0.tgz#7a7cd28470cc6d3a1cfe6d66886f6bc430d3ac87" - -spdy-transport@^2.0.18: - version "2.0.20" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-2.0.20.tgz#735e72054c486b2354fe89e702256004a39ace4d" - dependencies: - debug "^2.6.8" - detect-node "^2.0.3" - hpack.js "^2.1.6" - obuf "^1.1.1" - readable-stream "^2.2.9" - safe-buffer "^5.0.1" - wbuf "^1.7.2" - -spdy@^3.4.1: - version "3.4.7" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-3.4.7.tgz#42ff41ece5cc0f99a3a6c28aabb73f5c3b03acbc" - dependencies: - debug "^2.6.8" - handle-thing "^1.2.5" - http-deceiver "^1.2.7" - safe-buffer "^5.0.1" - select-hose "^2.0.0" - spdy-transport "^2.0.18" - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - dependencies: - extend-shallow "^3.0.0" - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - -sshpk@^1.7.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.13.1.tgz#512df6da6287144316dc4c18fe1cf1d940739be3" - dependencies: - asn1 "~0.2.3" - assert-plus "^1.0.0" - dashdash "^1.12.0" - getpass "^0.1.1" - optionalDependencies: - bcrypt-pbkdf "^1.0.0" - ecc-jsbn "~0.1.1" - jsbn "~0.1.0" - tweetnacl "~0.14.0" - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.3.1 < 2": - version "1.4.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.4.0.tgz#bb73d446da2796106efcc1b601a253d6c46bd087" - -statuses@~1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.3.1.tgz#faf51b9eb74aaef3b3acf4ad5f61abf24cb7b93e" - -stifle@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/stifle/-/stifle-1.0.4.tgz#8b3bcdf52419b0a9c79e35adadce50123c1d8e99" - -stream-browserify@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.1.tgz#66266ee5f9bdb9940a4e4514cafb43bb71e5c9db" - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-http@^2.7.2: - version "2.8.0" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.0.tgz#fd86546dac9b1c91aff8fc5d287b98fafb41bc10" - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.3" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -strict-uri-encode@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713" - -string-width@^1.0.1, string-width@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - -string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" - dependencies: - is-fullwidth-code-point "^2.0.0" - strip-ansi "^4.0.0" - -string_decoder@^1.0.0, string_decoder@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" - dependencies: - safe-buffer "~5.1.0" - -string_decoder@~0.10.x: - version "0.10.31" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" - -stringstream@~0.0.4: - version "0.0.5" - resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" - dependencies: - ansi-regex "^3.0.0" - -strip-bom@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-2.0.0.tgz#6219a85616520491f35788bdbf1447a99c7e6b0e" - dependencies: - is-utf8 "^0.2.0" - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - -strip-indent@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-1.0.1.tgz#0c7962a6adefa7bbd4ac366460a638552ae1a0a2" - dependencies: - get-stdin "^4.0.1" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" - -style-loader@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.19.1.tgz#591ffc80bcefe268b77c5d9ebc0505d772619f85" - dependencies: - loader-utils "^1.0.2" - schema-utils "^0.3.0" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - -supports-color@^3.2.3: - version "3.2.3" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" - dependencies: - has-flag "^1.0.0" - -supports-color@^4.2.1: - version "4.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-4.5.0.tgz#be7a0de484dec5c5cddf8b3d59125044912f635b" - dependencies: - has-flag "^2.0.0" - -supports-color@^5.1.0, supports-color@^5.2.0, supports-color@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.3.0.tgz#5b24ac15db80fa927cf5227a4a33fd3c4c7676c0" - dependencies: - has-flag "^3.0.0" - -svgo@^0.7.0: - version "0.7.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-0.7.2.tgz#9f5772413952135c6fefbf40afe6a4faa88b4bb5" - dependencies: - coa "~1.0.1" - colors "~1.1.2" - csso "~2.3.1" - js-yaml "~3.7.0" - mkdirp "~0.5.1" - sax "~1.2.1" - whet.extend "~0.9.9" - -symbol-observable@^1.0.4, symbol-observable@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" - -table@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" - dependencies: - ajv "^5.2.3" - ajv-keywords "^2.1.0" - chalk "^2.1.0" - lodash "^4.17.4" - slice-ansi "1.0.0" - string-width "^2.1.1" - -table@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/table/-/table-4.0.3.tgz#00b5e2b602f1794b9acaf9ca908a76386a7813bc" - dependencies: - ajv "^6.0.1" - ajv-keywords "^3.0.0" - chalk "^2.1.0" - lodash "^4.17.4" - slice-ansi "1.0.0" - string-width "^2.1.1" - -tapable@^0.2.7: - version "0.2.8" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-0.2.8.tgz#99372a5c999bf2df160afc0d74bed4f47948cd22" - -tar-pack@^3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.1.tgz#e1dbc03a9b9d3ba07e896ad027317eb679a10a1f" - dependencies: - debug "^2.2.0" - fstream "^1.0.10" - fstream-ignore "^1.0.5" - once "^1.3.3" - readable-stream "^2.1.4" - rimraf "^2.5.1" - tar "^2.2.1" - uid-number "^0.0.6" - -tar@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" - dependencies: - block-stream "*" - fstream "^1.0.2" - inherits "2" - -text-table@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - -theming@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/theming/-/theming-1.3.0.tgz#286d5bae80be890d0adc645e5ca0498723725bdc" - dependencies: - brcast "^3.0.1" - is-function "^1.0.1" - is-plain-object "^2.0.1" - prop-types "^15.5.8" - -through@^2.3.6, through@^2.3.8: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - -thunky@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.0.2.tgz#a862e018e3fb1ea2ec3fce5d55605cf57f247371" - -time-stamp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-2.0.0.tgz#95c6a44530e15ba8d6f4a3ecb8c3a3fac46da357" - -timed-out@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" - -timers-browserify@^2.0.4: - version "2.0.6" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.6.tgz#241e76927d9ca05f4d959819022f5b3664b64bae" - dependencies: - setimmediate "^1.0.4" - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - dependencies: - os-tmpdir "~1.0.2" - -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - -to-fast-properties@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-1.0.3.tgz#b83571fa4d8c25b82e231b06e3a3055de4ca1a47" - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -tough-cookie@~2.3.0: - version "2.3.4" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" - dependencies: - punycode "^1.4.1" - -"traverse@>=0.3.0 <0.4": - version "0.3.9" - resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9" - -trim-newlines@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613" - -trim-right@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" - -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - dependencies: - safe-buffer "^5.0.1" - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - dependencies: - prelude-ls "~1.1.2" - -type-is@~1.6.15: - version "1.6.16" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.16.tgz#f89ce341541c672b25ee7ae3c73dee3b2be50194" - dependencies: - media-typer "0.3.0" - mime-types "~2.1.18" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - -ua-parser-js@^0.7.9: - version "0.7.17" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.17.tgz#e9ec5f9498b9ec910e7ae3ac626a805c4d09ecac" - -uglify-js@^2.8.29: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" - dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" - -uglify-to-browserify@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" - -uglifyjs-webpack-plugin@^0.4.6: - version "0.4.6" - resolved "https://registry.yarnpkg.com/uglifyjs-webpack-plugin/-/uglifyjs-webpack-plugin-0.4.6.tgz#b951f4abb6bd617e66f63eb891498e391763e309" - dependencies: - source-map "^0.5.6" - uglify-js "^2.8.29" - webpack-sources "^1.0.1" - -uid-number@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" - -union-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^0.4.3" - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - -uniqid@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/uniqid/-/uniqid-4.1.1.tgz#89220ddf6b751ae52b5f72484863528596bb84c1" - dependencies: - macaddress "^0.2.8" - -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - -universalify@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.1.tgz#fa71badd4437af4c148841e3b3b165f9e9e590b7" - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -unzip@^0.1.11: - version "0.1.11" - resolved "https://registry.yarnpkg.com/unzip/-/unzip-0.1.11.tgz#89749c63b058d7d90d619f86b98aa1535d3b97f0" - dependencies: - binary ">= 0.3.0 < 1" - fstream ">= 0.1.30 < 1" - match-stream ">= 0.0.2 < 1" - pullstream ">= 0.4.1 < 1" - readable-stream "~1.0.31" - setimmediate ">= 1.0.1 < 2" - -upath@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.0.4.tgz#ee2321ba0a786c50973db043a50b7bcba822361d" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - -url-loader@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-0.6.2.tgz#a007a7109620e9d988d14bce677a1decb9a993f7" - dependencies: - loader-utils "^1.0.2" - mime "^1.4.1" - schema-utils "^0.3.0" - -url-parse-lax@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-1.0.0.tgz#7af8f303645e9bd79a272e7a14ac68bc0609da73" - dependencies: - prepend-http "^1.0.1" - -url-parse@1.0.x: - version "1.0.5" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.0.5.tgz#0854860422afdcfefeb6c965c662d4800169927b" - dependencies: - querystringify "0.0.x" - requires-port "1.0.x" - -url-parse@^1.1.8: - version "1.2.0" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.2.0.tgz#3a19e8aaa6d023ddd27dcc44cb4fc8f7fec23986" - dependencies: - querystringify "~1.0.0" - requires-port "~1.0.0" - -url-to-options@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9" - -url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -use@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/use/-/use-2.0.2.tgz#ae28a0d72f93bf22422a18a2e379993112dec8e8" - dependencies: - define-property "^0.2.5" - isobject "^3.0.0" - lazy-cache "^2.0.2" - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - -util@0.10.3, util@^0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - dependencies: - inherits "2.0.1" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - -uuid@^3.0.0, uuid@^3.0.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14" - -validate-npm-package-license@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.3.tgz#81643bcbef1bdfecd4623793dc4648948ba98338" - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - -vendors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.1.tgz#37ad73c8ee417fb3d580e785312307d274847f22" - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vm-browserify@0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" - dependencies: - indexof "0.0.1" - -warning@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c" - dependencies: - loose-envify "^1.0.0" - -watchpack@^1.4.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.5.0.tgz#231e783af830a22f8966f65c4c4bacc814072eed" - dependencies: - chokidar "^2.0.2" - graceful-fs "^4.1.2" - neo-async "^2.5.0" - -wbuf@^1.1.0, wbuf@^1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.2.tgz#d697b99f1f59512df2751be42769c1580b5801fe" - dependencies: - minimalistic-assert "^1.0.0" - -webpack-dev-middleware@1.12.2: - version "1.12.2" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-1.12.2.tgz#f8fc1120ce3b4fc5680ceecb43d777966b21105e" - dependencies: - memory-fs "~0.4.1" - mime "^1.5.0" - path-is-absolute "^1.0.0" - range-parser "^1.0.3" - time-stamp "^2.0.0" - -webpack-dev-server@^2.11.1: - version "2.11.2" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-2.11.2.tgz#1f4f4c78bf1895378f376815910812daf79a216f" - dependencies: - ansi-html "0.0.7" - array-includes "^3.0.3" - bonjour "^3.5.0" - chokidar "^2.0.0" - compression "^1.5.2" - connect-history-api-fallback "^1.3.0" - debug "^3.1.0" - del "^3.0.0" - express "^4.16.2" - html-entities "^1.2.0" - http-proxy-middleware "~0.17.4" - import-local "^1.0.0" - internal-ip "1.2.0" - ip "^1.1.5" - killable "^1.0.0" - loglevel "^1.4.1" - opn "^5.1.0" - portfinder "^1.0.9" - selfsigned "^1.9.1" - serve-index "^1.7.2" - sockjs "0.3.19" - sockjs-client "1.1.4" - spdy "^3.4.1" - strip-ansi "^3.0.0" - supports-color "^5.1.0" - webpack-dev-middleware "1.12.2" - yargs "6.6.0" - -webpack-sources@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.1.0.tgz#a101ebae59d6507354d71d8013950a3a8b7a5a54" - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack@^3.10.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-3.11.0.tgz#77da451b1d7b4b117adaf41a1a93b5742f24d894" - dependencies: - acorn "^5.0.0" - acorn-dynamic-import "^2.0.0" - ajv "^6.1.0" - ajv-keywords "^3.1.0" - async "^2.1.2" - enhanced-resolve "^3.4.0" - escope "^3.6.0" - interpret "^1.0.0" - json-loader "^0.5.4" - json5 "^0.5.1" - loader-runner "^2.3.0" - loader-utils "^1.1.0" - memory-fs "~0.4.1" - mkdirp "~0.5.0" - node-libs-browser "^2.0.0" - source-map "^0.5.3" - supports-color "^4.2.1" - tapable "^0.2.7" - uglifyjs-webpack-plugin "^0.4.6" - watchpack "^1.4.0" - webpack-sources "^1.0.1" - yargs "^8.0.2" - -websocket-driver@>=0.5.1: - version "0.7.0" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.0.tgz#0caf9d2d755d93aee049d4bdd0d3fe2cca2a24eb" - dependencies: - http-parser-js ">=0.4.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.3" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29" - -whatwg-fetch@>=0.10.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/whatwg-fetch/-/whatwg-fetch-2.0.3.tgz#9c84ec2dcf68187ff00bc64e1274b442176e1c84" - -whet.extend@~0.9.9: - version "0.9.9" - resolved "https://registry.yarnpkg.com/whet.extend/-/whet.extend-0.9.9.tgz#f877d5bf648c97e5aa542fadc16d6a259b9c11a1" - -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - -which@^1.2.9, which@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.0.tgz#ff04bdfc010ee547d780bec38e1ac1c2777d253a" - dependencies: - isexe "^2.0.0" - -wide-align@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.2.tgz#571e0f1b0604636ebc0dfc21b0339bbe31341710" - dependencies: - string-width "^1.0.2" - -window-size@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" - -window-size@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.2.0.tgz#b4315bb4214a3d7058ebeee892e13fa24d98b075" - -wordwrap@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" - -wordwrap@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" - -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - -write@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/write/-/write-0.2.1.tgz#5fc03828e264cea3fe91455476f7a3c566cb0757" - dependencies: - mkdirp "^0.5.1" - -xtend@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" - -y18n@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - -yallist@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" - -yargs-parser@^2.4.1: - version "2.4.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-2.4.1.tgz#85568de3cf150ff49fa51825f03a8c880ddcc5c4" - dependencies: - camelcase "^3.0.0" - lodash.assign "^4.0.6" - -yargs-parser@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-4.2.1.tgz#29cceac0dc4f03c6c87b4a9f217dd18c9f74871c" - dependencies: - camelcase "^3.0.0" - -yargs-parser@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-7.0.0.tgz#8d0ac42f16ea55debd332caf4c4038b3e3f5dfd9" - dependencies: - camelcase "^4.1.0" - -yargs@6.6.0: - version "6.6.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" - dependencies: - camelcase "^3.0.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" - y18n "^3.2.1" - yargs-parser "^4.2.0" - -yargs@^4.2.0: - version "4.8.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-4.8.1.tgz#c0c42924ca4aaa6b0e6da1739dfb216439f9ddc0" - dependencies: - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - lodash.assign "^4.0.3" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.1" - which-module "^1.0.0" - window-size "^0.2.0" - y18n "^3.2.1" - yargs-parser "^2.4.1" - -yargs@^8.0.2: - version "8.0.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-8.0.2.tgz#6299a9055b1cefc969ff7e79c1d918dceb22c360" - dependencies: - camelcase "^4.1.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^2.0.0" - read-pkg-up "^2.0.0" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^2.0.0" - which-module "^2.0.0" - y18n "^3.2.1" - yargs-parser "^7.0.0" - -yargs@~3.10.0: - version "3.10.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" - dependencies: - camelcase "^1.0.2" - cliui "^2.1.0" - decamelize "^1.0.0" - window-size "0.1.0" diff --git a/dashboard/config.go b/dashboard/config.go deleted file mode 100644 index c260ed4f0e..0000000000 --- a/dashboard/config.go +++ /dev/null @@ -1,41 +0,0 @@ -// 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 . - -package dashboard - -import "time" - -// DefaultConfig contains default settings for the dashboard. -var DefaultConfig = Config{ - Host: "localhost", - Port: 8080, - Refresh: 5 * time.Second, -} - -// Config contains the configuration parameters of the dashboard. -type Config struct { - // Host is the host interface on which to start the dashboard server. If this - // field is empty, no dashboard will be started. - Host string `toml:",omitempty"` - - // Port is the TCP port number on which to start the dashboard server. The - // default zero value is/ valid and will pick a port number randomly (useful - // for ephemeral nodes). - Port int `toml:",omitempty"` - - // Refresh is the refresh rate of the data updates, the chartEntry will be collected this often. - Refresh time.Duration `toml:",omitempty"` -} diff --git a/dashboard/cpu.go b/dashboard/cpu.go deleted file mode 100644 index c89879028d..0000000000 --- a/dashboard/cpu.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2018 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 . - -// +build !windows - -package dashboard - -import ( - "syscall" - - "github.com/ethereum/go-ethereum/log" -) - -// getProcessCPUTime retrieves the process' CPU time since program startup. -func getProcessCPUTime() float64 { - var usage syscall.Rusage - if err := syscall.Getrusage(syscall.RUSAGE_SELF, &usage); err != nil { - log.Warn("Failed to retrieve CPU time", "err", err) - return 0 - } - return float64(usage.Utime.Sec+usage.Stime.Sec) + float64(usage.Utime.Usec+usage.Stime.Usec)/1000000 -} diff --git a/dashboard/cpu_windows.go b/dashboard/cpu_windows.go deleted file mode 100644 index 9bb7ec789c..0000000000 --- a/dashboard/cpu_windows.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2018 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 . - -package dashboard - -// getProcessCPUTime returns 0 on Windows as there is no system call to resolve -// the actual process' CPU time. -func getProcessCPUTime() float64 { - return 0 -} diff --git a/dashboard/dashboard.go b/dashboard/dashboard.go deleted file mode 100644 index 399fa34c08..0000000000 --- a/dashboard/dashboard.go +++ /dev/null @@ -1,402 +0,0 @@ -// 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 . - -package dashboard - -//go:generate yarn --cwd ./assets install -//go:generate yarn --cwd ./assets build -//go:generate go-bindata -nometadata -o assets.go -prefix assets -nocompress -pkg dashboard assets/index.html assets/bundle.js -//go:generate sh -c "sed 's#var _bundleJs#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go" -//go:generate sh -c "sed 's#var _indexHtml#//nolint:misspell\\\n&#' assets.go > assets.go.tmp && mv assets.go.tmp assets.go" -//go:generate gofmt -w -s assets.go - -import ( - "fmt" - "net" - "net/http" - "runtime" - "sync" - "sync/atomic" - "time" - - "github.com/elastic/gosigar" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/metrics" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/websocket" -) - -const ( - activeMemorySampleLimit = 200 // Maximum number of active memory data samples - virtualMemorySampleLimit = 200 // Maximum number of virtual memory data samples - networkIngressSampleLimit = 200 // Maximum number of network ingress data samples - networkEgressSampleLimit = 200 // Maximum number of network egress data samples - processCPUSampleLimit = 200 // Maximum number of process cpu data samples - systemCPUSampleLimit = 200 // Maximum number of system cpu data samples - diskReadSampleLimit = 200 // Maximum number of disk read data samples - diskWriteSampleLimit = 200 // Maximum number of disk write data samples -) - -var nextID uint32 // Next connection id - -// Dashboard contains the dashboard internals. -type Dashboard struct { - config *Config - - listener net.Listener - conns map[uint32]*client // Currently live websocket connections - charts *SystemMessage - commit string - lock sync.RWMutex // Lock protecting the dashboard's internals - - quit chan chan error // Channel used for graceful exit - wg sync.WaitGroup -} - -// client represents active websocket connection with a remote browser. -type client struct { - conn *websocket.Conn // Particular live websocket connection - msg chan Message // Message queue for the update messages - logger log.Logger // Logger for the particular live websocket connection -} - -// New creates a new dashboard instance with the given configuration. -func New(config *Config, commit string) (*Dashboard, error) { - now := time.Now() - db := &Dashboard{ - conns: make(map[uint32]*client), - config: config, - quit: make(chan chan error), - charts: &SystemMessage{ - ActiveMemory: emptyChartEntries(now, activeMemorySampleLimit, config.Refresh), - VirtualMemory: emptyChartEntries(now, virtualMemorySampleLimit, config.Refresh), - NetworkIngress: emptyChartEntries(now, networkIngressSampleLimit, config.Refresh), - NetworkEgress: emptyChartEntries(now, networkEgressSampleLimit, config.Refresh), - ProcessCPU: emptyChartEntries(now, processCPUSampleLimit, config.Refresh), - SystemCPU: emptyChartEntries(now, systemCPUSampleLimit, config.Refresh), - DiskRead: emptyChartEntries(now, diskReadSampleLimit, config.Refresh), - DiskWrite: emptyChartEntries(now, diskWriteSampleLimit, config.Refresh), - }, - commit: commit, - } - return db, nil -} - -// emptyChartEntries returns a ChartEntry array containing limit number of empty samples. -func emptyChartEntries(t time.Time, limit int, refresh time.Duration) ChartEntries { - ce := make(ChartEntries, limit) - for i := 0; i < limit; i++ { - ce[i] = &ChartEntry{ - Time: t.Add(-time.Duration(i) * refresh), - } - } - return ce -} - -// Protocols is a meaningless implementation of node.Service. -func (db *Dashboard) Protocols() []p2p.Protocol { return nil } - -// APIs is a meaningless implementation of node.Service. -func (db *Dashboard) APIs() []rpc.API { return nil } - -// Start implements node.Service, starting the data collection thread and the listening server of the dashboard. -func (db *Dashboard) Start(server *p2p.Server) error { - log.Info("Starting dashboard") - - db.wg.Add(2) - go db.collectData() - go db.collectLogs() // In case of removing this line change 2 back to 1 in wg.Add. - - http.HandleFunc("/", db.webHandler) - http.Handle("/api", websocket.Handler(db.apiHandler)) - - listener, err := net.Listen("tcp", fmt.Sprintf("%s:%d", db.config.Host, db.config.Port)) - if err != nil { - return err - } - db.listener = listener - - go http.Serve(listener, nil) - - return nil -} - -// Stop implements node.Service, stopping the data collection thread and the connection listener of the dashboard. -func (db *Dashboard) Stop() error { - // Close the connection listener. - var errs []error - if err := db.listener.Close(); err != nil { - errs = append(errs, err) - } - // Close the collectors. - errc := make(chan error, 1) - for i := 0; i < 2; i++ { - db.quit <- errc - if err := <-errc; err != nil { - errs = append(errs, err) - } - } - // Close the connections. - db.lock.Lock() - for _, c := range db.conns { - if err := c.conn.Close(); err != nil { - c.logger.Warn("Failed to close connection", "err", err) - } - } - db.lock.Unlock() - - // Wait until every goroutine terminates. - db.wg.Wait() - log.Info("Dashboard stopped") - - var err error - if len(errs) > 0 { - err = fmt.Errorf("%v", errs) - } - - return err -} - -// webHandler handles all non-api requests, simply flattening and returning the dashboard website. -func (db *Dashboard) webHandler(w http.ResponseWriter, r *http.Request) { - log.Debug("Request", "URL", r.URL) - - path := r.URL.String() - if path == "/" { - path = "/index.html" - } - blob, err := Asset(path[1:]) - if err != nil { - log.Warn("Failed to load the asset", "path", path, "err", err) - http.Error(w, "not found", http.StatusNotFound) - return - } - w.Write(blob) -} - -// apiHandler handles requests for the dashboard. -func (db *Dashboard) apiHandler(conn *websocket.Conn) { - id := atomic.AddUint32(&nextID, 1) - client := &client{ - conn: conn, - msg: make(chan Message, 128), - logger: log.New("id", id), - } - done := make(chan struct{}) - - // Start listening for messages to send. - db.wg.Add(1) - go func() { - defer db.wg.Done() - - for { - select { - case <-done: - return - case msg := <-client.msg: - if err := websocket.JSON.Send(client.conn, msg); err != nil { - client.logger.Warn("Failed to send the message", "msg", msg, "err", err) - client.conn.Close() - return - } - } - } - }() - - versionMeta := "" - if len(params.VersionMeta) > 0 { - versionMeta = fmt.Sprintf(" (%s)", params.VersionMeta) - } - // Send the past data. - client.msg <- Message{ - General: &GeneralMessage{ - Version: fmt.Sprintf("v%d.%d.%d%s", params.VersionMajor, params.VersionMinor, params.VersionPatch, versionMeta), - Commit: db.commit, - }, - System: &SystemMessage{ - ActiveMemory: db.charts.ActiveMemory, - VirtualMemory: db.charts.VirtualMemory, - NetworkIngress: db.charts.NetworkIngress, - NetworkEgress: db.charts.NetworkEgress, - ProcessCPU: db.charts.ProcessCPU, - SystemCPU: db.charts.SystemCPU, - DiskRead: db.charts.DiskRead, - DiskWrite: db.charts.DiskWrite, - }, - } - // Start tracking the connection and drop at connection loss. - db.lock.Lock() - db.conns[id] = client - db.lock.Unlock() - defer func() { - db.lock.Lock() - delete(db.conns, id) - db.lock.Unlock() - }() - for { - fail := []byte{} - if _, err := conn.Read(fail); err != nil { - close(done) - return - } - // Ignore all messages - } -} - -// collectData collects the required data to plot on the dashboard. -func (db *Dashboard) collectData() { - defer db.wg.Done() - systemCPUUsage := gosigar.Cpu{} - systemCPUUsage.Get() - var ( - mem runtime.MemStats - - prevNetworkIngress = metrics.DefaultRegistry.Get("p2p/InboundTraffic").(metrics.Meter).Count() - prevNetworkEgress = metrics.DefaultRegistry.Get("p2p/OutboundTraffic").(metrics.Meter).Count() - prevProcessCPUTime = getProcessCPUTime() - prevSystemCPUUsage = systemCPUUsage - prevDiskRead = metrics.DefaultRegistry.Get("eth/db/chaindata/disk/read").(metrics.Meter).Count() - prevDiskWrite = metrics.DefaultRegistry.Get("eth/db/chaindata/disk/write").(metrics.Meter).Count() - - frequency = float64(db.config.Refresh / time.Second) - numCPU = float64(runtime.NumCPU()) - ) - - for { - select { - case errc := <-db.quit: - errc <- nil - return - case <-time.After(db.config.Refresh): - systemCPUUsage.Get() - var ( - curNetworkIngress = metrics.DefaultRegistry.Get("p2p/InboundTraffic").(metrics.Meter).Count() - curNetworkEgress = metrics.DefaultRegistry.Get("p2p/OutboundTraffic").(metrics.Meter).Count() - curProcessCPUTime = getProcessCPUTime() - curSystemCPUUsage = systemCPUUsage - curDiskRead = metrics.DefaultRegistry.Get("eth/db/chaindata/disk/read").(metrics.Meter).Count() - curDiskWrite = metrics.DefaultRegistry.Get("eth/db/chaindata/disk/write").(metrics.Meter).Count() - - deltaNetworkIngress = float64(curNetworkIngress - prevNetworkIngress) - deltaNetworkEgress = float64(curNetworkEgress - prevNetworkEgress) - deltaProcessCPUTime = curProcessCPUTime - prevProcessCPUTime - deltaSystemCPUUsage = curSystemCPUUsage.Delta(prevSystemCPUUsage) - deltaDiskRead = curDiskRead - prevDiskRead - deltaDiskWrite = curDiskWrite - prevDiskWrite - ) - prevNetworkIngress = curNetworkIngress - prevNetworkEgress = curNetworkEgress - prevProcessCPUTime = curProcessCPUTime - prevSystemCPUUsage = curSystemCPUUsage - prevDiskRead = curDiskRead - prevDiskWrite = curDiskWrite - - now := time.Now() - - runtime.ReadMemStats(&mem) - activeMemory := &ChartEntry{ - Time: now, - Value: float64(mem.Alloc) / frequency, - } - virtualMemory := &ChartEntry{ - Time: now, - Value: float64(mem.Sys) / frequency, - } - networkIngress := &ChartEntry{ - Time: now, - Value: deltaNetworkIngress / frequency, - } - networkEgress := &ChartEntry{ - Time: now, - Value: deltaNetworkEgress / frequency, - } - processCPU := &ChartEntry{ - Time: now, - Value: deltaProcessCPUTime / frequency / numCPU * 100, - } - systemCPU := &ChartEntry{ - Time: now, - Value: float64(deltaSystemCPUUsage.Sys+deltaSystemCPUUsage.User) / frequency / numCPU, - } - diskRead := &ChartEntry{ - Time: now, - Value: float64(deltaDiskRead) / frequency, - } - diskWrite := &ChartEntry{ - Time: now, - Value: float64(deltaDiskWrite) / frequency, - } - db.charts.ActiveMemory = append(db.charts.ActiveMemory[1:], activeMemory) - db.charts.VirtualMemory = append(db.charts.VirtualMemory[1:], virtualMemory) - db.charts.NetworkIngress = append(db.charts.NetworkIngress[1:], networkIngress) - db.charts.NetworkEgress = append(db.charts.NetworkEgress[1:], networkEgress) - db.charts.ProcessCPU = append(db.charts.ProcessCPU[1:], processCPU) - db.charts.SystemCPU = append(db.charts.SystemCPU[1:], systemCPU) - db.charts.DiskRead = append(db.charts.DiskRead[1:], diskRead) - db.charts.DiskWrite = append(db.charts.DiskRead[1:], diskWrite) - - db.sendToAll(&Message{ - System: &SystemMessage{ - ActiveMemory: ChartEntries{activeMemory}, - VirtualMemory: ChartEntries{virtualMemory}, - NetworkIngress: ChartEntries{networkIngress}, - NetworkEgress: ChartEntries{networkEgress}, - ProcessCPU: ChartEntries{processCPU}, - SystemCPU: ChartEntries{systemCPU}, - DiskRead: ChartEntries{diskRead}, - DiskWrite: ChartEntries{diskWrite}, - }, - }) - } - } -} - -// collectLogs collects and sends the logs to the active dashboards. -func (db *Dashboard) collectLogs() { - defer db.wg.Done() - - id := 1 - // TODO (kurkomisi): log collection comes here. - for { - select { - case errc := <-db.quit: - errc <- nil - return - case <-time.After(db.config.Refresh / 2): - db.sendToAll(&Message{ - Logs: &LogsMessage{ - Log: []string{fmt.Sprintf("%-4d: This is a fake log.", id)}, - }, - }) - id++ - } - } -} - -// sendToAll sends the given message to the active dashboards. -func (db *Dashboard) sendToAll(msg *Message) { - db.lock.Lock() - for _, c := range db.conns { - select { - case c.msg <- *msg: - default: - c.conn.Close() - } - } - db.lock.Unlock() -} diff --git a/dashboard/message.go b/dashboard/message.go deleted file mode 100644 index 84b8d74439..0000000000 --- a/dashboard/message.go +++ /dev/null @@ -1,72 +0,0 @@ -// 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 . - -package dashboard - -import "time" - -type Message struct { - General *GeneralMessage `json:"general,omitempty"` - Home *HomeMessage `json:"home,omitempty"` - Chain *ChainMessage `json:"chain,omitempty"` - TxPool *TxPoolMessage `json:"txpool,omitempty"` - Network *NetworkMessage `json:"network,omitempty"` - System *SystemMessage `json:"system,omitempty"` - Logs *LogsMessage `json:"logs,omitempty"` -} - -type ChartEntries []*ChartEntry - -type ChartEntry struct { - Time time.Time `json:"time,omitempty"` - Value float64 `json:"value,omitempty"` -} - -type GeneralMessage struct { - Version string `json:"version,omitempty"` - Commit string `json:"commit,omitempty"` -} - -type HomeMessage struct { - /* TODO (kurkomisi) */ -} - -type ChainMessage struct { - /* TODO (kurkomisi) */ -} - -type TxPoolMessage struct { - /* TODO (kurkomisi) */ -} - -type NetworkMessage struct { - /* TODO (kurkomisi) */ -} - -type SystemMessage struct { - ActiveMemory ChartEntries `json:"activeMemory,omitempty"` - VirtualMemory ChartEntries `json:"virtualMemory,omitempty"` - NetworkIngress ChartEntries `json:"networkIngress,omitempty"` - NetworkEgress ChartEntries `json:"networkEgress,omitempty"` - ProcessCPU ChartEntries `json:"processCPU,omitempty"` - SystemCPU ChartEntries `json:"systemCPU,omitempty"` - DiskRead ChartEntries `json:"diskRead,omitempty"` - DiskWrite ChartEntries `json:"diskWrite,omitempty"` -} - -type LogsMessage struct { - Log []string `json:"log,omitempty"` -} diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go deleted file mode 100644 index 9f3d7237e9..0000000000 --- a/ethstats/ethstats.go +++ /dev/null @@ -1,717 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package ethstats implements the network stats reporting service. -package ethstats - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "math/big" - "net" - "regexp" - "runtime" - "strconv" - "strings" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/les" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rpc" - "golang.org/x/net/websocket" -) - -const ( - // historyUpdateRange is the number of blocks a node should report upon login or - // history request. - historyUpdateRange = 50 - - // txChanSize is the size of channel listening to NewTxsEvent. - // The number is referenced from the size of tx pool. - txChanSize = 4096 - // chainHeadChanSize is the size of channel listening to ChainHeadEvent. - chainHeadChanSize = 10 -) - -type txPool interface { - // SubscribeNewTxsEvent should return an event subscription of - // NewTxsEvent and send events to the given channel. - SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription -} - -type blockChain interface { - SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription -} - -// Service implements an Ethereum netstats reporting daemon that pushes local -// chain statistics up to a monitoring server. -type Service struct { - server *p2p.Server // Peer-to-peer server to retrieve networking infos - eth *eth.Ethereum // Full Ethereum service if monitoring a full node - les *les.LightEthereum // Light Ethereum service if monitoring a light node - engine consensus.Engine // Consensus engine to retrieve variadic block fields - - node string // Name of the node to display on the monitoring page - pass string // Password to authorize access to the monitoring page - host string // Remote address of the monitoring service - - pongCh chan struct{} // Pong notifications are fed into this channel - histCh chan []uint64 // History request block numbers are fed into this channel -} - -// New returns a monitoring service ready for stats reporting. -func New(url string, ethServ *eth.Ethereum, lesServ *les.LightEthereum) (*Service, error) { - // Parse the netstats connection url - re := regexp.MustCompile("([^:@]*)(:([^@]*))?@(.+)") - parts := re.FindStringSubmatch(url) - if len(parts) != 5 { - return nil, fmt.Errorf("invalid netstats url: \"%s\", should be nodename:secret@host:port", url) - } - // Assemble and return the stats service - var engine consensus.Engine - if ethServ != nil { - engine = ethServ.Engine() - } else { - engine = lesServ.Engine() - } - return &Service{ - eth: ethServ, - les: lesServ, - engine: engine, - node: parts[1], - pass: parts[3], - host: parts[4], - pongCh: make(chan struct{}), - histCh: make(chan []uint64, 1), - }, nil -} - -// Protocols implements node.Service, returning the P2P network protocols used -// by the stats service (nil as it doesn't use the devp2p overlay network). -func (s *Service) Protocols() []p2p.Protocol { return nil } - -// APIs implements node.Service, returning the RPC API endpoints provided by the -// stats service (nil as it doesn't provide any user callable APIs). -func (s *Service) APIs() []rpc.API { return nil } - -// Start implements node.Service, starting up the monitoring and reporting daemon. -func (s *Service) Start(server *p2p.Server) error { - s.server = server - go s.loop() - - log.Info("Stats daemon started") - return nil -} - -// Stop implements node.Service, terminating the monitoring and reporting daemon. -func (s *Service) Stop() error { - log.Info("Stats daemon stopped") - return nil -} - -// loop keeps trying to connect to the netstats server, reporting chain events -// until termination. -func (s *Service) loop() { - // Subscribe to chain events to execute updates on - var blockchain blockChain - var txpool txPool - if s.eth != nil { - blockchain = s.eth.BlockChain() - txpool = s.eth.TxPool() - } else { - blockchain = s.les.BlockChain() - txpool = s.les.TxPool() - } - - chainHeadCh := make(chan core.ChainHeadEvent, chainHeadChanSize) - headSub := blockchain.SubscribeChainHeadEvent(chainHeadCh) - defer headSub.Unsubscribe() - - txEventCh := make(chan core.NewTxsEvent, txChanSize) - txSub := txpool.SubscribeNewTxsEvent(txEventCh) - defer txSub.Unsubscribe() - - // Start a goroutine that exhausts the subsciptions to avoid events piling up - var ( - quitCh = make(chan struct{}) - headCh = make(chan *types.Block, 1) - txCh = make(chan struct{}, 1) - ) - go func() { - var lastTx mclock.AbsTime - - HandleLoop: - for { - select { - // Notify of chain head events, but drop if too frequent - case head := <-chainHeadCh: - select { - case headCh <- head.Block: - default: - } - - // Notify of new transaction events, but drop if too frequent - case <-txEventCh: - if time.Duration(mclock.Now()-lastTx) < time.Second { - continue - } - lastTx = mclock.Now() - - select { - case txCh <- struct{}{}: - default: - } - - // node stopped - case <-txSub.Err(): - break HandleLoop - case <-headSub.Err(): - break HandleLoop - } - } - close(quitCh) - }() - // Loop reporting until termination - for { - // Resolve the URL, defaulting to TLS, but falling back to none too - path := fmt.Sprintf("%s/api", s.host) - urls := []string{path} - - if !strings.Contains(path, "://") { // url.Parse and url.IsAbs is unsuitable (https://github.com/golang/go/issues/19779) - urls = []string{"wss://" + path, "ws://" + path} - } - // Establish a websocket connection to the server on any supported URL - var ( - conf *websocket.Config - conn *websocket.Conn - err error - ) - for _, url := range urls { - if conf, err = websocket.NewConfig(url, "http://localhost/"); err != nil { - continue - } - conf.Dialer = &net.Dialer{Timeout: 5 * time.Second} - if conn, err = websocket.DialConfig(conf); err == nil { - break - } - } - if err != nil { - log.Warn("Stats server unreachable", "err", err) - time.Sleep(10 * time.Second) - continue - } - // Authenticate the client with the server - if err = s.login(conn); err != nil { - log.Warn("Stats login failed", "err", err) - conn.Close() - time.Sleep(10 * time.Second) - continue - } - go s.readLoop(conn) - - // Send the initial stats so our node looks decent from the get go - if err = s.report(conn); err != nil { - log.Warn("Initial stats report failed", "err", err) - conn.Close() - continue - } - // Keep sending status updates until the connection breaks - fullReport := time.NewTicker(15 * time.Second) - - for err == nil { - select { - case <-quitCh: - conn.Close() - return - - case <-fullReport.C: - if err = s.report(conn); err != nil { - log.Warn("Full stats report failed", "err", err) - } - case list := <-s.histCh: - if err = s.reportHistory(conn, list); err != nil { - log.Warn("Requested history report failed", "err", err) - } - case head := <-headCh: - if err = s.reportBlock(conn, head); err != nil { - log.Warn("Block stats report failed", "err", err) - } - if err = s.reportPending(conn); err != nil { - log.Warn("Post-block transaction stats report failed", "err", err) - } - case <-txCh: - if err = s.reportPending(conn); err != nil { - log.Warn("Transaction stats report failed", "err", err) - } - } - } - // Make sure the connection is closed - conn.Close() - } -} - -// readLoop loops as long as the connection is alive and retrieves data packets -// from the network socket. If any of them match an active request, it forwards -// it, if they themselves are requests it initiates a reply, and lastly it drops -// unknown packets. -func (s *Service) readLoop(conn *websocket.Conn) { - // If the read loop exists, close the connection - defer conn.Close() - - for { - // Retrieve the next generic network packet and bail out on error - var msg map[string][]interface{} - if err := websocket.JSON.Receive(conn, &msg); err != nil { - log.Warn("Failed to decode stats server message", "err", err) - return - } - log.Trace("Received message from stats server", "msg", msg) - if len(msg["emit"]) == 0 { - log.Warn("Stats server sent non-broadcast", "msg", msg) - return - } - command, ok := msg["emit"][0].(string) - if !ok { - log.Warn("Invalid stats server message type", "type", msg["emit"][0]) - return - } - // If the message is a ping reply, deliver (someone must be listening!) - if len(msg["emit"]) == 2 && command == "node-pong" { - select { - case s.pongCh <- struct{}{}: - // Pong delivered, continue listening - continue - default: - // Ping routine dead, abort - log.Warn("Stats server pinger seems to have died") - return - } - } - // If the message is a history request, forward to the event processor - if len(msg["emit"]) == 2 && command == "history" { - // Make sure the request is valid and doesn't crash us - request, ok := msg["emit"][1].(map[string]interface{}) - if !ok { - log.Warn("Invalid stats history request", "msg", msg["emit"][1]) - s.histCh <- nil - continue // Ethstats sometime sends invalid history requests, ignore those - } - list, ok := request["list"].([]interface{}) - if !ok { - log.Warn("Invalid stats history block list", "list", request["list"]) - return - } - // Convert the block number list to an integer list - numbers := make([]uint64, len(list)) - for i, num := range list { - n, ok := num.(float64) - if !ok { - log.Warn("Invalid stats history block number", "number", num) - return - } - numbers[i] = uint64(n) - } - select { - case s.histCh <- numbers: - continue - default: - } - } - // Report anything else and continue - log.Info("Unknown stats message", "msg", msg) - } -} - -// nodeInfo is the collection of metainformation about a node that is displayed -// on the monitoring page. -type nodeInfo struct { - Name string `json:"name"` - Node string `json:"node"` - Port int `json:"port"` - Network string `json:"net"` - Protocol string `json:"protocol"` - API string `json:"api"` - Os string `json:"os"` - OsVer string `json:"os_v"` - Client string `json:"client"` - History bool `json:"canUpdateHistory"` -} - -// authMsg is the authentication infos needed to login to a monitoring server. -type authMsg struct { - ID string `json:"id"` - Info nodeInfo `json:"info"` - Secret string `json:"secret"` -} - -// login tries to authorize the client at the remote server. -func (s *Service) login(conn *websocket.Conn) error { - // Construct and send the login authentication - infos := s.server.NodeInfo() - - var network, protocol string - if info := infos.Protocols["eth"]; info != nil { - network = fmt.Sprintf("%d", info.(*eth.NodeInfo).Network) - protocol = fmt.Sprintf("eth/%d", eth.ProtocolVersions[0]) - } else { - network = fmt.Sprintf("%d", infos.Protocols["les"].(*les.NodeInfo).Network) - protocol = fmt.Sprintf("les/%d", les.ClientProtocolVersions[0]) - } - auth := &authMsg{ - ID: s.node, - Info: nodeInfo{ - Name: s.node, - Node: infos.Name, - Port: infos.Ports.Listener, - Network: network, - Protocol: protocol, - API: "No", - Os: runtime.GOOS, - OsVer: runtime.GOARCH, - Client: "0.1.1", - History: true, - }, - Secret: s.pass, - } - login := map[string][]interface{}{ - "emit": {"hello", auth}, - } - if err := websocket.JSON.Send(conn, login); err != nil { - return err - } - // Retrieve the remote ack or connection termination - var ack map[string][]string - if err := websocket.JSON.Receive(conn, &ack); err != nil || len(ack["emit"]) != 1 || ack["emit"][0] != "ready" { - return errors.New("unauthorized") - } - return nil -} - -// report collects all possible data to report and send it to the stats server. -// This should only be used on reconnects or rarely to avoid overloading the -// server. Use the individual methods for reporting subscribed events. -func (s *Service) report(conn *websocket.Conn) error { - if err := s.reportLatency(conn); err != nil { - return err - } - if err := s.reportBlock(conn, nil); err != nil { - return err - } - if err := s.reportPending(conn); err != nil { - return err - } - if err := s.reportStats(conn); err != nil { - return err - } - return nil -} - -// reportLatency sends a ping request to the server, measures the RTT time and -// finally sends a latency update. -func (s *Service) reportLatency(conn *websocket.Conn) error { - // Send the current time to the ethstats server - start := time.Now() - - ping := map[string][]interface{}{ - "emit": {"node-ping", map[string]string{ - "id": s.node, - "clientTime": start.String(), - }}, - } - if err := websocket.JSON.Send(conn, ping); err != nil { - return err - } - // Wait for the pong request to arrive back - select { - case <-s.pongCh: - // Pong delivered, report the latency - case <-time.After(5 * time.Second): - // Ping timeout, abort - return errors.New("ping timed out") - } - latency := strconv.Itoa(int((time.Since(start) / time.Duration(2)).Nanoseconds() / 1000000)) - - // Send back the measured latency - log.Trace("Sending measured latency to ethstats", "latency", latency) - - stats := map[string][]interface{}{ - "emit": {"latency", map[string]string{ - "id": s.node, - "latency": latency, - }}, - } - return websocket.JSON.Send(conn, stats) -} - -// blockStats is the information to report about individual blocks. -type blockStats struct { - Number *big.Int `json:"number"` - Hash common.Hash `json:"hash"` - ParentHash common.Hash `json:"parentHash"` - Timestamp *big.Int `json:"timestamp"` - Miner common.Address `json:"miner"` - GasUsed uint64 `json:"gasUsed"` - GasLimit uint64 `json:"gasLimit"` - Diff string `json:"difficulty"` - TotalDiff string `json:"totalDifficulty"` - Txs []txStats `json:"transactions"` - TxHash common.Hash `json:"transactionsRoot"` - Root common.Hash `json:"stateRoot"` - Uncles uncleStats `json:"uncles"` -} - -// txStats is the information to report about individual transactions. -type txStats struct { - Hash common.Hash `json:"hash"` -} - -// uncleStats is a custom wrapper around an uncle array to force serializing -// empty arrays instead of returning null for them. -type uncleStats []*types.Header - -func (s uncleStats) MarshalJSON() ([]byte, error) { - if uncles := ([]*types.Header)(s); len(uncles) > 0 { - return json.Marshal(uncles) - } - return []byte("[]"), nil -} - -// reportBlock retrieves the current chain head and reports it to the stats server. -func (s *Service) reportBlock(conn *websocket.Conn, block *types.Block) error { - // Gather the block details from the header or block chain - details := s.assembleBlockStats(block) - - // Assemble the block report and send it to the server - log.Trace("Sending new block to ethstats", "number", details.Number, "hash", details.Hash) - - stats := map[string]interface{}{ - "id": s.node, - "block": details, - } - report := map[string][]interface{}{ - "emit": {"block", stats}, - } - return websocket.JSON.Send(conn, report) -} - -// assembleBlockStats retrieves any required metadata to report a single block -// and assembles the block stats. If block is nil, the current head is processed. -func (s *Service) assembleBlockStats(block *types.Block) *blockStats { - // Gather the block infos from the local blockchain - var ( - header *types.Header - td *big.Int - txs []txStats - uncles []*types.Header - ) - if s.eth != nil { - // Full nodes have all needed information available - if block == nil { - block = s.eth.BlockChain().CurrentBlock() - } - header = block.Header() - td = s.eth.BlockChain().GetTd(header.Hash(), header.Number.Uint64()) - - txs = make([]txStats, len(block.Transactions())) - for i, tx := range block.Transactions() { - txs[i].Hash = tx.Hash() - } - uncles = block.Uncles() - } else { - // Light nodes would need on-demand lookups for transactions/uncles, skip - if block != nil { - header = block.Header() - } else { - header = s.les.BlockChain().CurrentHeader() - } - td = s.les.BlockChain().GetTd(header.Hash(), header.Number.Uint64()) - txs = []txStats{} - } - // Assemble and return the block stats - author, _ := s.engine.Author(header) - - return &blockStats{ - Number: header.Number, - Hash: header.Hash(), - ParentHash: header.ParentHash, - Timestamp: header.Time, - Miner: author, - GasUsed: header.GasUsed, - GasLimit: header.GasLimit, - Diff: header.Difficulty.String(), - TotalDiff: td.String(), - Txs: txs, - TxHash: header.TxHash, - Root: header.Root, - Uncles: uncles, - } -} - -// reportHistory retrieves the most recent batch of blocks and reports it to the -// stats server. -func (s *Service) reportHistory(conn *websocket.Conn, list []uint64) error { - // Figure out the indexes that need reporting - indexes := make([]uint64, 0, historyUpdateRange) - if len(list) > 0 { - // Specific indexes requested, send them back in particular - indexes = append(indexes, list...) - } else { - // No indexes requested, send back the top ones - var head int64 - if s.eth != nil { - head = s.eth.BlockChain().CurrentHeader().Number.Int64() - } else { - head = s.les.BlockChain().CurrentHeader().Number.Int64() - } - start := head - historyUpdateRange + 1 - if start < 0 { - start = 0 - } - for i := uint64(start); i <= uint64(head); i++ { - indexes = append(indexes, i) - } - } - // Gather the batch of blocks to report - history := make([]*blockStats, len(indexes)) - for i, number := range indexes { - // Retrieve the next block if it's known to us - var block *types.Block - if s.eth != nil { - block = s.eth.BlockChain().GetBlockByNumber(number) - } else { - if header := s.les.BlockChain().GetHeaderByNumber(number); header != nil { - block = types.NewBlockWithHeader(header) - } - } - // If we do have the block, add to the history and continue - if block != nil { - history[len(history)-1-i] = s.assembleBlockStats(block) - continue - } - // Ran out of blocks, cut the report short and send - history = history[len(history)-i:] - break - } - // Assemble the history report and send it to the server - if len(history) > 0 { - log.Trace("Sending historical blocks to ethstats", "first", history[0].Number, "last", history[len(history)-1].Number) - } else { - log.Trace("No history to send to stats server") - } - stats := map[string]interface{}{ - "id": s.node, - "history": history, - } - report := map[string][]interface{}{ - "emit": {"history", stats}, - } - return websocket.JSON.Send(conn, report) -} - -// pendStats is the information to report about pending transactions. -type pendStats struct { - Pending int `json:"pending"` -} - -// reportPending retrieves the current number of pending transactions and reports -// it to the stats server. -func (s *Service) reportPending(conn *websocket.Conn) error { - // Retrieve the pending count from the local blockchain - var pending int - if s.eth != nil { - pending, _ = s.eth.TxPool().Stats() - } else { - pending = s.les.TxPool().Stats() - } - // Assemble the transaction stats and send it to the server - log.Trace("Sending pending transactions to ethstats", "count", pending) - - stats := map[string]interface{}{ - "id": s.node, - "stats": &pendStats{ - Pending: pending, - }, - } - report := map[string][]interface{}{ - "emit": {"pending", stats}, - } - return websocket.JSON.Send(conn, report) -} - -// nodeStats is the information to report about the local node. -type nodeStats struct { - Active bool `json:"active"` - Syncing bool `json:"syncing"` - Mining bool `json:"mining"` - Hashrate int `json:"hashrate"` - Peers int `json:"peers"` - GasPrice int `json:"gasPrice"` - Uptime int `json:"uptime"` -} - -// reportPending retrieves various stats about the node at the networking and -// mining layer and reports it to the stats server. -func (s *Service) reportStats(conn *websocket.Conn) error { - // Gather the syncing and mining infos from the local miner instance - var ( - mining bool - hashrate int - syncing bool - gasprice int - ) - if s.eth != nil { - mining = s.eth.Miner().Mining() - hashrate = int(s.eth.Miner().HashRate()) - - sync := s.eth.Downloader().Progress() - syncing = s.eth.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock - - price, _ := s.eth.APIBackend.SuggestPrice(context.Background()) - gasprice = int(price.Uint64()) - } else { - sync := s.les.Downloader().Progress() - syncing = s.les.BlockChain().CurrentHeader().Number.Uint64() >= sync.HighestBlock - } - // Assemble the node stats and send it to the server - log.Trace("Sending node details to ethstats") - - stats := map[string]interface{}{ - "id": s.node, - "stats": &nodeStats{ - Active: true, - Mining: mining, - Hashrate: hashrate, - Peers: s.server.PeerCount(), - GasPrice: gasprice, - Syncing: syncing, - Uptime: 100, - }, - } - report := map[string][]interface{}{ - "emit": {"stats", stats}, - } - return websocket.JSON.Send(conn, report) -} diff --git a/les/api_backend.go b/les/api_backend.go deleted file mode 100644 index dea33c4702..0000000000 --- a/les/api_backend.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package les - -import ( - "context" - "math/big" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/bloombits" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rpc" -) - -type LesApiBackend struct { - eth *LightEthereum - gpo *gasprice.Oracle -} - -func (b *LesApiBackend) ChainConfig() *params.ChainConfig { - return b.eth.chainConfig -} - -func (b *LesApiBackend) CurrentBlock() *types.Block { - return types.NewBlockWithHeader(b.eth.BlockChain().CurrentHeader()) -} - -func (b *LesApiBackend) SetHead(number uint64) { - b.eth.protocolManager.downloader.Cancel() - b.eth.blockchain.SetHead(number) -} - -func (b *LesApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { - if blockNr == rpc.LatestBlockNumber || blockNr == rpc.PendingBlockNumber { - return b.eth.blockchain.CurrentHeader(), nil - } - - return b.eth.blockchain.GetHeaderByNumberOdr(ctx, uint64(blockNr)) -} - -func (b *LesApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) { - header, err := b.HeaderByNumber(ctx, blockNr) - if header == nil || err != nil { - return nil, err - } - return b.GetBlock(ctx, header.Hash()) -} - -func (b *LesApiBackend) StateAndHeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*state.StateDB, *types.Header, error) { - header, err := b.HeaderByNumber(ctx, blockNr) - if header == nil || err != nil { - return nil, nil, err - } - return light.NewState(ctx, header, b.eth.odr), header, nil -} - -func (b *LesApiBackend) GetBlock(ctx context.Context, blockHash common.Hash) (*types.Block, error) { - return b.eth.blockchain.GetBlockByHash(ctx, blockHash) -} - -func (b *LesApiBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) { - if number := rawdb.ReadHeaderNumber(b.eth.chainDb, hash); number != nil { - return light.GetBlockReceipts(ctx, b.eth.odr, hash, *number) - } - return nil, nil -} - -func (b *LesApiBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) { - if number := rawdb.ReadHeaderNumber(b.eth.chainDb, hash); number != nil { - return light.GetBlockLogs(ctx, b.eth.odr, hash, *number) - } - return nil, nil -} - -func (b *LesApiBackend) GetTd(hash common.Hash) *big.Int { - return b.eth.blockchain.GetTdByHash(hash) -} - -func (b *LesApiBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmCfg vm.Config) (*vm.EVM, func() error, error) { - state.SetBalance(msg.From(), math.MaxBig256) - context := core.NewEVMContext(msg, header, b.eth.blockchain, nil) - return vm.NewEVM(context, state, b.eth.chainConfig, vmCfg), state.Error, nil -} - -func (b *LesApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error { - return b.eth.txPool.Add(ctx, signedTx) -} - -func (b *LesApiBackend) RemoveTx(txHash common.Hash) { - b.eth.txPool.RemoveTx(txHash) -} - -func (b *LesApiBackend) GetPoolTransactions() (types.Transactions, error) { - return b.eth.txPool.GetTransactions() -} - -func (b *LesApiBackend) GetPoolTransaction(txHash common.Hash) *types.Transaction { - return b.eth.txPool.GetTransaction(txHash) -} - -func (b *LesApiBackend) GetPoolNonce(ctx context.Context, addr common.Address) (uint64, error) { - return b.eth.txPool.GetNonce(ctx, addr) -} - -func (b *LesApiBackend) Stats() (pending int, queued int) { - return b.eth.txPool.Stats(), 0 -} - -func (b *LesApiBackend) TxPoolContent() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { - return b.eth.txPool.Content() -} - -func (b *LesApiBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { - return b.eth.txPool.SubscribeNewTxsEvent(ch) -} - -func (b *LesApiBackend) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { - return b.eth.blockchain.SubscribeChainEvent(ch) -} - -func (b *LesApiBackend) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { - return b.eth.blockchain.SubscribeChainHeadEvent(ch) -} - -func (b *LesApiBackend) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { - return b.eth.blockchain.SubscribeChainSideEvent(ch) -} - -func (b *LesApiBackend) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { - return b.eth.blockchain.SubscribeLogsEvent(ch) -} - -func (b *LesApiBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { - return b.eth.blockchain.SubscribeRemovedLogsEvent(ch) -} - -func (b *LesApiBackend) Downloader() *downloader.Downloader { - return b.eth.Downloader() -} - -func (b *LesApiBackend) ProtocolVersion() int { - return b.eth.LesVersion() + 10000 -} - -func (b *LesApiBackend) SuggestPrice(ctx context.Context) (*big.Int, error) { - return b.gpo.SuggestPrice(ctx) -} - -func (b *LesApiBackend) ChainDb() ethdb.Database { - return b.eth.chainDb -} - -func (b *LesApiBackend) EventMux() *event.TypeMux { - return b.eth.eventMux -} - -func (b *LesApiBackend) AccountManager() *accounts.Manager { - return b.eth.accountManager -} - -func (b *LesApiBackend) BloomStatus() (uint64, uint64) { - if b.eth.bloomIndexer == nil { - return 0, 0 - } - sections, _, _ := b.eth.bloomIndexer.Sections() - return light.BloomTrieFrequency, sections -} - -func (b *LesApiBackend) ServiceFilter(ctx context.Context, session *bloombits.MatcherSession) { - for i := 0; i < bloomFilterThreads; i++ { - go session.Multiplex(bloomRetrievalBatch, bloomRetrievalWait, b.eth.bloomRequests) - } -} diff --git a/les/backend.go b/les/backend.go deleted file mode 100644 index 35f67f29f8..0000000000 --- a/les/backend.go +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package les implements the Light Ethereum Subprotocol. -package les - -import ( - "fmt" - "sync" - "time" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/bloombits" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/eth/filters" - "github.com/ethereum/go-ethereum/eth/gasprice" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/internal/ethapi" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discv5" - "github.com/ethereum/go-ethereum/params" - rpc "github.com/ethereum/go-ethereum/rpc" -) - -type LightEthereum struct { - config *eth.Config - - odr *LesOdr - relay *LesTxRelay - chainConfig *params.ChainConfig - // Channel for shutting down the service - shutdownChan chan bool - // Handlers - peers *peerSet - txPool *light.TxPool - blockchain *light.LightChain - protocolManager *ProtocolManager - serverPool *serverPool - reqDist *requestDistributor - retriever *retrieveManager - // DB interfaces - chainDb ethdb.Database // Block chain database - - bloomRequests chan chan *bloombits.Retrieval // Channel receiving bloom data retrieval requests - bloomIndexer, chtIndexer, bloomTrieIndexer *core.ChainIndexer - - ApiBackend *LesApiBackend - - eventMux *event.TypeMux - engine consensus.Engine - accountManager *accounts.Manager - - networkId uint64 - netRPCService *ethapi.PublicNetAPI - - wg sync.WaitGroup -} - -func New(ctx *node.ServiceContext, config *eth.Config) (*LightEthereum, error) { - chainDb, err := eth.CreateDB(ctx, config, "lightchaindata") - if err != nil { - return nil, err - } - chainConfig, genesisHash, genesisErr := core.SetupGenesisBlock(chainDb, config.Genesis) - if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat { - return nil, genesisErr - } - log.Info("Initialised chain configuration", "config", chainConfig) - - peers := newPeerSet() - quitSync := make(chan struct{}) - - leth := &LightEthereum{ - config: config, - chainConfig: chainConfig, - chainDb: chainDb, - eventMux: ctx.EventMux, - peers: peers, - reqDist: newRequestDistributor(peers, quitSync), - accountManager: ctx.AccountManager, - engine: eth.CreateConsensusEngine(ctx, &config.Ethash, chainConfig, chainDb), - shutdownChan: make(chan bool), - networkId: config.NetworkId, - bloomRequests: make(chan chan *bloombits.Retrieval), - bloomIndexer: eth.NewBloomIndexer(chainDb, light.BloomTrieFrequency), - chtIndexer: light.NewChtIndexer(chainDb, true), - bloomTrieIndexer: light.NewBloomTrieIndexer(chainDb, true), - } - - leth.relay = NewLesTxRelay(peers, leth.reqDist) - leth.serverPool = newServerPool(chainDb, quitSync, &leth.wg) - leth.retriever = newRetrieveManager(peers, leth.reqDist, leth.serverPool) - leth.odr = NewLesOdr(chainDb, leth.chtIndexer, leth.bloomTrieIndexer, leth.bloomIndexer, leth.retriever) - if leth.blockchain, err = light.NewLightChain(leth.odr, leth.chainConfig, leth.engine); err != nil { - return nil, err - } - leth.bloomIndexer.Start(leth.blockchain) - // Rewind the chain in case of an incompatible config upgrade. - if compat, ok := genesisErr.(*params.ConfigCompatError); ok { - log.Warn("Rewinding chain to upgrade configuration", "err", compat) - leth.blockchain.SetHead(compat.RewindTo) - rawdb.WriteChainConfig(chainDb, genesisHash, chainConfig) - } - - leth.txPool = light.NewTxPool(leth.chainConfig, leth.blockchain, leth.relay) - if leth.protocolManager, err = NewProtocolManager(leth.chainConfig, true, ClientProtocolVersions, config.NetworkId, leth.eventMux, leth.engine, leth.peers, leth.blockchain, nil, chainDb, leth.odr, leth.relay, leth.serverPool, quitSync, &leth.wg); err != nil { - return nil, err - } - leth.ApiBackend = &LesApiBackend{leth, nil} - gpoParams := config.GPO - if gpoParams.Default == nil { - gpoParams.Default = config.GasPrice - } - leth.ApiBackend.gpo = gasprice.NewOracle(leth.ApiBackend, gpoParams) - return leth, nil -} - -func lesTopic(genesisHash common.Hash, protocolVersion uint) discv5.Topic { - var name string - switch protocolVersion { - case lpv1: - name = "LES" - case lpv2: - name = "LES2" - default: - panic(nil) - } - return discv5.Topic(name + "@" + common.Bytes2Hex(genesisHash.Bytes()[0:8])) -} - -type LightDummyAPI struct{} - -// Etherbase is the address that mining rewards will be send to -func (s *LightDummyAPI) Etherbase() (common.Address, error) { - return common.Address{}, fmt.Errorf("not supported") -} - -// Coinbase is the address that mining rewards will be send to (alias for Etherbase) -func (s *LightDummyAPI) Coinbase() (common.Address, error) { - return common.Address{}, fmt.Errorf("not supported") -} - -// Hashrate returns the POW hashrate -func (s *LightDummyAPI) Hashrate() hexutil.Uint { - return 0 -} - -// Mining returns an indication if this node is currently mining. -func (s *LightDummyAPI) Mining() bool { - return false -} - -// APIs returns the collection of RPC services the ethereum package offers. -// NOTE, some of these services probably need to be moved to somewhere else. -func (s *LightEthereum) APIs() []rpc.API { - return append(ethapi.GetAPIs(s.ApiBackend), []rpc.API{ - { - Namespace: "eth", - Version: "1.0", - Service: &LightDummyAPI{}, - Public: true, - }, { - Namespace: "eth", - Version: "1.0", - Service: downloader.NewPublicDownloaderAPI(s.protocolManager.downloader, s.eventMux), - Public: true, - }, { - Namespace: "eth", - Version: "1.0", - Service: filters.NewPublicFilterAPI(s.ApiBackend, true), - Public: true, - }, { - Namespace: "net", - Version: "1.0", - Service: s.netRPCService, - Public: true, - }, - }...) -} - -func (s *LightEthereum) ResetWithGenesisBlock(gb *types.Block) { - s.blockchain.ResetWithGenesisBlock(gb) -} - -func (s *LightEthereum) BlockChain() *light.LightChain { return s.blockchain } -func (s *LightEthereum) TxPool() *light.TxPool { return s.txPool } -func (s *LightEthereum) Engine() consensus.Engine { return s.engine } -func (s *LightEthereum) LesVersion() int { return int(s.protocolManager.SubProtocols[0].Version) } -func (s *LightEthereum) Downloader() *downloader.Downloader { return s.protocolManager.downloader } -func (s *LightEthereum) EventMux() *event.TypeMux { return s.eventMux } - -// Protocols implements node.Service, returning all the currently configured -// network protocols to start. -func (s *LightEthereum) Protocols() []p2p.Protocol { - return s.protocolManager.SubProtocols -} - -// Start implements node.Service, starting all internal goroutines needed by the -// Ethereum protocol implementation. -func (s *LightEthereum) Start(srvr *p2p.Server) error { - s.startBloomHandlers() - log.Warn("Light client mode is an experimental feature") - s.netRPCService = ethapi.NewPublicNetAPI(srvr, s.networkId) - // clients are searching for the first advertised protocol in the list - protocolVersion := AdvertiseProtocolVersions[0] - s.serverPool.start(srvr, lesTopic(s.blockchain.Genesis().Hash(), protocolVersion)) - s.protocolManager.Start(s.config.LightPeers) - return nil -} - -// Stop implements node.Service, terminating all internal goroutines used by the -// Ethereum protocol. -func (s *LightEthereum) Stop() error { - s.odr.Stop() - if s.bloomIndexer != nil { - s.bloomIndexer.Close() - } - if s.chtIndexer != nil { - s.chtIndexer.Close() - } - if s.bloomTrieIndexer != nil { - s.bloomTrieIndexer.Close() - } - s.blockchain.Stop() - s.protocolManager.Stop() - s.txPool.Stop() - - s.eventMux.Stop() - - time.Sleep(time.Millisecond * 200) - s.chainDb.Close() - close(s.shutdownChan) - - return nil -} diff --git a/les/bloombits.go b/les/bloombits.go deleted file mode 100644 index 2871a90064..0000000000 --- a/les/bloombits.go +++ /dev/null @@ -1,74 +0,0 @@ -// 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 . - -package les - -import ( - "time" - - "github.com/ethereum/go-ethereum/common/bitutil" - "github.com/ethereum/go-ethereum/light" -) - -const ( - // bloomServiceThreads is the number of goroutines used globally by an Ethereum - // instance to service bloombits lookups for all running filters. - bloomServiceThreads = 16 - - // bloomFilterThreads is the number of goroutines used locally per filter to - // multiplex requests onto the global servicing goroutines. - bloomFilterThreads = 3 - - // bloomRetrievalBatch is the maximum number of bloom bit retrievals to service - // in a single batch. - bloomRetrievalBatch = 16 - - // bloomRetrievalWait is the maximum time to wait for enough bloom bit requests - // to accumulate request an entire batch (avoiding hysteresis). - bloomRetrievalWait = time.Microsecond * 100 -) - -// startBloomHandlers starts a batch of goroutines to accept bloom bit database -// retrievals from possibly a range of filters and serving the data to satisfy. -func (eth *LightEthereum) startBloomHandlers() { - for i := 0; i < bloomServiceThreads; i++ { - go func() { - for { - select { - case <-eth.shutdownChan: - return - - case request := <-eth.bloomRequests: - task := <-request - task.Bitsets = make([][]byte, len(task.Sections)) - compVectors, err := light.GetBloomBits(task.Context, eth.odr, task.Bit, task.Sections) - if err == nil { - for i := range task.Sections { - if blob, err := bitutil.DecompressBytes(compVectors[i], int(light.BloomTrieFrequency/8)); err == nil { - task.Bitsets[i] = blob - } else { - task.Error = err - } - } - } else { - task.Error = err - } - request <- task - } - } - }() - } -} diff --git a/les/distributor.go b/les/distributor.go deleted file mode 100644 index 159fa4c73f..0000000000 --- a/les/distributor.go +++ /dev/null @@ -1,285 +0,0 @@ -// 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 . - -// Package light implements on-demand retrieval capable state and chain objects -// for the Ethereum Light Client. -package les - -import ( - "container/list" - "errors" - "sync" - "time" -) - -// ErrNoPeers is returned if no peers capable of serving a queued request are available -var ErrNoPeers = errors.New("no suitable peers available") - -// requestDistributor implements a mechanism that distributes requests to -// suitable peers, obeying flow control rules and prioritizing them in creation -// order (even when a resend is necessary). -type requestDistributor struct { - reqQueue *list.List - lastReqOrder uint64 - peers map[distPeer]struct{} - peerLock sync.RWMutex - stopChn, loopChn chan struct{} - loopNextSent bool - lock sync.Mutex -} - -// distPeer is an LES server peer interface for the request distributor. -// waitBefore returns either the necessary waiting time before sending a request -// with the given upper estimated cost or the estimated remaining relative buffer -// value after sending such a request (in which case the request can be sent -// immediately). At least one of these values is always zero. -type distPeer interface { - waitBefore(uint64) (time.Duration, float64) - canQueue() bool - queueSend(f func()) -} - -// distReq is the request abstraction used by the distributor. It is based on -// three callback functions: -// - getCost returns the upper estimate of the cost of sending the request to a given peer -// - canSend tells if the server peer is suitable to serve the request -// - request prepares sending the request to the given peer and returns a function that -// does the actual sending. Request order should be preserved but the callback itself should not -// block until it is sent because other peers might still be able to receive requests while -// one of them is blocking. Instead, the returned function is put in the peer's send queue. -type distReq struct { - getCost func(distPeer) uint64 - canSend func(distPeer) bool - request func(distPeer) func() - - reqOrder uint64 - sentChn chan distPeer - element *list.Element -} - -// newRequestDistributor creates a new request distributor -func newRequestDistributor(peers *peerSet, stopChn chan struct{}) *requestDistributor { - d := &requestDistributor{ - reqQueue: list.New(), - loopChn: make(chan struct{}, 2), - stopChn: stopChn, - peers: make(map[distPeer]struct{}), - } - if peers != nil { - peers.notify(d) - } - go d.loop() - return d -} - -// registerPeer implements peerSetNotify -func (d *requestDistributor) registerPeer(p *peer) { - d.peerLock.Lock() - d.peers[p] = struct{}{} - d.peerLock.Unlock() -} - -// unregisterPeer implements peerSetNotify -func (d *requestDistributor) unregisterPeer(p *peer) { - d.peerLock.Lock() - delete(d.peers, p) - d.peerLock.Unlock() -} - -// registerTestPeer adds a new test peer -func (d *requestDistributor) registerTestPeer(p distPeer) { - d.peerLock.Lock() - d.peers[p] = struct{}{} - d.peerLock.Unlock() -} - -// distMaxWait is the maximum waiting time after which further necessary waiting -// times are recalculated based on new feedback from the servers -const distMaxWait = time.Millisecond * 10 - -// main event loop -func (d *requestDistributor) loop() { - for { - select { - case <-d.stopChn: - d.lock.Lock() - elem := d.reqQueue.Front() - for elem != nil { - close(elem.Value.(*distReq).sentChn) - elem = elem.Next() - } - d.lock.Unlock() - return - case <-d.loopChn: - d.lock.Lock() - d.loopNextSent = false - loop: - for { - peer, req, wait := d.nextRequest() - if req != nil && wait == 0 { - chn := req.sentChn // save sentChn because remove sets it to nil - d.remove(req) - send := req.request(peer) - if send != nil { - peer.queueSend(send) - } - chn <- peer - close(chn) - } else { - if wait == 0 { - // no request to send and nothing to wait for; the next - // queued request will wake up the loop - break loop - } - d.loopNextSent = true // a "next" signal has been sent, do not send another one until this one has been received - if wait > distMaxWait { - // waiting times may be reduced by incoming request replies, if it is too long, recalculate it periodically - wait = distMaxWait - } - go func() { - time.Sleep(wait) - d.loopChn <- struct{}{} - }() - break loop - } - } - d.lock.Unlock() - } - } -} - -// selectPeerItem represents a peer to be selected for a request by weightedRandomSelect -type selectPeerItem struct { - peer distPeer - req *distReq - weight int64 -} - -// Weight implements wrsItem interface -func (sp selectPeerItem) Weight() int64 { - return sp.weight -} - -// nextRequest returns the next possible request from any peer, along with the -// associated peer and necessary waiting time -func (d *requestDistributor) nextRequest() (distPeer, *distReq, time.Duration) { - checkedPeers := make(map[distPeer]struct{}) - elem := d.reqQueue.Front() - var ( - bestPeer distPeer - bestReq *distReq - bestWait time.Duration - sel *weightedRandomSelect - ) - - d.peerLock.RLock() - defer d.peerLock.RUnlock() - - for (len(d.peers) > 0 || elem == d.reqQueue.Front()) && elem != nil { - req := elem.Value.(*distReq) - canSend := false - for peer := range d.peers { - if _, ok := checkedPeers[peer]; !ok && peer.canQueue() && req.canSend(peer) { - canSend = true - cost := req.getCost(peer) - wait, bufRemain := peer.waitBefore(cost) - if wait == 0 { - if sel == nil { - sel = newWeightedRandomSelect() - } - sel.update(selectPeerItem{peer: peer, req: req, weight: int64(bufRemain*1000000) + 1}) - } else { - if bestReq == nil || wait < bestWait { - bestPeer = peer - bestReq = req - bestWait = wait - } - } - checkedPeers[peer] = struct{}{} - } - } - next := elem.Next() - if !canSend && elem == d.reqQueue.Front() { - close(req.sentChn) - d.remove(req) - } - elem = next - } - - if sel != nil { - c := sel.choose().(selectPeerItem) - return c.peer, c.req, 0 - } - return bestPeer, bestReq, bestWait -} - -// queue adds a request to the distribution queue, returns a channel where the -// receiving peer is sent once the request has been sent (request callback returned). -// If the request is cancelled or timed out without suitable peers, the channel is -// closed without sending any peer references to it. -func (d *requestDistributor) queue(r *distReq) chan distPeer { - d.lock.Lock() - defer d.lock.Unlock() - - if r.reqOrder == 0 { - d.lastReqOrder++ - r.reqOrder = d.lastReqOrder - } - - back := d.reqQueue.Back() - if back == nil || r.reqOrder > back.Value.(*distReq).reqOrder { - r.element = d.reqQueue.PushBack(r) - } else { - before := d.reqQueue.Front() - for before.Value.(*distReq).reqOrder < r.reqOrder { - before = before.Next() - } - r.element = d.reqQueue.InsertBefore(r, before) - } - - if !d.loopNextSent { - d.loopNextSent = true - d.loopChn <- struct{}{} - } - - r.sentChn = make(chan distPeer, 1) - return r.sentChn -} - -// cancel removes a request from the queue if it has not been sent yet (returns -// false if it has been sent already). It is guaranteed that the callback functions -// will not be called after cancel returns. -func (d *requestDistributor) cancel(r *distReq) bool { - d.lock.Lock() - defer d.lock.Unlock() - - if r.sentChn == nil { - return false - } - - close(r.sentChn) - d.remove(r) - return true -} - -// remove removes a request from the queue -func (d *requestDistributor) remove(r *distReq) { - r.sentChn = nil - if r.element != nil { - d.reqQueue.Remove(r.element) - r.element = nil - } -} diff --git a/les/distributor_test.go b/les/distributor_test.go deleted file mode 100644 index 2891bcab49..0000000000 --- a/les/distributor_test.go +++ /dev/null @@ -1,185 +0,0 @@ -// 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 . - -// Package light implements on-demand retrieval capable state and chain objects -// for the Ethereum Light Client. -package les - -import ( - "math/rand" - "sync" - "testing" - "time" -) - -type testDistReq struct { - cost, procTime, order uint64 - canSendTo map[*testDistPeer]struct{} -} - -func (r *testDistReq) getCost(dp distPeer) uint64 { - return r.cost -} - -func (r *testDistReq) canSend(dp distPeer) bool { - _, ok := r.canSendTo[dp.(*testDistPeer)] - return ok -} - -func (r *testDistReq) request(dp distPeer) func() { - return func() { dp.(*testDistPeer).send(r) } -} - -type testDistPeer struct { - sent []*testDistReq - sumCost uint64 - lock sync.RWMutex -} - -func (p *testDistPeer) send(r *testDistReq) { - p.lock.Lock() - defer p.lock.Unlock() - - p.sent = append(p.sent, r) - p.sumCost += r.cost -} - -func (p *testDistPeer) worker(t *testing.T, checkOrder bool, stop chan struct{}) { - var last uint64 - for { - wait := time.Millisecond - p.lock.Lock() - if len(p.sent) > 0 { - rq := p.sent[0] - wait = time.Duration(rq.procTime) - p.sumCost -= rq.cost - if checkOrder { - if rq.order <= last { - t.Errorf("Requests processed in wrong order") - } - last = rq.order - } - p.sent = p.sent[1:] - } - p.lock.Unlock() - select { - case <-stop: - return - case <-time.After(wait): - } - } -} - -const ( - testDistBufLimit = 10000000 - testDistMaxCost = 1000000 - testDistPeerCount = 5 - testDistReqCount = 50000 - testDistMaxResendCount = 3 -) - -func (p *testDistPeer) waitBefore(cost uint64) (time.Duration, float64) { - p.lock.RLock() - sumCost := p.sumCost + cost - p.lock.RUnlock() - if sumCost < testDistBufLimit { - return 0, float64(testDistBufLimit-sumCost) / float64(testDistBufLimit) - } - return time.Duration(sumCost - testDistBufLimit), 0 -} - -func (p *testDistPeer) canQueue() bool { - return true -} - -func (p *testDistPeer) queueSend(f func()) { - f() -} - -func TestRequestDistributor(t *testing.T) { - testRequestDistributor(t, false) -} - -func TestRequestDistributorResend(t *testing.T) { - testRequestDistributor(t, true) -} - -func testRequestDistributor(t *testing.T, resend bool) { - stop := make(chan struct{}) - defer close(stop) - - dist := newRequestDistributor(nil, stop) - var peers [testDistPeerCount]*testDistPeer - for i := range peers { - peers[i] = &testDistPeer{} - go peers[i].worker(t, !resend, stop) - dist.registerTestPeer(peers[i]) - } - - var wg sync.WaitGroup - - for i := 1; i <= testDistReqCount; i++ { - cost := uint64(rand.Int63n(testDistMaxCost)) - procTime := uint64(rand.Int63n(int64(cost + 1))) - rq := &testDistReq{ - cost: cost, - procTime: procTime, - order: uint64(i), - canSendTo: make(map[*testDistPeer]struct{}), - } - for _, peer := range peers { - if rand.Intn(2) != 0 { - rq.canSendTo[peer] = struct{}{} - } - } - - wg.Add(1) - req := &distReq{ - getCost: rq.getCost, - canSend: rq.canSend, - request: rq.request, - } - chn := dist.queue(req) - go func() { - cnt := 1 - if resend && len(rq.canSendTo) != 0 { - cnt = rand.Intn(testDistMaxResendCount) + 1 - } - for i := 0; i < cnt; i++ { - if i != 0 { - chn = dist.queue(req) - } - p := <-chn - if p == nil { - if len(rq.canSendTo) != 0 { - t.Errorf("Request that could have been sent was dropped") - } - } else { - peer := p.(*testDistPeer) - if _, ok := rq.canSendTo[peer]; !ok { - t.Errorf("Request sent to wrong peer") - } - } - } - wg.Done() - }() - if rand.Intn(1000) == 0 { - time.Sleep(time.Duration(rand.Intn(5000000))) - } - } - - wg.Wait() -} diff --git a/les/execqueue.go b/les/execqueue.go deleted file mode 100644 index 614721bf0d..0000000000 --- a/les/execqueue.go +++ /dev/null @@ -1,97 +0,0 @@ -// 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 . - -package les - -import "sync" - -// execQueue implements a queue that executes function calls in a single thread, -// in the same order as they have been queued. -type execQueue struct { - mu sync.Mutex - cond *sync.Cond - funcs []func() - closeWait chan struct{} -} - -// newExecQueue creates a new execution queue. -func newExecQueue(capacity int) *execQueue { - q := &execQueue{funcs: make([]func(), 0, capacity)} - q.cond = sync.NewCond(&q.mu) - go q.loop() - return q -} - -func (q *execQueue) loop() { - for f := q.waitNext(false); f != nil; f = q.waitNext(true) { - f() - } - close(q.closeWait) -} - -func (q *execQueue) waitNext(drop bool) (f func()) { - q.mu.Lock() - if drop { - // Remove the function that just executed. We do this here instead of when - // dequeuing so len(q.funcs) includes the function that is running. - q.funcs = append(q.funcs[:0], q.funcs[1:]...) - } - for !q.isClosed() { - if len(q.funcs) > 0 { - f = q.funcs[0] - break - } - q.cond.Wait() - } - q.mu.Unlock() - return f -} - -func (q *execQueue) isClosed() bool { - return q.closeWait != nil -} - -// canQueue returns true if more function calls can be added to the execution queue. -func (q *execQueue) canQueue() bool { - q.mu.Lock() - ok := !q.isClosed() && len(q.funcs) < cap(q.funcs) - q.mu.Unlock() - return ok -} - -// queue adds a function call to the execution queue. Returns true if successful. -func (q *execQueue) queue(f func()) bool { - q.mu.Lock() - ok := !q.isClosed() && len(q.funcs) < cap(q.funcs) - if ok { - q.funcs = append(q.funcs, f) - q.cond.Signal() - } - q.mu.Unlock() - return ok -} - -// quit stops the exec queue. -// quit waits for the current execution to finish before returning. -func (q *execQueue) quit() { - q.mu.Lock() - if !q.isClosed() { - q.closeWait = make(chan struct{}) - q.cond.Signal() - } - q.mu.Unlock() - <-q.closeWait -} diff --git a/les/execqueue_test.go b/les/execqueue_test.go deleted file mode 100644 index cd45b03f22..0000000000 --- a/les/execqueue_test.go +++ /dev/null @@ -1,62 +0,0 @@ -// 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 . - -package les - -import ( - "testing" -) - -func TestExecQueue(t *testing.T) { - var ( - N = 10000 - q = newExecQueue(N) - counter int - execd = make(chan int) - testexit = make(chan struct{}) - ) - defer q.quit() - defer close(testexit) - - check := func(state string, wantOK bool) { - c := counter - counter++ - qf := func() { - select { - case execd <- c: - case <-testexit: - } - } - if q.canQueue() != wantOK { - t.Fatalf("canQueue() == %t for %s", !wantOK, state) - } - if q.queue(qf) != wantOK { - t.Fatalf("canQueue() == %t for %s", !wantOK, state) - } - } - - for i := 0; i < N; i++ { - check("queue below cap", true) - } - check("full queue", false) - for i := 0; i < N; i++ { - if c := <-execd; c != i { - t.Fatal("execution out of order") - } - } - q.quit() - check("closed queue", false) -} diff --git a/les/fetcher.go b/les/fetcher.go deleted file mode 100644 index 59d3a2aa3c..0000000000 --- a/les/fetcher.go +++ /dev/null @@ -1,769 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package les implements the Light Ethereum Subprotocol. -package les - -import ( - "math/big" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/log" -) - -const ( - blockDelayTimeout = time.Second * 10 // timeout for a peer to announce a head that has already been confirmed by others - maxNodeCount = 20 // maximum number of fetcherTreeNode entries remembered for each peer -) - -// lightFetcher implements retrieval of newly announced headers. It also provides a peerHasBlock function for the -// ODR system to ensure that we only request data related to a certain block from peers who have already processed -// and announced that block. -type lightFetcher struct { - pm *ProtocolManager - odr *LesOdr - chain *light.LightChain - - lock sync.Mutex // lock protects access to the fetcher's internal state variables except sent requests - maxConfirmedTd *big.Int - peers map[*peer]*fetcherPeerInfo - lastUpdateStats *updateStatsEntry - syncing bool - syncDone chan *peer - - reqMu sync.RWMutex // reqMu protects access to sent header fetch requests - requested map[uint64]fetchRequest - deliverChn chan fetchResponse - timeoutChn chan uint64 - requestChn chan bool // true if initiated from outside -} - -// fetcherPeerInfo holds fetcher-specific information about each active peer -type fetcherPeerInfo struct { - root, lastAnnounced *fetcherTreeNode - nodeCnt int - confirmedTd *big.Int - bestConfirmed *fetcherTreeNode - nodeByHash map[common.Hash]*fetcherTreeNode - firstUpdateStats *updateStatsEntry -} - -// fetcherTreeNode is a node of a tree that holds information about blocks recently -// announced and confirmed by a certain peer. Each new announce message from a peer -// adds nodes to the tree, based on the previous announced head and the reorg depth. -// There are three possible states for a tree node: -// - announced: not downloaded (known) yet, but we know its head, number and td -// - intermediate: not known, hash and td are empty, they are filled out when it becomes known -// - known: both announced by this peer and downloaded (from any peer). -// This structure makes it possible to always know which peer has a certain block, -// which is necessary for selecting a suitable peer for ODR requests and also for -// canonizing new heads. It also helps to always download the minimum necessary -// amount of headers with a single request. -type fetcherTreeNode struct { - hash common.Hash - number uint64 - td *big.Int - known, requested bool - parent *fetcherTreeNode - children []*fetcherTreeNode -} - -// fetchRequest represents a header download request -type fetchRequest struct { - hash common.Hash - amount uint64 - peer *peer - sent mclock.AbsTime - timeout bool -} - -// fetchResponse represents a header download response -type fetchResponse struct { - reqID uint64 - headers []*types.Header - peer *peer -} - -// newLightFetcher creates a new light fetcher -func newLightFetcher(pm *ProtocolManager) *lightFetcher { - f := &lightFetcher{ - pm: pm, - chain: pm.blockchain.(*light.LightChain), - odr: pm.odr, - peers: make(map[*peer]*fetcherPeerInfo), - deliverChn: make(chan fetchResponse, 100), - requested: make(map[uint64]fetchRequest), - timeoutChn: make(chan uint64), - requestChn: make(chan bool, 100), - syncDone: make(chan *peer), - maxConfirmedTd: big.NewInt(0), - } - pm.peers.notify(f) - - f.pm.wg.Add(1) - go f.syncLoop() - return f -} - -// syncLoop is the main event loop of the light fetcher -func (f *lightFetcher) syncLoop() { - requesting := false - defer f.pm.wg.Done() - for { - select { - case <-f.pm.quitSync: - return - // when a new announce is received, request loop keeps running until - // no further requests are necessary or possible - case newAnnounce := <-f.requestChn: - f.lock.Lock() - s := requesting - requesting = false - var ( - rq *distReq - reqID uint64 - ) - if !f.syncing && !(newAnnounce && s) { - rq, reqID = f.nextRequest() - } - syncing := f.syncing - f.lock.Unlock() - - if rq != nil { - requesting = true - _, ok := <-f.pm.reqDist.queue(rq) - if !ok { - f.requestChn <- false - } - - if !syncing { - go func() { - time.Sleep(softRequestTimeout) - f.reqMu.Lock() - req, ok := f.requested[reqID] - if ok { - req.timeout = true - f.requested[reqID] = req - } - f.reqMu.Unlock() - // keep starting new requests while possible - f.requestChn <- false - }() - } - } - case reqID := <-f.timeoutChn: - f.reqMu.Lock() - req, ok := f.requested[reqID] - if ok { - delete(f.requested, reqID) - } - f.reqMu.Unlock() - if ok { - f.pm.serverPool.adjustResponseTime(req.peer.poolEntry, time.Duration(mclock.Now()-req.sent), true) - req.peer.Log().Debug("Fetching data timed out hard") - go f.pm.removePeer(req.peer.id) - } - case resp := <-f.deliverChn: - f.reqMu.Lock() - req, ok := f.requested[resp.reqID] - if ok && req.peer != resp.peer { - ok = false - } - if ok { - delete(f.requested, resp.reqID) - } - f.reqMu.Unlock() - if ok { - f.pm.serverPool.adjustResponseTime(req.peer.poolEntry, time.Duration(mclock.Now()-req.sent), req.timeout) - } - f.lock.Lock() - if !ok || !(f.syncing || f.processResponse(req, resp)) { - resp.peer.Log().Debug("Failed processing response") - go f.pm.removePeer(resp.peer.id) - } - f.lock.Unlock() - case p := <-f.syncDone: - f.lock.Lock() - p.Log().Debug("Done synchronising with peer") - f.checkSyncedHeaders(p) - f.syncing = false - f.lock.Unlock() - } - } -} - -// registerPeer adds a new peer to the fetcher's peer set -func (f *lightFetcher) registerPeer(p *peer) { - p.lock.Lock() - p.hasBlock = func(hash common.Hash, number uint64) bool { - return f.peerHasBlock(p, hash, number) - } - p.lock.Unlock() - - f.lock.Lock() - defer f.lock.Unlock() - - f.peers[p] = &fetcherPeerInfo{nodeByHash: make(map[common.Hash]*fetcherTreeNode)} -} - -// unregisterPeer removes a new peer from the fetcher's peer set -func (f *lightFetcher) unregisterPeer(p *peer) { - p.lock.Lock() - p.hasBlock = nil - p.lock.Unlock() - - f.lock.Lock() - defer f.lock.Unlock() - - // check for potential timed out block delay statistics - f.checkUpdateStats(p, nil) - delete(f.peers, p) -} - -// announce processes a new announcement message received from a peer, adding new -// nodes to the peer's block tree and removing old nodes if necessary -func (f *lightFetcher) announce(p *peer, head *announceData) { - f.lock.Lock() - defer f.lock.Unlock() - p.Log().Debug("Received new announcement", "number", head.Number, "hash", head.Hash, "reorg", head.ReorgDepth) - - fp := f.peers[p] - if fp == nil { - p.Log().Debug("Announcement from unknown peer") - return - } - - if fp.lastAnnounced != nil && head.Td.Cmp(fp.lastAnnounced.td) <= 0 { - // announced tds should be strictly monotonic - p.Log().Debug("Received non-monotonic td", "current", head.Td, "previous", fp.lastAnnounced.td) - go f.pm.removePeer(p.id) - return - } - - n := fp.lastAnnounced - for i := uint64(0); i < head.ReorgDepth; i++ { - if n == nil { - break - } - n = n.parent - } - if n != nil { - // n is now the reorg common ancestor, add a new branch of nodes - // check if the node count is too high to add new nodes - locked := false - for uint64(fp.nodeCnt)+head.Number-n.number > maxNodeCount && fp.root != nil { - if !locked { - f.chain.LockChain() - defer f.chain.UnlockChain() - locked = true - } - // if one of root's children is canonical, keep it, delete other branches and root itself - var newRoot *fetcherTreeNode - for i, nn := range fp.root.children { - if rawdb.ReadCanonicalHash(f.pm.chainDb, nn.number) == nn.hash { - fp.root.children = append(fp.root.children[:i], fp.root.children[i+1:]...) - nn.parent = nil - newRoot = nn - break - } - } - fp.deleteNode(fp.root) - if n == fp.root { - n = newRoot - } - fp.root = newRoot - if newRoot == nil || !f.checkKnownNode(p, newRoot) { - fp.bestConfirmed = nil - fp.confirmedTd = nil - } - - if n == nil { - break - } - } - if n != nil { - for n.number < head.Number { - nn := &fetcherTreeNode{number: n.number + 1, parent: n} - n.children = append(n.children, nn) - n = nn - fp.nodeCnt++ - } - n.hash = head.Hash - n.td = head.Td - fp.nodeByHash[n.hash] = n - } - } - if n == nil { - // could not find reorg common ancestor or had to delete entire tree, a new root and a resync is needed - if fp.root != nil { - fp.deleteNode(fp.root) - } - n = &fetcherTreeNode{hash: head.Hash, number: head.Number, td: head.Td} - fp.root = n - fp.nodeCnt++ - fp.nodeByHash[n.hash] = n - fp.bestConfirmed = nil - fp.confirmedTd = nil - } - - f.checkKnownNode(p, n) - p.lock.Lock() - p.headInfo = head - fp.lastAnnounced = n - p.lock.Unlock() - f.checkUpdateStats(p, nil) - f.requestChn <- true -} - -// peerHasBlock returns true if we can assume the peer knows the given block -// based on its announcements -func (f *lightFetcher) peerHasBlock(p *peer, hash common.Hash, number uint64) bool { - f.lock.Lock() - defer f.lock.Unlock() - - if f.syncing { - // always return true when syncing - // false positives are acceptable, a more sophisticated condition can be implemented later - return true - } - - fp := f.peers[p] - if fp == nil || fp.root == nil { - return false - } - - if number >= fp.root.number { - // it is recent enough that if it is known, is should be in the peer's block tree - return fp.nodeByHash[hash] != nil - } - f.chain.LockChain() - defer f.chain.UnlockChain() - // if it's older than the peer's block tree root but it's in the same canonical chain - // as the root, we can still be sure the peer knows it - // - // when syncing, just check if it is part of the known chain, there is nothing better we - // can do since we do not know the most recent block hash yet - return rawdb.ReadCanonicalHash(f.pm.chainDb, fp.root.number) == fp.root.hash && rawdb.ReadCanonicalHash(f.pm.chainDb, number) == hash -} - -// requestAmount calculates the amount of headers to be downloaded starting -// from a certain head backwards -func (f *lightFetcher) requestAmount(p *peer, n *fetcherTreeNode) uint64 { - amount := uint64(0) - nn := n - for nn != nil && !f.checkKnownNode(p, nn) { - nn = nn.parent - amount++ - } - if nn == nil { - amount = n.number - } - return amount -} - -// requestedID tells if a certain reqID has been requested by the fetcher -func (f *lightFetcher) requestedID(reqID uint64) bool { - f.reqMu.RLock() - _, ok := f.requested[reqID] - f.reqMu.RUnlock() - return ok -} - -// nextRequest selects the peer and announced head to be requested next, amount -// to be downloaded starting from the head backwards is also returned -func (f *lightFetcher) nextRequest() (*distReq, uint64) { - var ( - bestHash common.Hash - bestAmount uint64 - ) - bestTd := f.maxConfirmedTd - bestSyncing := false - - for p, fp := range f.peers { - for hash, n := range fp.nodeByHash { - if !f.checkKnownNode(p, n) && !n.requested && (bestTd == nil || n.td.Cmp(bestTd) >= 0) { - amount := f.requestAmount(p, n) - if bestTd == nil || n.td.Cmp(bestTd) > 0 || amount < bestAmount { - bestHash = hash - bestAmount = amount - bestTd = n.td - bestSyncing = fp.bestConfirmed == nil || fp.root == nil || !f.checkKnownNode(p, fp.root) - } - } - } - } - if bestTd == f.maxConfirmedTd { - return nil, 0 - } - - f.syncing = bestSyncing - - var rq *distReq - reqID := genReqID() - if f.syncing { - rq = &distReq{ - getCost: func(dp distPeer) uint64 { - return 0 - }, - canSend: func(dp distPeer) bool { - p := dp.(*peer) - f.lock.Lock() - defer f.lock.Unlock() - - fp := f.peers[p] - return fp != nil && fp.nodeByHash[bestHash] != nil - }, - request: func(dp distPeer) func() { - go func() { - p := dp.(*peer) - p.Log().Debug("Synchronisation started") - f.pm.synchronise(p) - f.syncDone <- p - }() - return nil - }, - } - } else { - rq = &distReq{ - getCost: func(dp distPeer) uint64 { - p := dp.(*peer) - return p.GetRequestCost(GetBlockHeadersMsg, int(bestAmount)) - }, - canSend: func(dp distPeer) bool { - p := dp.(*peer) - f.lock.Lock() - defer f.lock.Unlock() - - fp := f.peers[p] - if fp == nil { - return false - } - n := fp.nodeByHash[bestHash] - return n != nil && !n.requested - }, - request: func(dp distPeer) func() { - p := dp.(*peer) - f.lock.Lock() - fp := f.peers[p] - if fp != nil { - n := fp.nodeByHash[bestHash] - if n != nil { - n.requested = true - } - } - f.lock.Unlock() - - cost := p.GetRequestCost(GetBlockHeadersMsg, int(bestAmount)) - p.fcServer.QueueRequest(reqID, cost) - f.reqMu.Lock() - f.requested[reqID] = fetchRequest{hash: bestHash, amount: bestAmount, peer: p, sent: mclock.Now()} - f.reqMu.Unlock() - go func() { - time.Sleep(hardRequestTimeout) - f.timeoutChn <- reqID - }() - return func() { p.RequestHeadersByHash(reqID, cost, bestHash, int(bestAmount), 0, true) } - }, - } - } - return rq, reqID -} - -// deliverHeaders delivers header download request responses for processing -func (f *lightFetcher) deliverHeaders(peer *peer, reqID uint64, headers []*types.Header) { - f.deliverChn <- fetchResponse{reqID: reqID, headers: headers, peer: peer} -} - -// processResponse processes header download request responses, returns true if successful -func (f *lightFetcher) processResponse(req fetchRequest, resp fetchResponse) bool { - if uint64(len(resp.headers)) != req.amount || resp.headers[0].Hash() != req.hash { - req.peer.Log().Debug("Response content mismatch", "requested", len(resp.headers), "reqfrom", resp.headers[0], "delivered", req.amount, "delfrom", req.hash) - return false - } - headers := make([]*types.Header, req.amount) - for i, header := range resp.headers { - headers[int(req.amount)-1-i] = header - } - if _, err := f.chain.InsertHeaderChain(headers, 1); err != nil { - if err == consensus.ErrFutureBlock { - return true - } - log.Debug("Failed to insert header chain", "err", err) - return false - } - tds := make([]*big.Int, len(headers)) - for i, header := range headers { - td := f.chain.GetTd(header.Hash(), header.Number.Uint64()) - if td == nil { - log.Debug("Total difficulty not found for header", "index", i+1, "number", header.Number, "hash", header.Hash()) - return false - } - tds[i] = td - } - f.newHeaders(headers, tds) - return true -} - -// newHeaders updates the block trees of all active peers according to a newly -// downloaded and validated batch or headers -func (f *lightFetcher) newHeaders(headers []*types.Header, tds []*big.Int) { - var maxTd *big.Int - for p, fp := range f.peers { - if !f.checkAnnouncedHeaders(fp, headers, tds) { - p.Log().Debug("Inconsistent announcement") - go f.pm.removePeer(p.id) - } - if fp.confirmedTd != nil && (maxTd == nil || maxTd.Cmp(fp.confirmedTd) > 0) { - maxTd = fp.confirmedTd - } - } - if maxTd != nil { - f.updateMaxConfirmedTd(maxTd) - } -} - -// checkAnnouncedHeaders updates peer's block tree if necessary after validating -// a batch of headers. It searches for the latest header in the batch that has a -// matching tree node (if any), and if it has not been marked as known already, -// sets it and its parents to known (even those which are older than the currently -// validated ones). Return value shows if all hashes, numbers and Tds matched -// correctly to the announced values (otherwise the peer should be dropped). -func (f *lightFetcher) checkAnnouncedHeaders(fp *fetcherPeerInfo, headers []*types.Header, tds []*big.Int) bool { - var ( - n *fetcherTreeNode - header *types.Header - td *big.Int - ) - - for i := len(headers) - 1; ; i-- { - if i < 0 { - if n == nil { - // no more headers and nothing to match - return true - } - // we ran out of recently delivered headers but have not reached a node known by this peer yet, continue matching - hash, number := header.ParentHash, header.Number.Uint64()-1 - td = f.chain.GetTd(hash, number) - header = f.chain.GetHeader(hash, number) - if header == nil || td == nil { - log.Error("Missing parent of validated header", "hash", hash, "number", number) - return false - } - } else { - header = headers[i] - td = tds[i] - } - hash := header.Hash() - number := header.Number.Uint64() - if n == nil { - n = fp.nodeByHash[hash] - } - if n != nil { - if n.td == nil { - // node was unannounced - if nn := fp.nodeByHash[hash]; nn != nil { - // if there was already a node with the same hash, continue there and drop this one - nn.children = append(nn.children, n.children...) - n.children = nil - fp.deleteNode(n) - n = nn - } else { - n.hash = hash - n.td = td - fp.nodeByHash[hash] = n - } - } - // check if it matches the header - if n.hash != hash || n.number != number || n.td.Cmp(td) != 0 { - // peer has previously made an invalid announcement - return false - } - if n.known { - // we reached a known node that matched our expectations, return with success - return true - } - n.known = true - if fp.confirmedTd == nil || td.Cmp(fp.confirmedTd) > 0 { - fp.confirmedTd = td - fp.bestConfirmed = n - } - n = n.parent - if n == nil { - return true - } - } - } -} - -// checkSyncedHeaders updates peer's block tree after synchronisation by marking -// downloaded headers as known. If none of the announced headers are found after -// syncing, the peer is dropped. -func (f *lightFetcher) checkSyncedHeaders(p *peer) { - fp := f.peers[p] - if fp == nil { - p.Log().Debug("Unknown peer to check sync headers") - return - } - n := fp.lastAnnounced - var td *big.Int - for n != nil { - if td = f.chain.GetTd(n.hash, n.number); td != nil { - break - } - n = n.parent - } - // now n is the latest downloaded header after syncing - if n == nil { - p.Log().Debug("Synchronisation failed") - go f.pm.removePeer(p.id) - } else { - header := f.chain.GetHeader(n.hash, n.number) - f.newHeaders([]*types.Header{header}, []*big.Int{td}) - } -} - -// checkKnownNode checks if a block tree node is known (downloaded and validated) -// If it was not known previously but found in the database, sets its known flag -func (f *lightFetcher) checkKnownNode(p *peer, n *fetcherTreeNode) bool { - if n.known { - return true - } - td := f.chain.GetTd(n.hash, n.number) - if td == nil { - return false - } - header := f.chain.GetHeader(n.hash, n.number) - // check the availability of both header and td because reads are not protected by chain db mutex - // Note: returning false is always safe here - if header == nil { - return false - } - - fp := f.peers[p] - if fp == nil { - p.Log().Debug("Unknown peer to check known nodes") - return false - } - if !f.checkAnnouncedHeaders(fp, []*types.Header{header}, []*big.Int{td}) { - p.Log().Debug("Inconsistent announcement") - go f.pm.removePeer(p.id) - } - if fp.confirmedTd != nil { - f.updateMaxConfirmedTd(fp.confirmedTd) - } - return n.known -} - -// deleteNode deletes a node and its child subtrees from a peer's block tree -func (fp *fetcherPeerInfo) deleteNode(n *fetcherTreeNode) { - if n.parent != nil { - for i, nn := range n.parent.children { - if nn == n { - n.parent.children = append(n.parent.children[:i], n.parent.children[i+1:]...) - break - } - } - } - for { - if n.td != nil { - delete(fp.nodeByHash, n.hash) - } - fp.nodeCnt-- - if len(n.children) == 0 { - return - } - for i, nn := range n.children { - if i == 0 { - n = nn - } else { - fp.deleteNode(nn) - } - } - } -} - -// updateStatsEntry items form a linked list that is expanded with a new item every time a new head with a higher Td -// than the previous one has been downloaded and validated. The list contains a series of maximum confirmed Td values -// and the time these values have been confirmed, both increasing monotonically. A maximum confirmed Td is calculated -// both globally for all peers and also for each individual peer (meaning that the given peer has announced the head -// and it has also been downloaded from any peer, either before or after the given announcement). -// The linked list has a global tail where new confirmed Td entries are added and a separate head for each peer, -// pointing to the next Td entry that is higher than the peer's max confirmed Td (nil if it has already confirmed -// the current global head). -type updateStatsEntry struct { - time mclock.AbsTime - td *big.Int - next *updateStatsEntry -} - -// updateMaxConfirmedTd updates the block delay statistics of active peers. Whenever a new highest Td is confirmed, -// adds it to the end of a linked list together with the time it has been confirmed. Then checks which peers have -// already confirmed a head with the same or higher Td (which counts as zero block delay) and updates their statistics. -// Those who have not confirmed such a head by now will be updated by a subsequent checkUpdateStats call with a -// positive block delay value. -func (f *lightFetcher) updateMaxConfirmedTd(td *big.Int) { - if f.maxConfirmedTd == nil || td.Cmp(f.maxConfirmedTd) > 0 { - f.maxConfirmedTd = td - newEntry := &updateStatsEntry{ - time: mclock.Now(), - td: td, - } - if f.lastUpdateStats != nil { - f.lastUpdateStats.next = newEntry - } - f.lastUpdateStats = newEntry - for p := range f.peers { - f.checkUpdateStats(p, newEntry) - } - } -} - -// checkUpdateStats checks those peers who have not confirmed a certain highest Td (or a larger one) by the time it -// has been confirmed by another peer. If they have confirmed such a head by now, their stats are updated with the -// block delay which is (this peer's confirmation time)-(first confirmation time). After blockDelayTimeout has passed, -// the stats are updated with blockDelayTimeout value. In either case, the confirmed or timed out updateStatsEntry -// items are removed from the head of the linked list. -// If a new entry has been added to the global tail, it is passed as a parameter here even though this function -// assumes that it has already been added, so that if the peer's list is empty (all heads confirmed, head is nil), -// it can set the new head to newEntry. -func (f *lightFetcher) checkUpdateStats(p *peer, newEntry *updateStatsEntry) { - now := mclock.Now() - fp := f.peers[p] - if fp == nil { - p.Log().Debug("Unknown peer to check update stats") - return - } - if newEntry != nil && fp.firstUpdateStats == nil { - fp.firstUpdateStats = newEntry - } - for fp.firstUpdateStats != nil && fp.firstUpdateStats.time <= now-mclock.AbsTime(blockDelayTimeout) { - f.pm.serverPool.adjustBlockDelay(p.poolEntry, blockDelayTimeout) - fp.firstUpdateStats = fp.firstUpdateStats.next - } - if fp.confirmedTd != nil { - for fp.firstUpdateStats != nil && fp.firstUpdateStats.td.Cmp(fp.confirmedTd) <= 0 { - f.pm.serverPool.adjustBlockDelay(p.poolEntry, time.Duration(now-fp.firstUpdateStats.time)) - fp.firstUpdateStats = fp.firstUpdateStats.next - } - } -} diff --git a/les/flowcontrol/control.go b/les/flowcontrol/control.go deleted file mode 100644 index d50eb809cc..0000000000 --- a/les/flowcontrol/control.go +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package flowcontrol implements a client side flow control mechanism -package flowcontrol - -import ( - "sync" - "time" - - "github.com/ethereum/go-ethereum/common/mclock" -) - -const fcTimeConst = time.Millisecond - -type ServerParams struct { - BufLimit, MinRecharge uint64 -} - -type ClientNode struct { - params *ServerParams - bufValue uint64 - lastTime mclock.AbsTime - lock sync.Mutex - cm *ClientManager - cmNode *cmNode -} - -func NewClientNode(cm *ClientManager, params *ServerParams) *ClientNode { - node := &ClientNode{ - cm: cm, - params: params, - bufValue: params.BufLimit, - lastTime: mclock.Now(), - } - node.cmNode = cm.addNode(node) - return node -} - -func (peer *ClientNode) Remove(cm *ClientManager) { - cm.removeNode(peer.cmNode) -} - -func (peer *ClientNode) recalcBV(time mclock.AbsTime) { - dt := uint64(time - peer.lastTime) - if time < peer.lastTime { - dt = 0 - } - peer.bufValue += peer.params.MinRecharge * dt / uint64(fcTimeConst) - if peer.bufValue > peer.params.BufLimit { - peer.bufValue = peer.params.BufLimit - } - peer.lastTime = time -} - -func (peer *ClientNode) AcceptRequest() (uint64, bool) { - peer.lock.Lock() - defer peer.lock.Unlock() - - time := mclock.Now() - peer.recalcBV(time) - return peer.bufValue, peer.cm.accept(peer.cmNode, time) -} - -func (peer *ClientNode) RequestProcessed(cost uint64) (bv, realCost uint64) { - peer.lock.Lock() - defer peer.lock.Unlock() - - time := mclock.Now() - peer.recalcBV(time) - peer.bufValue -= cost - peer.recalcBV(time) - rcValue, rcost := peer.cm.processed(peer.cmNode, time) - if rcValue < peer.params.BufLimit { - bv := peer.params.BufLimit - rcValue - if bv > peer.bufValue { - peer.bufValue = bv - } - } - return peer.bufValue, rcost -} - -type ServerNode struct { - bufEstimate uint64 - lastTime mclock.AbsTime - params *ServerParams - sumCost uint64 // sum of req costs sent to this server - pending map[uint64]uint64 // value = sumCost after sending the given req - lock sync.RWMutex -} - -func NewServerNode(params *ServerParams) *ServerNode { - return &ServerNode{ - bufEstimate: params.BufLimit, - lastTime: mclock.Now(), - params: params, - pending: make(map[uint64]uint64), - } -} - -func (peer *ServerNode) recalcBLE(time mclock.AbsTime) { - dt := uint64(time - peer.lastTime) - if time < peer.lastTime { - dt = 0 - } - peer.bufEstimate += peer.params.MinRecharge * dt / uint64(fcTimeConst) - if peer.bufEstimate > peer.params.BufLimit { - peer.bufEstimate = peer.params.BufLimit - } - peer.lastTime = time -} - -// safetyMargin is added to the flow control waiting time when estimated buffer value is low -const safetyMargin = time.Millisecond - -func (peer *ServerNode) canSend(maxCost uint64) (time.Duration, float64) { - peer.recalcBLE(mclock.Now()) - maxCost += uint64(safetyMargin) * peer.params.MinRecharge / uint64(fcTimeConst) - if maxCost > peer.params.BufLimit { - maxCost = peer.params.BufLimit - } - if peer.bufEstimate >= maxCost { - return 0, float64(peer.bufEstimate-maxCost) / float64(peer.params.BufLimit) - } - return time.Duration((maxCost - peer.bufEstimate) * uint64(fcTimeConst) / peer.params.MinRecharge), 0 -} - -// CanSend returns the minimum waiting time required before sending a request -// with the given maximum estimated cost. Second return value is the relative -// estimated buffer level after sending the request (divided by BufLimit). -func (peer *ServerNode) CanSend(maxCost uint64) (time.Duration, float64) { - peer.lock.RLock() - defer peer.lock.RUnlock() - - return peer.canSend(maxCost) -} - -// QueueRequest should be called when the request has been assigned to the given -// server node, before putting it in the send queue. It is mandatory that requests -// are sent in the same order as the QueueRequest calls are made. -func (peer *ServerNode) QueueRequest(reqID, maxCost uint64) { - peer.lock.Lock() - defer peer.lock.Unlock() - - peer.bufEstimate -= maxCost - peer.sumCost += maxCost - peer.pending[reqID] = peer.sumCost -} - -// GotReply adjusts estimated buffer value according to the value included in -// the latest request reply. -func (peer *ServerNode) GotReply(reqID, bv uint64) { - - peer.lock.Lock() - defer peer.lock.Unlock() - - if bv > peer.params.BufLimit { - bv = peer.params.BufLimit - } - sc, ok := peer.pending[reqID] - if !ok { - return - } - delete(peer.pending, reqID) - cc := peer.sumCost - sc - peer.bufEstimate = 0 - if bv > cc { - peer.bufEstimate = bv - cc - } - peer.lastTime = mclock.Now() -} diff --git a/les/flowcontrol/manager.go b/les/flowcontrol/manager.go deleted file mode 100644 index 28cc6f0fe7..0000000000 --- a/les/flowcontrol/manager.go +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package flowcontrol implements a client side flow control mechanism -package flowcontrol - -import ( - "sync" - "time" - - "github.com/ethereum/go-ethereum/common/mclock" -) - -const rcConst = 1000000 - -type cmNode struct { - node *ClientNode - lastUpdate mclock.AbsTime - serving, recharging bool - rcWeight uint64 - rcValue, rcDelta, startValue int64 - finishRecharge mclock.AbsTime -} - -func (node *cmNode) update(time mclock.AbsTime) { - dt := int64(time - node.lastUpdate) - node.rcValue += node.rcDelta * dt / rcConst - node.lastUpdate = time - if node.recharging && time >= node.finishRecharge { - node.recharging = false - node.rcDelta = 0 - node.rcValue = 0 - } -} - -func (node *cmNode) set(serving bool, simReqCnt, sumWeight uint64) { - if node.serving && !serving { - node.recharging = true - sumWeight += node.rcWeight - } - node.serving = serving - if node.recharging && serving { - node.recharging = false - sumWeight -= node.rcWeight - } - - node.rcDelta = 0 - if serving { - node.rcDelta = int64(rcConst / simReqCnt) - } - if node.recharging { - node.rcDelta = -int64(node.node.cm.rcRecharge * node.rcWeight / sumWeight) - node.finishRecharge = node.lastUpdate + mclock.AbsTime(node.rcValue*rcConst/(-node.rcDelta)) - } -} - -type ClientManager struct { - lock sync.Mutex - nodes map[*cmNode]struct{} - simReqCnt, sumWeight, rcSumValue uint64 - maxSimReq, maxRcSum uint64 - rcRecharge uint64 - resumeQueue chan chan bool - time mclock.AbsTime -} - -func NewClientManager(rcTarget, maxSimReq, maxRcSum uint64) *ClientManager { - cm := &ClientManager{ - nodes: make(map[*cmNode]struct{}), - resumeQueue: make(chan chan bool), - rcRecharge: rcConst * rcConst / (100*rcConst/rcTarget - rcConst), - maxSimReq: maxSimReq, - maxRcSum: maxRcSum, - } - go cm.queueProc() - return cm -} - -func (self *ClientManager) Stop() { - self.lock.Lock() - defer self.lock.Unlock() - - // signal any waiting accept routines to return false - self.nodes = make(map[*cmNode]struct{}) - close(self.resumeQueue) -} - -func (self *ClientManager) addNode(cnode *ClientNode) *cmNode { - time := mclock.Now() - node := &cmNode{ - node: cnode, - lastUpdate: time, - finishRecharge: time, - rcWeight: 1, - } - self.lock.Lock() - defer self.lock.Unlock() - - self.nodes[node] = struct{}{} - self.update(mclock.Now()) - return node -} - -func (self *ClientManager) removeNode(node *cmNode) { - self.lock.Lock() - defer self.lock.Unlock() - - time := mclock.Now() - self.stop(node, time) - delete(self.nodes, node) - self.update(time) -} - -// recalc sumWeight -func (self *ClientManager) updateNodes(time mclock.AbsTime) (rce bool) { - var sumWeight, rcSum uint64 - for node := range self.nodes { - rc := node.recharging - node.update(time) - if rc && !node.recharging { - rce = true - } - if node.recharging { - sumWeight += node.rcWeight - } - rcSum += uint64(node.rcValue) - } - self.sumWeight = sumWeight - self.rcSumValue = rcSum - return -} - -func (self *ClientManager) update(time mclock.AbsTime) { - for { - firstTime := time - for node := range self.nodes { - if node.recharging && node.finishRecharge < firstTime { - firstTime = node.finishRecharge - } - } - if self.updateNodes(firstTime) { - for node := range self.nodes { - if node.recharging { - node.set(node.serving, self.simReqCnt, self.sumWeight) - } - } - } else { - self.time = time - return - } - } -} - -func (self *ClientManager) canStartReq() bool { - return self.simReqCnt < self.maxSimReq && self.rcSumValue < self.maxRcSum -} - -func (self *ClientManager) queueProc() { - for rc := range self.resumeQueue { - for { - time.Sleep(time.Millisecond * 10) - self.lock.Lock() - self.update(mclock.Now()) - cs := self.canStartReq() - self.lock.Unlock() - if cs { - break - } - } - close(rc) - } -} - -func (self *ClientManager) accept(node *cmNode, time mclock.AbsTime) bool { - self.lock.Lock() - defer self.lock.Unlock() - - self.update(time) - if !self.canStartReq() { - resume := make(chan bool) - self.lock.Unlock() - self.resumeQueue <- resume - <-resume - self.lock.Lock() - if _, ok := self.nodes[node]; !ok { - return false // reject if node has been removed or manager has been stopped - } - } - self.simReqCnt++ - node.set(true, self.simReqCnt, self.sumWeight) - node.startValue = node.rcValue - self.update(self.time) - return true -} - -func (self *ClientManager) stop(node *cmNode, time mclock.AbsTime) { - if node.serving { - self.update(time) - self.simReqCnt-- - node.set(false, self.simReqCnt, self.sumWeight) - self.update(time) - } -} - -func (self *ClientManager) processed(node *cmNode, time mclock.AbsTime) (rcValue, rcCost uint64) { - self.lock.Lock() - defer self.lock.Unlock() - - self.stop(node, time) - return uint64(node.rcValue), uint64(node.rcValue - node.startValue) -} diff --git a/les/handler.go b/les/handler.go deleted file mode 100644 index a1c16cb875..0000000000 --- a/les/handler.go +++ /dev/null @@ -1,1283 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package les implements the Light Ethereum Subprotocol. -package les - -import ( - "encoding/binary" - "encoding/json" - "errors" - "fmt" - "math/big" - "net" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/p2p/discv5" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie" -) - -const ( - softResponseLimit = 2 * 1024 * 1024 // Target maximum size of returned blocks, headers or node data. - estHeaderRlpSize = 500 // Approximate size of an RLP encoded block header - - ethVersion = 63 // equivalent eth version for the downloader - - MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request - MaxBodyFetch = 32 // Amount of block bodies to be fetched per retrieval request - MaxReceiptFetch = 128 // Amount of transaction receipts to allow fetching per request - MaxCodeFetch = 64 // Amount of contract codes to allow fetching per request - MaxProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request - MaxHelperTrieProofsFetch = 64 // Amount of merkle proofs to be fetched per retrieval request - MaxTxSend = 64 // Amount of transactions to be send per request - MaxTxStatus = 256 // Amount of transactions to queried per request - - disableClientRemovePeer = false -) - -// errIncompatibleConfig is returned if the requested protocols and configs are -// not compatible (low protocol version restrictions and high requirements). -var errIncompatibleConfig = errors.New("incompatible configuration") - -func errResp(code errCode, format string, v ...interface{}) error { - return fmt.Errorf("%v - %v", code, fmt.Sprintf(format, v...)) -} - -type BlockChain interface { - Config() *params.ChainConfig - HasHeader(hash common.Hash, number uint64) bool - GetHeader(hash common.Hash, number uint64) *types.Header - GetHeaderByHash(hash common.Hash) *types.Header - CurrentHeader() *types.Header - GetTd(hash common.Hash, number uint64) *big.Int - State() (*state.StateDB, error) - InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) - Rollback(chain []common.Hash) - GetHeaderByNumber(number uint64) *types.Header - GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) - Genesis() *types.Block - SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription -} - -type txPool interface { - AddRemotes(txs []*types.Transaction) []error - Status(hashes []common.Hash) []core.TxStatus -} - -type ProtocolManager struct { - lightSync bool - txpool txPool - txrelay *LesTxRelay - networkId uint64 - chainConfig *params.ChainConfig - blockchain BlockChain - chainDb ethdb.Database - odr *LesOdr - server *LesServer - serverPool *serverPool - lesTopic discv5.Topic - reqDist *requestDistributor - retriever *retrieveManager - - downloader *downloader.Downloader - fetcher *lightFetcher - peers *peerSet - maxPeers int - - SubProtocols []p2p.Protocol - - eventMux *event.TypeMux - - // channels for fetcher, syncer, txsyncLoop - newPeerCh chan *peer - quitSync chan struct{} - noMorePeers chan struct{} - - // wait group is used for graceful shutdowns during downloading - // and processing - wg *sync.WaitGroup -} - -// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable -// with the ethereum network. -func NewProtocolManager(chainConfig *params.ChainConfig, lightSync bool, protocolVersions []uint, networkId uint64, mux *event.TypeMux, engine consensus.Engine, peers *peerSet, blockchain BlockChain, txpool txPool, chainDb ethdb.Database, odr *LesOdr, txrelay *LesTxRelay, serverPool *serverPool, quitSync chan struct{}, wg *sync.WaitGroup) (*ProtocolManager, error) { - // Create the protocol manager with the base fields - manager := &ProtocolManager{ - lightSync: lightSync, - eventMux: mux, - blockchain: blockchain, - chainConfig: chainConfig, - chainDb: chainDb, - odr: odr, - networkId: networkId, - txpool: txpool, - txrelay: txrelay, - serverPool: serverPool, - peers: peers, - newPeerCh: make(chan *peer), - quitSync: quitSync, - wg: wg, - noMorePeers: make(chan struct{}), - } - if odr != nil { - manager.retriever = odr.retriever - manager.reqDist = odr.retriever.dist - } - - // Initiate a sub-protocol for every implemented version we can handle - manager.SubProtocols = make([]p2p.Protocol, 0, len(protocolVersions)) - for _, version := range protocolVersions { - // Compatible, initialize the sub-protocol - version := version // Closure for the run - manager.SubProtocols = append(manager.SubProtocols, p2p.Protocol{ - Name: "les", - Version: version, - Length: ProtocolLengths[version], - Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error { - var entry *poolEntry - peer := manager.newPeer(int(version), networkId, p, rw) - if manager.serverPool != nil { - addr := p.RemoteAddr().(*net.TCPAddr) - entry = manager.serverPool.connect(peer, addr.IP, uint16(addr.Port)) - } - peer.poolEntry = entry - select { - case manager.newPeerCh <- peer: - manager.wg.Add(1) - defer manager.wg.Done() - err := manager.handle(peer) - if entry != nil { - manager.serverPool.disconnect(entry) - } - return err - case <-manager.quitSync: - if entry != nil { - manager.serverPool.disconnect(entry) - } - return p2p.DiscQuitting - } - }, - NodeInfo: func() interface{} { - return manager.NodeInfo() - }, - PeerInfo: func(id discover.NodeID) interface{} { - if p := manager.peers.Peer(fmt.Sprintf("%x", id[:8])); p != nil { - return p.Info() - } - return nil - }, - }) - } - if len(manager.SubProtocols) == 0 { - return nil, errIncompatibleConfig - } - - removePeer := manager.removePeer - if disableClientRemovePeer { - removePeer = func(id string) {} - } - - if lightSync { - manager.downloader = downloader.New(downloader.LightSync, chainDb, manager.eventMux, nil, blockchain, removePeer) - manager.peers.notify((*downloaderPeerNotify)(manager)) - manager.fetcher = newLightFetcher(manager) - } - - return manager, nil -} - -// removePeer initiates disconnection from a peer by removing it from the peer set -func (pm *ProtocolManager) removePeer(id string) { - pm.peers.Unregister(id) -} - -func (pm *ProtocolManager) Start(maxPeers int) { - pm.maxPeers = maxPeers - - if pm.lightSync { - go pm.syncer() - } else { - go func() { - for range pm.newPeerCh { - } - }() - } -} - -func (pm *ProtocolManager) Stop() { - // Showing a log message. During download / process this could actually - // take between 5 to 10 seconds and therefor feedback is required. - log.Info("Stopping light Ethereum protocol") - - // Quit the sync loop. - // After this send has completed, no new peers will be accepted. - pm.noMorePeers <- struct{}{} - - close(pm.quitSync) // quits syncer, fetcher - - // Disconnect existing sessions. - // This also closes the gate for any new registrations on the peer set. - // sessions which are already established but not added to pm.peers yet - // will exit when they try to register. - pm.peers.Close() - - // Wait for any process action - pm.wg.Wait() - - log.Info("Light Ethereum protocol stopped") -} - -func (pm *ProtocolManager) newPeer(pv int, nv uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { - return newPeer(pv, nv, p, newMeteredMsgWriter(rw)) -} - -// handle is the callback invoked to manage the life cycle of a les peer. When -// this function terminates, the peer is disconnected. -func (pm *ProtocolManager) handle(p *peer) error { - // Ignore maxPeers if this is a trusted peer - if pm.peers.Len() >= pm.maxPeers && !p.Peer.Info().Network.Trusted { - return p2p.DiscTooManyPeers - } - - p.Log().Debug("Light Ethereum peer connected", "name", p.Name()) - - // Execute the LES handshake - var ( - genesis = pm.blockchain.Genesis() - head = pm.blockchain.CurrentHeader() - hash = head.Hash() - number = head.Number.Uint64() - td = pm.blockchain.GetTd(hash, number) - ) - if err := p.Handshake(td, hash, number, genesis.Hash(), pm.server); err != nil { - p.Log().Debug("Light Ethereum handshake failed", "err", err) - return err - } - if rw, ok := p.rw.(*meteredMsgReadWriter); ok { - rw.Init(p.version) - } - // Register the peer locally - if err := pm.peers.Register(p); err != nil { - p.Log().Error("Light Ethereum peer registration failed", "err", err) - return err - } - defer func() { - if pm.server != nil && pm.server.fcManager != nil && p.fcClient != nil { - p.fcClient.Remove(pm.server.fcManager) - } - pm.removePeer(p.id) - }() - // Register the peer in the downloader. If the downloader considers it banned, we disconnect - if pm.lightSync { - p.lock.Lock() - head := p.headInfo - p.lock.Unlock() - if pm.fetcher != nil { - pm.fetcher.announce(p, head) - } - - if p.poolEntry != nil { - pm.serverPool.registered(p.poolEntry) - } - } - - stop := make(chan struct{}) - defer close(stop) - go func() { - // new block announce loop - for { - select { - case announce := <-p.announceChn: - p.SendAnnounce(announce) - case <-stop: - return - } - } - }() - - // main loop. handle incoming messages. - for { - if err := pm.handleMsg(p); err != nil { - p.Log().Debug("Light Ethereum message handling failed", "err", err) - return err - } - } -} - -var reqList = []uint64{GetBlockHeadersMsg, GetBlockBodiesMsg, GetCodeMsg, GetReceiptsMsg, GetProofsV1Msg, SendTxMsg, SendTxV2Msg, GetTxStatusMsg, GetHeaderProofsMsg, GetProofsV2Msg, GetHelperTrieProofsMsg} - -// handleMsg is invoked whenever an inbound message is received from a remote -// peer. The remote connection is torn down upon returning any error. -func (pm *ProtocolManager) handleMsg(p *peer) error { - // Read the next message from the remote peer, and ensure it's fully consumed - msg, err := p.rw.ReadMsg() - if err != nil { - return err - } - p.Log().Trace("Light Ethereum message arrived", "code", msg.Code, "bytes", msg.Size) - - costs := p.fcCosts[msg.Code] - reject := func(reqCnt, maxCnt uint64) bool { - if p.fcClient == nil || reqCnt > maxCnt { - return true - } - bufValue, _ := p.fcClient.AcceptRequest() - cost := costs.baseCost + reqCnt*costs.reqCost - if cost > pm.server.defParams.BufLimit { - cost = pm.server.defParams.BufLimit - } - if cost > bufValue { - recharge := time.Duration((cost - bufValue) * 1000000 / pm.server.defParams.MinRecharge) - p.Log().Error("Request came too early", "recharge", common.PrettyDuration(recharge)) - return true - } - return false - } - - if msg.Size > ProtocolMaxMsgSize { - return errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) - } - defer msg.Discard() - - var deliverMsg *Msg - - // Handle the message depending on its contents - switch msg.Code { - case StatusMsg: - p.Log().Trace("Received status message") - // Status messages should never arrive after the handshake - return errResp(ErrExtraStatusMsg, "uncontrolled status message") - - // Block header query, collect the requested headers and reply - case AnnounceMsg: - p.Log().Trace("Received announce message") - if p.requestAnnounceType == announceTypeNone { - return errResp(ErrUnexpectedResponse, "") - } - - var req announceData - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "%v: %v", msg, err) - } - - if p.requestAnnounceType == announceTypeSigned { - if err := req.checkSignature(p.pubKey); err != nil { - p.Log().Trace("Invalid announcement signature", "err", err) - return err - } - p.Log().Trace("Valid announcement signature") - } - - p.Log().Trace("Announce message content", "number", req.Number, "hash", req.Hash, "td", req.Td, "reorg", req.ReorgDepth) - if pm.fetcher != nil { - pm.fetcher.announce(p, &req) - } - - case GetBlockHeadersMsg: - p.Log().Trace("Received block header request") - // Decode the complex header query - var req struct { - ReqID uint64 - Query getBlockHeadersData - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "%v: %v", msg, err) - } - - query := req.Query - if reject(query.Amount, MaxHeaderFetch) { - return errResp(ErrRequestRejected, "") - } - - hashMode := query.Origin.Hash != (common.Hash{}) - first := true - maxNonCanonical := uint64(100) - - // Gather headers until the fetch or network limits is reached - var ( - bytes common.StorageSize - headers []*types.Header - unknown bool - ) - for !unknown && len(headers) < int(query.Amount) && bytes < softResponseLimit { - // Retrieve the next header satisfying the query - var origin *types.Header - if hashMode { - if first { - first = false - origin = pm.blockchain.GetHeaderByHash(query.Origin.Hash) - if origin != nil { - query.Origin.Number = origin.Number.Uint64() - } - } else { - origin = pm.blockchain.GetHeader(query.Origin.Hash, query.Origin.Number) - } - } else { - origin = pm.blockchain.GetHeaderByNumber(query.Origin.Number) - } - if origin == nil { - break - } - headers = append(headers, origin) - bytes += estHeaderRlpSize - - // Advance to the next header of the query - switch { - case hashMode && query.Reverse: - // Hash based traversal towards the genesis block - ancestor := query.Skip + 1 - if ancestor == 0 { - unknown = true - } else { - query.Origin.Hash, query.Origin.Number = pm.blockchain.GetAncestor(query.Origin.Hash, query.Origin.Number, ancestor, &maxNonCanonical) - unknown = (query.Origin.Hash == common.Hash{}) - } - case hashMode && !query.Reverse: - // Hash based traversal towards the leaf block - var ( - current = origin.Number.Uint64() - next = current + query.Skip + 1 - ) - if next <= current { - infos, _ := json.MarshalIndent(p.Peer.Info(), "", " ") - p.Log().Warn("GetBlockHeaders skip overflow attack", "current", current, "skip", query.Skip, "next", next, "attacker", infos) - unknown = true - } else { - if header := pm.blockchain.GetHeaderByNumber(next); header != nil { - nextHash := header.Hash() - expOldHash, _ := pm.blockchain.GetAncestor(nextHash, next, query.Skip+1, &maxNonCanonical) - if expOldHash == query.Origin.Hash { - query.Origin.Hash, query.Origin.Number = nextHash, next - } else { - unknown = true - } - } else { - unknown = true - } - } - case query.Reverse: - // Number based traversal towards the genesis block - if query.Origin.Number >= query.Skip+1 { - query.Origin.Number -= query.Skip + 1 - } else { - unknown = true - } - - case !query.Reverse: - // Number based traversal towards the leaf block - query.Origin.Number += query.Skip + 1 - } - } - - bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + query.Amount*costs.reqCost) - pm.server.fcCostStats.update(msg.Code, query.Amount, rcost) - return p.SendBlockHeaders(req.ReqID, bv, headers) - - case BlockHeadersMsg: - if pm.downloader == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received block header response message") - // A batch of headers arrived to one of our previous requests - var resp struct { - ReqID, BV uint64 - Headers []*types.Header - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.GotReply(resp.ReqID, resp.BV) - if pm.fetcher != nil && pm.fetcher.requestedID(resp.ReqID) { - pm.fetcher.deliverHeaders(p, resp.ReqID, resp.Headers) - } else { - err := pm.downloader.DeliverHeaders(p.id, resp.Headers) - if err != nil { - log.Debug(fmt.Sprint(err)) - } - } - - case GetBlockBodiesMsg: - p.Log().Trace("Received block bodies request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Hashes []common.Hash - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather blocks until the fetch or network limits is reached - var ( - bytes int - bodies []rlp.RawValue - ) - reqCnt := len(req.Hashes) - if reject(uint64(reqCnt), MaxBodyFetch) { - return errResp(ErrRequestRejected, "") - } - for _, hash := range req.Hashes { - if bytes >= softResponseLimit { - break - } - // Retrieve the requested block body, stopping if enough was found - if number := rawdb.ReadHeaderNumber(pm.chainDb, hash); number != nil { - if data := rawdb.ReadBodyRLP(pm.chainDb, hash, *number); len(data) != 0 { - bodies = append(bodies, data) - bytes += len(data) - } - } - } - bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) - pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) - return p.SendBlockBodiesRLP(req.ReqID, bv, bodies) - - case BlockBodiesMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received block bodies response") - // A batch of block bodies arrived to one of our previous requests - var resp struct { - ReqID, BV uint64 - Data []*types.Body - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.GotReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgBlockBodies, - ReqID: resp.ReqID, - Obj: resp.Data, - } - - case GetCodeMsg: - p.Log().Trace("Received code request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Reqs []CodeReq - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - bytes int - data [][]byte - ) - reqCnt := len(req.Reqs) - if reject(uint64(reqCnt), MaxCodeFetch) { - return errResp(ErrRequestRejected, "") - } - for _, req := range req.Reqs { - // Retrieve the requested state entry, stopping if enough was found - if number := rawdb.ReadHeaderNumber(pm.chainDb, req.BHash); number != nil { - if header := rawdb.ReadHeader(pm.chainDb, req.BHash, *number); header != nil { - statedb, err := pm.blockchain.State() - if err != nil { - continue - } - account, err := pm.getAccount(statedb, header.Root, common.BytesToHash(req.AccKey)) - if err != nil { - continue - } - code, _ := statedb.Database().TrieDB().Node(common.BytesToHash(account.CodeHash)) - - data = append(data, code) - if bytes += len(code); bytes >= softResponseLimit { - break - } - } - } - } - bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) - pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) - return p.SendCode(req.ReqID, bv, data) - - case CodeMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received code response") - // A batch of node state data arrived to one of our previous requests - var resp struct { - ReqID, BV uint64 - Data [][]byte - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.GotReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgCode, - ReqID: resp.ReqID, - Obj: resp.Data, - } - - case GetReceiptsMsg: - p.Log().Trace("Received receipts request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Hashes []common.Hash - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - bytes int - receipts []rlp.RawValue - ) - reqCnt := len(req.Hashes) - if reject(uint64(reqCnt), MaxReceiptFetch) { - return errResp(ErrRequestRejected, "") - } - for _, hash := range req.Hashes { - if bytes >= softResponseLimit { - break - } - // Retrieve the requested block's receipts, skipping if unknown to us - var results types.Receipts - if number := rawdb.ReadHeaderNumber(pm.chainDb, hash); number != nil { - results = rawdb.ReadReceipts(pm.chainDb, hash, *number) - } - if results == nil { - if header := pm.blockchain.GetHeaderByHash(hash); header == nil || header.ReceiptHash != types.EmptyRootHash { - continue - } - } - // If known, encode and queue for response packet - if encoded, err := rlp.EncodeToBytes(results); err != nil { - log.Error("Failed to encode receipt", "err", err) - } else { - receipts = append(receipts, encoded) - bytes += len(encoded) - } - } - bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) - pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) - return p.SendReceiptsRLP(req.ReqID, bv, receipts) - - case ReceiptsMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received receipts response") - // A batch of receipts arrived to one of our previous requests - var resp struct { - ReqID, BV uint64 - Receipts []types.Receipts - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.GotReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgReceipts, - ReqID: resp.ReqID, - Obj: resp.Receipts, - } - - case GetProofsV1Msg: - p.Log().Trace("Received proofs request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Reqs []ProofReq - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - bytes int - proofs proofsData - ) - reqCnt := len(req.Reqs) - if reject(uint64(reqCnt), MaxProofsFetch) { - return errResp(ErrRequestRejected, "") - } - for _, req := range req.Reqs { - // Retrieve the requested state entry, stopping if enough was found - if number := rawdb.ReadHeaderNumber(pm.chainDb, req.BHash); number != nil { - if header := rawdb.ReadHeader(pm.chainDb, req.BHash, *number); header != nil { - statedb, err := pm.blockchain.State() - if err != nil { - continue - } - var trie state.Trie - if len(req.AccKey) > 0 { - account, err := pm.getAccount(statedb, header.Root, common.BytesToHash(req.AccKey)) - if err != nil { - continue - } - trie, _ = statedb.Database().OpenStorageTrie(common.BytesToHash(req.AccKey), account.Root) - } else { - trie, _ = statedb.Database().OpenTrie(header.Root) - } - if trie != nil { - var proof light.NodeList - trie.Prove(req.Key, 0, &proof) - - proofs = append(proofs, proof) - if bytes += proof.DataSize(); bytes >= softResponseLimit { - break - } - } - } - } - } - bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) - pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) - return p.SendProofs(req.ReqID, bv, proofs) - - case GetProofsV2Msg: - p.Log().Trace("Received les/2 proofs request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Reqs []ProofReq - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - lastBHash common.Hash - statedb *state.StateDB - root common.Hash - ) - reqCnt := len(req.Reqs) - if reject(uint64(reqCnt), MaxProofsFetch) { - return errResp(ErrRequestRejected, "") - } - - nodes := light.NewNodeSet() - - for _, req := range req.Reqs { - // Look up the state belonging to the request - if statedb == nil || req.BHash != lastBHash { - statedb, root, lastBHash = nil, common.Hash{}, req.BHash - - if number := rawdb.ReadHeaderNumber(pm.chainDb, req.BHash); number != nil { - if header := rawdb.ReadHeader(pm.chainDb, req.BHash, *number); header != nil { - statedb, _ = pm.blockchain.State() - root = header.Root - } - } - } - if statedb == nil { - continue - } - // Pull the account or storage trie of the request - var trie state.Trie - if len(req.AccKey) > 0 { - account, err := pm.getAccount(statedb, root, common.BytesToHash(req.AccKey)) - if err != nil { - continue - } - trie, _ = statedb.Database().OpenStorageTrie(common.BytesToHash(req.AccKey), account.Root) - } else { - trie, _ = statedb.Database().OpenTrie(root) - } - if trie == nil { - continue - } - // Prove the user's request from the account or stroage trie - trie.Prove(req.Key, req.FromLevel, nodes) - if nodes.DataSize() >= softResponseLimit { - break - } - } - bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) - pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) - return p.SendProofsV2(req.ReqID, bv, nodes.NodeList()) - - case ProofsV1Msg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received proofs response") - // A batch of merkle proofs arrived to one of our previous requests - var resp struct { - ReqID, BV uint64 - Data []light.NodeList - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.GotReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgProofsV1, - ReqID: resp.ReqID, - Obj: resp.Data, - } - - case ProofsV2Msg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received les/2 proofs response") - // A batch of merkle proofs arrived to one of our previous requests - var resp struct { - ReqID, BV uint64 - Data light.NodeList - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.GotReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgProofsV2, - ReqID: resp.ReqID, - Obj: resp.Data, - } - - case GetHeaderProofsMsg: - p.Log().Trace("Received headers proof request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Reqs []ChtReq - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - bytes int - proofs []ChtResp - ) - reqCnt := len(req.Reqs) - if reject(uint64(reqCnt), MaxHelperTrieProofsFetch) { - return errResp(ErrRequestRejected, "") - } - trieDb := trie.NewDatabase(ethdb.NewTable(pm.chainDb, light.ChtTablePrefix)) - for _, req := range req.Reqs { - if header := pm.blockchain.GetHeaderByNumber(req.BlockNum); header != nil { - sectionHead := rawdb.ReadCanonicalHash(pm.chainDb, req.ChtNum*light.CHTFrequencyServer-1) - if root := light.GetChtRoot(pm.chainDb, req.ChtNum-1, sectionHead); root != (common.Hash{}) { - trie, err := trie.New(root, trieDb) - if err != nil { - continue - } - var encNumber [8]byte - binary.BigEndian.PutUint64(encNumber[:], req.BlockNum) - - var proof light.NodeList - trie.Prove(encNumber[:], 0, &proof) - - proofs = append(proofs, ChtResp{Header: header, Proof: proof}) - if bytes += proof.DataSize() + estHeaderRlpSize; bytes >= softResponseLimit { - break - } - } - } - } - bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) - pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) - return p.SendHeaderProofs(req.ReqID, bv, proofs) - - case GetHelperTrieProofsMsg: - p.Log().Trace("Received helper trie proof request") - // Decode the retrieval message - var req struct { - ReqID uint64 - Reqs []HelperTrieReq - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - // Gather state data until the fetch or network limits is reached - var ( - auxBytes int - auxData [][]byte - ) - reqCnt := len(req.Reqs) - if reject(uint64(reqCnt), MaxHelperTrieProofsFetch) { - return errResp(ErrRequestRejected, "") - } - - var ( - lastIdx uint64 - lastType uint - root common.Hash - auxTrie *trie.Trie - ) - nodes := light.NewNodeSet() - for _, req := range req.Reqs { - if auxTrie == nil || req.Type != lastType || req.TrieIdx != lastIdx { - auxTrie, lastType, lastIdx = nil, req.Type, req.TrieIdx - - var prefix string - if root, prefix = pm.getHelperTrie(req.Type, req.TrieIdx); root != (common.Hash{}) { - auxTrie, _ = trie.New(root, trie.NewDatabase(ethdb.NewTable(pm.chainDb, prefix))) - } - } - if req.AuxReq == auxRoot { - var data []byte - if root != (common.Hash{}) { - data = root[:] - } - auxData = append(auxData, data) - auxBytes += len(data) - } else { - if auxTrie != nil { - auxTrie.Prove(req.Key, req.FromLevel, nodes) - } - if req.AuxReq != 0 { - data := pm.getHelperTrieAuxData(req) - auxData = append(auxData, data) - auxBytes += len(data) - } - } - if nodes.DataSize()+auxBytes >= softResponseLimit { - break - } - } - bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) - pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) - return p.SendHelperTrieProofs(req.ReqID, bv, HelperTrieResps{Proofs: nodes.NodeList(), AuxData: auxData}) - - case HeaderProofsMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received headers proof response") - var resp struct { - ReqID, BV uint64 - Data []ChtResp - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - p.fcServer.GotReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgHeaderProofs, - ReqID: resp.ReqID, - Obj: resp.Data, - } - - case HelperTrieProofsMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received helper trie proof response") - var resp struct { - ReqID, BV uint64 - Data HelperTrieResps - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - - p.fcServer.GotReply(resp.ReqID, resp.BV) - deliverMsg = &Msg{ - MsgType: MsgHelperTrieProofs, - ReqID: resp.ReqID, - Obj: resp.Data, - } - - case SendTxMsg: - if pm.txpool == nil { - return errResp(ErrRequestRejected, "") - } - // Transactions arrived, parse all of them and deliver to the pool - var txs []*types.Transaction - if err := msg.Decode(&txs); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - reqCnt := len(txs) - if reject(uint64(reqCnt), MaxTxSend) { - return errResp(ErrRequestRejected, "") - } - pm.txpool.AddRemotes(txs) - - _, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) - pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) - - case SendTxV2Msg: - if pm.txpool == nil { - return errResp(ErrRequestRejected, "") - } - // Transactions arrived, parse all of them and deliver to the pool - var req struct { - ReqID uint64 - Txs []*types.Transaction - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - reqCnt := len(req.Txs) - if reject(uint64(reqCnt), MaxTxSend) { - return errResp(ErrRequestRejected, "") - } - - hashes := make([]common.Hash, len(req.Txs)) - for i, tx := range req.Txs { - hashes[i] = tx.Hash() - } - stats := pm.txStatus(hashes) - for i, stat := range stats { - if stat.Status == core.TxStatusUnknown { - if errs := pm.txpool.AddRemotes([]*types.Transaction{req.Txs[i]}); errs[0] != nil { - stats[i].Error = errs[0].Error() - continue - } - stats[i] = pm.txStatus([]common.Hash{hashes[i]})[0] - } - } - - bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) - pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) - - return p.SendTxStatus(req.ReqID, bv, stats) - - case GetTxStatusMsg: - if pm.txpool == nil { - return errResp(ErrUnexpectedResponse, "") - } - // Transactions arrived, parse all of them and deliver to the pool - var req struct { - ReqID uint64 - Hashes []common.Hash - } - if err := msg.Decode(&req); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - reqCnt := len(req.Hashes) - if reject(uint64(reqCnt), MaxTxStatus) { - return errResp(ErrRequestRejected, "") - } - bv, rcost := p.fcClient.RequestProcessed(costs.baseCost + uint64(reqCnt)*costs.reqCost) - pm.server.fcCostStats.update(msg.Code, uint64(reqCnt), rcost) - - return p.SendTxStatus(req.ReqID, bv, pm.txStatus(req.Hashes)) - - case TxStatusMsg: - if pm.odr == nil { - return errResp(ErrUnexpectedResponse, "") - } - - p.Log().Trace("Received tx status response") - var resp struct { - ReqID, BV uint64 - Status []txStatus - } - if err := msg.Decode(&resp); err != nil { - return errResp(ErrDecode, "msg %v: %v", msg, err) - } - - p.fcServer.GotReply(resp.ReqID, resp.BV) - - default: - p.Log().Trace("Received unknown message", "code", msg.Code) - return errResp(ErrInvalidMsgCode, "%v", msg.Code) - } - - if deliverMsg != nil { - err := pm.retriever.deliver(p, deliverMsg) - if err != nil { - p.responseErrors++ - if p.responseErrors > maxResponseErrors { - return err - } - } - } - return nil -} - -// getAccount retrieves an account from the state based at root. -func (pm *ProtocolManager) getAccount(statedb *state.StateDB, root, hash common.Hash) (state.Account, error) { - trie, err := trie.New(root, statedb.Database().TrieDB()) - if err != nil { - return state.Account{}, err - } - blob, err := trie.TryGet(hash[:]) - if err != nil { - return state.Account{}, err - } - var account state.Account - if err = rlp.DecodeBytes(blob, &account); err != nil { - return state.Account{}, err - } - return account, nil -} - -// getHelperTrie returns the post-processed trie root for the given trie ID and section index -func (pm *ProtocolManager) getHelperTrie(id uint, idx uint64) (common.Hash, string) { - switch id { - case htCanonical: - sectionHead := rawdb.ReadCanonicalHash(pm.chainDb, (idx+1)*light.CHTFrequencyClient-1) - return light.GetChtV2Root(pm.chainDb, idx, sectionHead), light.ChtTablePrefix - case htBloomBits: - sectionHead := rawdb.ReadCanonicalHash(pm.chainDb, (idx+1)*light.BloomTrieFrequency-1) - return light.GetBloomTrieRoot(pm.chainDb, idx, sectionHead), light.BloomTrieTablePrefix - } - return common.Hash{}, "" -} - -// getHelperTrieAuxData returns requested auxiliary data for the given HelperTrie request -func (pm *ProtocolManager) getHelperTrieAuxData(req HelperTrieReq) []byte { - switch { - case req.Type == htCanonical && req.AuxReq == auxHeader && len(req.Key) == 8: - blockNum := binary.BigEndian.Uint64(req.Key) - hash := rawdb.ReadCanonicalHash(pm.chainDb, blockNum) - return rawdb.ReadHeaderRLP(pm.chainDb, hash, blockNum) - } - return nil -} - -func (pm *ProtocolManager) txStatus(hashes []common.Hash) []txStatus { - stats := make([]txStatus, len(hashes)) - for i, stat := range pm.txpool.Status(hashes) { - // Save the status we've got from the transaction pool - stats[i].Status = stat - - // If the transaction is unknown to the pool, try looking it up locally - if stat == core.TxStatusUnknown { - if block, number, index := rawdb.ReadTxLookupEntry(pm.chainDb, hashes[i]); block != (common.Hash{}) { - stats[i].Status = core.TxStatusIncluded - stats[i].Lookup = &rawdb.TxLookupEntry{BlockHash: block, BlockIndex: number, Index: index} - } - } - } - return stats -} - -// NodeInfo represents a short summary of the Ethereum sub-protocol metadata -// known about the host peer. -type NodeInfo struct { - Network uint64 `json:"network"` // Ethereum network ID (1=Frontier, 2=Morden, Ropsten=3, Rinkeby=4) - Difficulty *big.Int `json:"difficulty"` // Total difficulty of the host's blockchain - Genesis common.Hash `json:"genesis"` // SHA3 hash of the host's genesis block - Config *params.ChainConfig `json:"config"` // Chain configuration for the fork rules - Head common.Hash `json:"head"` // SHA3 hash of the host's best owned block -} - -// NodeInfo retrieves some protocol metadata about the running host node. -func (self *ProtocolManager) NodeInfo() *NodeInfo { - head := self.blockchain.CurrentHeader() - hash := head.Hash() - - return &NodeInfo{ - Network: self.networkId, - Difficulty: self.blockchain.GetTd(hash, head.Number.Uint64()), - Genesis: self.blockchain.Genesis().Hash(), - Config: self.blockchain.Config(), - Head: hash, - } -} - -// downloaderPeerNotify implements peerSetNotify -type downloaderPeerNotify ProtocolManager - -type peerConnection struct { - manager *ProtocolManager - peer *peer -} - -func (pc *peerConnection) Head() (common.Hash, *big.Int) { - return pc.peer.HeadAndTd() -} - -func (pc *peerConnection) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error { - reqID := genReqID() - rq := &distReq{ - getCost: func(dp distPeer) uint64 { - peer := dp.(*peer) - return peer.GetRequestCost(GetBlockHeadersMsg, amount) - }, - canSend: func(dp distPeer) bool { - return dp.(*peer) == pc.peer - }, - request: func(dp distPeer) func() { - peer := dp.(*peer) - cost := peer.GetRequestCost(GetBlockHeadersMsg, amount) - peer.fcServer.QueueRequest(reqID, cost) - return func() { peer.RequestHeadersByHash(reqID, cost, origin, amount, skip, reverse) } - }, - } - _, ok := <-pc.manager.reqDist.queue(rq) - if !ok { - return ErrNoPeers - } - return nil -} - -func (pc *peerConnection) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error { - reqID := genReqID() - rq := &distReq{ - getCost: func(dp distPeer) uint64 { - peer := dp.(*peer) - return peer.GetRequestCost(GetBlockHeadersMsg, amount) - }, - canSend: func(dp distPeer) bool { - return dp.(*peer) == pc.peer - }, - request: func(dp distPeer) func() { - peer := dp.(*peer) - cost := peer.GetRequestCost(GetBlockHeadersMsg, amount) - peer.fcServer.QueueRequest(reqID, cost) - return func() { peer.RequestHeadersByNumber(reqID, cost, origin, amount, skip, reverse) } - }, - } - _, ok := <-pc.manager.reqDist.queue(rq) - if !ok { - return ErrNoPeers - } - return nil -} - -func (d *downloaderPeerNotify) registerPeer(p *peer) { - pm := (*ProtocolManager)(d) - pc := &peerConnection{ - manager: pm, - peer: p, - } - pm.downloader.RegisterLightPeer(p.id, ethVersion, pc) -} - -func (d *downloaderPeerNotify) unregisterPeer(p *peer) { - pm := (*ProtocolManager)(d) - pm.downloader.UnregisterPeer(p.id) -} diff --git a/les/handler_test.go b/les/handler_test.go deleted file mode 100644 index 31aad3ed45..0000000000 --- a/les/handler_test.go +++ /dev/null @@ -1,578 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package les - -import ( - "encoding/binary" - "math/big" - "math/rand" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie" -) - -func expectResponse(r p2p.MsgReader, msgcode, reqID, bv uint64, data interface{}) error { - type resp struct { - ReqID, BV uint64 - Data interface{} - } - return p2p.ExpectMsg(r, msgcode, resp{reqID, bv, data}) -} - -// Tests that block headers can be retrieved from a remote chain based on user queries. -func TestGetBlockHeadersLes1(t *testing.T) { testGetBlockHeaders(t, 1) } -func TestGetBlockHeadersLes2(t *testing.T) { testGetBlockHeaders(t, 2) } - -func testGetBlockHeaders(t *testing.T, protocol int) { - pm := newTestProtocolManagerMust(t, false, downloader.MaxHashFetch+15, nil, nil, nil, ethdb.NewMemDatabase()) - bc := pm.blockchain.(*core.BlockChain) - peer, _ := newTestPeer(t, "peer", protocol, pm, true) - defer peer.close() - - // Create a "random" unknown hash for testing - var unknown common.Hash - for i := range unknown { - unknown[i] = byte(i) - } - // Create a batch of tests for various scenarios - limit := uint64(MaxHeaderFetch) - tests := []struct { - query *getBlockHeadersData // The query to execute for header retrieval - expect []common.Hash // The hashes of the block whose headers are expected - }{ - // A single random block should be retrievable by hash and number too - { - &getBlockHeadersData{Origin: hashOrNumber{Hash: bc.GetBlockByNumber(limit / 2).Hash()}, Amount: 1}, - []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()}, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 1}, - []common.Hash{bc.GetBlockByNumber(limit / 2).Hash()}, - }, - // Multiple headers should be retrievable in both directions - { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3}, - []common.Hash{ - bc.GetBlockByNumber(limit / 2).Hash(), - bc.GetBlockByNumber(limit/2 + 1).Hash(), - bc.GetBlockByNumber(limit/2 + 2).Hash(), - }, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Amount: 3, Reverse: true}, - []common.Hash{ - bc.GetBlockByNumber(limit / 2).Hash(), - bc.GetBlockByNumber(limit/2 - 1).Hash(), - bc.GetBlockByNumber(limit/2 - 2).Hash(), - }, - }, - // Multiple headers with skip lists should be retrievable - { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3}, - []common.Hash{ - bc.GetBlockByNumber(limit / 2).Hash(), - bc.GetBlockByNumber(limit/2 + 4).Hash(), - bc.GetBlockByNumber(limit/2 + 8).Hash(), - }, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: limit / 2}, Skip: 3, Amount: 3, Reverse: true}, - []common.Hash{ - bc.GetBlockByNumber(limit / 2).Hash(), - bc.GetBlockByNumber(limit/2 - 4).Hash(), - bc.GetBlockByNumber(limit/2 - 8).Hash(), - }, - }, - // The chain endpoints should be retrievable - { - &getBlockHeadersData{Origin: hashOrNumber{Number: 0}, Amount: 1}, - []common.Hash{bc.GetBlockByNumber(0).Hash()}, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64()}, Amount: 1}, - []common.Hash{bc.CurrentBlock().Hash()}, - }, - // Ensure protocol limits are honored - /*{ - &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true}, - bc.GetBlockHashesFromHash(bc.CurrentBlock().Hash(), limit), - },*/ - // Check that requesting more than available is handled gracefully - { - &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 3, Amount: 3}, - []common.Hash{ - bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(), - bc.GetBlockByNumber(bc.CurrentBlock().NumberU64()).Hash(), - }, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 3, Amount: 3, Reverse: true}, - []common.Hash{ - bc.GetBlockByNumber(4).Hash(), - bc.GetBlockByNumber(0).Hash(), - }, - }, - // Check that requesting more than available is handled gracefully, even if mid skip - { - &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() - 4}, Skip: 2, Amount: 3}, - []common.Hash{ - bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 4).Hash(), - bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - 1).Hash(), - }, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: 4}, Skip: 2, Amount: 3, Reverse: true}, - []common.Hash{ - bc.GetBlockByNumber(4).Hash(), - bc.GetBlockByNumber(1).Hash(), - }, - }, - // Check that non existing headers aren't returned - { - &getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1}, - []common.Hash{}, - }, { - &getBlockHeadersData{Origin: hashOrNumber{Number: bc.CurrentBlock().NumberU64() + 1}, Amount: 1}, - []common.Hash{}, - }, - } - // Run each of the tests and verify the results against the chain - var reqID uint64 - for i, tt := range tests { - // Collect the headers to expect in the response - headers := []*types.Header{} - for _, hash := range tt.expect { - headers = append(headers, bc.GetHeaderByHash(hash)) - } - // Send the hash request and verify the response - reqID++ - cost := peer.GetRequestCost(GetBlockHeadersMsg, int(tt.query.Amount)) - sendRequest(peer.app, GetBlockHeadersMsg, reqID, cost, tt.query) - if err := expectResponse(peer.app, BlockHeadersMsg, reqID, testBufLimit, headers); err != nil { - t.Errorf("test %d: headers mismatch: %v", i, err) - } - } -} - -// Tests that block contents can be retrieved from a remote chain based on their hashes. -func TestGetBlockBodiesLes1(t *testing.T) { testGetBlockBodies(t, 1) } -func TestGetBlockBodiesLes2(t *testing.T) { testGetBlockBodies(t, 2) } - -func testGetBlockBodies(t *testing.T, protocol int) { - pm := newTestProtocolManagerMust(t, false, downloader.MaxBlockFetch+15, nil, nil, nil, ethdb.NewMemDatabase()) - bc := pm.blockchain.(*core.BlockChain) - peer, _ := newTestPeer(t, "peer", protocol, pm, true) - defer peer.close() - - // Create a batch of tests for various scenarios - limit := MaxBodyFetch - tests := []struct { - random int // Number of blocks to fetch randomly from the chain - explicit []common.Hash // Explicitly requested blocks - available []bool // Availability of explicitly requested blocks - expected int // Total number of existing blocks to expect - }{ - {1, nil, nil, 1}, // A single random block should be retrievable - {10, nil, nil, 10}, // Multiple random blocks should be retrievable - {limit, nil, nil, limit}, // The maximum possible blocks should be retrievable - //{limit + 1, nil, nil, limit}, // No more than the possible block count should be returned - {0, []common.Hash{bc.Genesis().Hash()}, []bool{true}, 1}, // The genesis block should be retrievable - {0, []common.Hash{bc.CurrentBlock().Hash()}, []bool{true}, 1}, // The chains head block should be retrievable - {0, []common.Hash{{}}, []bool{false}, 0}, // A non existent block should not be returned - - // Existing and non-existing blocks interleaved should not cause problems - {0, []common.Hash{ - {}, - bc.GetBlockByNumber(1).Hash(), - {}, - bc.GetBlockByNumber(10).Hash(), - {}, - bc.GetBlockByNumber(100).Hash(), - {}, - }, []bool{false, true, false, true, false, true, false}, 3}, - } - // Run each of the tests and verify the results against the chain - var reqID uint64 - for i, tt := range tests { - // Collect the hashes to request, and the response to expect - hashes, seen := []common.Hash{}, make(map[int64]bool) - bodies := []*types.Body{} - - for j := 0; j < tt.random; j++ { - for { - num := rand.Int63n(int64(bc.CurrentBlock().NumberU64())) - if !seen[num] { - seen[num] = true - - block := bc.GetBlockByNumber(uint64(num)) - hashes = append(hashes, block.Hash()) - if len(bodies) < tt.expected { - bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()}) - } - break - } - } - } - for j, hash := range tt.explicit { - hashes = append(hashes, hash) - if tt.available[j] && len(bodies) < tt.expected { - block := bc.GetBlockByHash(hash) - bodies = append(bodies, &types.Body{Transactions: block.Transactions(), Uncles: block.Uncles()}) - } - } - reqID++ - // Send the hash request and verify the response - cost := peer.GetRequestCost(GetBlockBodiesMsg, len(hashes)) - sendRequest(peer.app, GetBlockBodiesMsg, reqID, cost, hashes) - if err := expectResponse(peer.app, BlockBodiesMsg, reqID, testBufLimit, bodies); err != nil { - t.Errorf("test %d: bodies mismatch: %v", i, err) - } - } -} - -// Tests that the contract codes can be retrieved based on account addresses. -func TestGetCodeLes1(t *testing.T) { testGetCode(t, 1) } -func TestGetCodeLes2(t *testing.T) { testGetCode(t, 2) } - -func testGetCode(t *testing.T, protocol int) { - // Assemble the test environment - pm := newTestProtocolManagerMust(t, false, 4, testChainGen, nil, nil, ethdb.NewMemDatabase()) - bc := pm.blockchain.(*core.BlockChain) - peer, _ := newTestPeer(t, "peer", protocol, pm, true) - defer peer.close() - - var codereqs []*CodeReq - var codes [][]byte - - for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { - header := bc.GetHeaderByNumber(i) - req := &CodeReq{ - BHash: header.Hash(), - AccKey: crypto.Keccak256(testContractAddr[:]), - } - codereqs = append(codereqs, req) - if i >= testContractDeployed { - codes = append(codes, testContractCodeDeployed) - } - } - - cost := peer.GetRequestCost(GetCodeMsg, len(codereqs)) - sendRequest(peer.app, GetCodeMsg, 42, cost, codereqs) - if err := expectResponse(peer.app, CodeMsg, 42, testBufLimit, codes); err != nil { - t.Errorf("codes mismatch: %v", err) - } -} - -// Tests that the transaction receipts can be retrieved based on hashes. -func TestGetReceiptLes1(t *testing.T) { testGetReceipt(t, 1) } -func TestGetReceiptLes2(t *testing.T) { testGetReceipt(t, 2) } - -func testGetReceipt(t *testing.T, protocol int) { - // Assemble the test environment - db := ethdb.NewMemDatabase() - pm := newTestProtocolManagerMust(t, false, 4, testChainGen, nil, nil, db) - bc := pm.blockchain.(*core.BlockChain) - peer, _ := newTestPeer(t, "peer", protocol, pm, true) - defer peer.close() - - // Collect the hashes to request, and the response to expect - hashes, receipts := []common.Hash{}, []types.Receipts{} - for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { - block := bc.GetBlockByNumber(i) - - hashes = append(hashes, block.Hash()) - receipts = append(receipts, rawdb.ReadReceipts(db, block.Hash(), block.NumberU64())) - } - // Send the hash request and verify the response - cost := peer.GetRequestCost(GetReceiptsMsg, len(hashes)) - sendRequest(peer.app, GetReceiptsMsg, 42, cost, hashes) - if err := expectResponse(peer.app, ReceiptsMsg, 42, testBufLimit, receipts); err != nil { - t.Errorf("receipts mismatch: %v", err) - } -} - -// Tests that trie merkle proofs can be retrieved -func TestGetProofsLes1(t *testing.T) { testGetProofs(t, 1) } -func TestGetProofsLes2(t *testing.T) { testGetProofs(t, 2) } - -func testGetProofs(t *testing.T, protocol int) { - // Assemble the test environment - db := ethdb.NewMemDatabase() - pm := newTestProtocolManagerMust(t, false, 4, testChainGen, nil, nil, db) - bc := pm.blockchain.(*core.BlockChain) - peer, _ := newTestPeer(t, "peer", protocol, pm, true) - defer peer.close() - - var ( - proofreqs []ProofReq - proofsV1 [][]rlp.RawValue - ) - proofsV2 := light.NewNodeSet() - - accounts := []common.Address{testBankAddress, acc1Addr, acc2Addr, {}} - for i := uint64(0); i <= bc.CurrentBlock().NumberU64(); i++ { - header := bc.GetHeaderByNumber(i) - root := header.Root - trie, _ := trie.New(root, trie.NewDatabase(db)) - - for _, acc := range accounts { - req := ProofReq{ - BHash: header.Hash(), - Key: crypto.Keccak256(acc[:]), - } - proofreqs = append(proofreqs, req) - - switch protocol { - case 1: - var proof light.NodeList - trie.Prove(crypto.Keccak256(acc[:]), 0, &proof) - proofsV1 = append(proofsV1, proof) - case 2: - trie.Prove(crypto.Keccak256(acc[:]), 0, proofsV2) - } - } - } - // Send the proof request and verify the response - switch protocol { - case 1: - cost := peer.GetRequestCost(GetProofsV1Msg, len(proofreqs)) - sendRequest(peer.app, GetProofsV1Msg, 42, cost, proofreqs) - if err := expectResponse(peer.app, ProofsV1Msg, 42, testBufLimit, proofsV1); err != nil { - t.Errorf("proofs mismatch: %v", err) - } - case 2: - cost := peer.GetRequestCost(GetProofsV2Msg, len(proofreqs)) - sendRequest(peer.app, GetProofsV2Msg, 42, cost, proofreqs) - if err := expectResponse(peer.app, ProofsV2Msg, 42, testBufLimit, proofsV2.NodeList()); err != nil { - t.Errorf("proofs mismatch: %v", err) - } - } -} - -// Tests that CHT proofs can be correctly retrieved. -func TestGetCHTProofsLes1(t *testing.T) { testGetCHTProofs(t, 1) } -func TestGetCHTProofsLes2(t *testing.T) { testGetCHTProofs(t, 2) } - -func testGetCHTProofs(t *testing.T, protocol int) { - // Figure out the client's CHT frequency - frequency := uint64(light.CHTFrequencyClient) - if protocol == 1 { - frequency = uint64(light.CHTFrequencyServer) - } - // Assemble the test environment - db := ethdb.NewMemDatabase() - pm := newTestProtocolManagerMust(t, false, int(frequency)+light.HelperTrieProcessConfirmations, testChainGen, nil, nil, db) - bc := pm.blockchain.(*core.BlockChain) - peer, _ := newTestPeer(t, "peer", protocol, pm, true) - defer peer.close() - - // Wait a while for the CHT indexer to process the new headers - time.Sleep(100 * time.Millisecond * time.Duration(frequency/light.CHTFrequencyServer)) // Chain indexer throttling - time.Sleep(250 * time.Millisecond) // CI tester slack - - // Assemble the proofs from the different protocols - header := bc.GetHeaderByNumber(frequency) - rlp, _ := rlp.EncodeToBytes(header) - - key := make([]byte, 8) - binary.BigEndian.PutUint64(key, frequency) - - proofsV1 := []ChtResp{{ - Header: header, - }} - proofsV2 := HelperTrieResps{ - AuxData: [][]byte{rlp}, - } - switch protocol { - case 1: - root := light.GetChtRoot(db, 0, bc.GetHeaderByNumber(frequency-1).Hash()) - trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(db, light.ChtTablePrefix))) - - var proof light.NodeList - trie.Prove(key, 0, &proof) - proofsV1[0].Proof = proof - - case 2: - root := light.GetChtV2Root(db, 0, bc.GetHeaderByNumber(frequency-1).Hash()) - trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(db, light.ChtTablePrefix))) - trie.Prove(key, 0, &proofsV2.Proofs) - } - // Assemble the requests for the different protocols - requestsV1 := []ChtReq{{ - ChtNum: 1, - BlockNum: frequency, - }} - requestsV2 := []HelperTrieReq{{ - Type: htCanonical, - TrieIdx: 0, - Key: key, - AuxReq: auxHeader, - }} - // Send the proof request and verify the response - switch protocol { - case 1: - cost := peer.GetRequestCost(GetHeaderProofsMsg, len(requestsV1)) - sendRequest(peer.app, GetHeaderProofsMsg, 42, cost, requestsV1) - if err := expectResponse(peer.app, HeaderProofsMsg, 42, testBufLimit, proofsV1); err != nil { - t.Errorf("proofs mismatch: %v", err) - } - case 2: - cost := peer.GetRequestCost(GetHelperTrieProofsMsg, len(requestsV2)) - sendRequest(peer.app, GetHelperTrieProofsMsg, 42, cost, requestsV2) - if err := expectResponse(peer.app, HelperTrieProofsMsg, 42, testBufLimit, proofsV2); err != nil { - t.Errorf("proofs mismatch: %v", err) - } - } -} - -// Tests that bloombits proofs can be correctly retrieved. -func TestGetBloombitsProofs(t *testing.T) { - // Assemble the test environment - db := ethdb.NewMemDatabase() - pm := newTestProtocolManagerMust(t, false, light.BloomTrieFrequency+256, testChainGen, nil, nil, db) - bc := pm.blockchain.(*core.BlockChain) - peer, _ := newTestPeer(t, "peer", 2, pm, true) - defer peer.close() - - // Wait a while for the bloombits indexer to process the new headers - time.Sleep(100 * time.Millisecond * time.Duration(light.BloomTrieFrequency/4096)) // Chain indexer throttling - time.Sleep(250 * time.Millisecond) // CI tester slack - - // Request and verify each bit of the bloom bits proofs - for bit := 0; bit < 2048; bit++ { - // Assemble therequest and proofs for the bloombits - key := make([]byte, 10) - - binary.BigEndian.PutUint16(key[:2], uint16(bit)) - binary.BigEndian.PutUint64(key[2:], uint64(light.BloomTrieFrequency)) - - requests := []HelperTrieReq{{ - Type: htBloomBits, - TrieIdx: 0, - Key: key, - }} - var proofs HelperTrieResps - - root := light.GetBloomTrieRoot(db, 0, bc.GetHeaderByNumber(light.BloomTrieFrequency-1).Hash()) - trie, _ := trie.New(root, trie.NewDatabase(ethdb.NewTable(db, light.BloomTrieTablePrefix))) - trie.Prove(key, 0, &proofs.Proofs) - - // Send the proof request and verify the response - cost := peer.GetRequestCost(GetHelperTrieProofsMsg, len(requests)) - sendRequest(peer.app, GetHelperTrieProofsMsg, 42, cost, requests) - if err := expectResponse(peer.app, HelperTrieProofsMsg, 42, testBufLimit, proofs); err != nil { - t.Errorf("bit %d: proofs mismatch: %v", bit, err) - } - } -} - -func TestTransactionStatusLes2(t *testing.T) { - db := ethdb.NewMemDatabase() - pm := newTestProtocolManagerMust(t, false, 0, nil, nil, nil, db) - chain := pm.blockchain.(*core.BlockChain) - config := core.DefaultTxPoolConfig - config.Journal = "" - txpool := core.NewTxPool(config, params.TestChainConfig, chain) - pm.txpool = txpool - peer, _ := newTestPeer(t, "peer", 2, pm, true) - defer peer.close() - - var reqID uint64 - - test := func(tx *types.Transaction, send bool, expStatus txStatus) { - reqID++ - if send { - cost := peer.GetRequestCost(SendTxV2Msg, 1) - sendRequest(peer.app, SendTxV2Msg, reqID, cost, types.Transactions{tx}) - } else { - cost := peer.GetRequestCost(GetTxStatusMsg, 1) - sendRequest(peer.app, GetTxStatusMsg, reqID, cost, []common.Hash{tx.Hash()}) - } - if err := expectResponse(peer.app, TxStatusMsg, reqID, testBufLimit, []txStatus{expStatus}); err != nil { - t.Errorf("transaction status mismatch") - } - } - - signer := types.HomesteadSigner{} - - // test error status by sending an underpriced transaction - tx0, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) - test(tx0, true, txStatus{Status: core.TxStatusUnknown, Error: core.ErrUnderpriced.Error()}) - - tx1, _ := types.SignTx(types.NewTransaction(0, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey) - test(tx1, false, txStatus{Status: core.TxStatusUnknown}) // query before sending, should be unknown - test(tx1, true, txStatus{Status: core.TxStatusPending}) // send valid processable tx, should return pending - test(tx1, true, txStatus{Status: core.TxStatusPending}) // adding it again should not return an error - - tx2, _ := types.SignTx(types.NewTransaction(1, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey) - tx3, _ := types.SignTx(types.NewTransaction(2, acc1Addr, big.NewInt(10000), params.TxGas, big.NewInt(100000000000), nil), signer, testBankKey) - // send transactions in the wrong order, tx3 should be queued - test(tx3, true, txStatus{Status: core.TxStatusQueued}) - test(tx2, true, txStatus{Status: core.TxStatusPending}) - // query again, now tx3 should be pending too - test(tx3, false, txStatus{Status: core.TxStatusPending}) - - // generate and add a block with tx1 and tx2 included - gchain, _ := core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 1, func(i int, block *core.BlockGen) { - block.AddTx(tx1) - block.AddTx(tx2) - }) - if _, err := chain.InsertChain(gchain); err != nil { - panic(err) - } - // wait until TxPool processes the inserted block - for i := 0; i < 10; i++ { - if pending, _ := txpool.Stats(); pending == 1 { - break - } - time.Sleep(100 * time.Millisecond) - } - if pending, _ := txpool.Stats(); pending != 1 { - t.Fatalf("pending count mismatch: have %d, want 1", pending) - } - - // check if their status is included now - block1hash := rawdb.ReadCanonicalHash(db, 1) - test(tx1, false, txStatus{Status: core.TxStatusIncluded, Lookup: &rawdb.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 0}}) - test(tx2, false, txStatus{Status: core.TxStatusIncluded, Lookup: &rawdb.TxLookupEntry{BlockHash: block1hash, BlockIndex: 1, Index: 1}}) - - // create a reorg that rolls them back - gchain, _ = core.GenerateChain(params.TestChainConfig, chain.GetBlockByNumber(0), ethash.NewFaker(), db, 2, func(i int, block *core.BlockGen) {}) - if _, err := chain.InsertChain(gchain); err != nil { - panic(err) - } - // wait until TxPool processes the reorg - for i := 0; i < 10; i++ { - if pending, _ := txpool.Stats(); pending == 3 { - break - } - time.Sleep(100 * time.Millisecond) - } - if pending, _ := txpool.Stats(); pending != 3 { - t.Fatalf("pending count mismatch: have %d, want 3", pending) - } - // check if their status is pending again - test(tx1, false, txStatus{Status: core.TxStatusPending}) - test(tx2, false, txStatus{Status: core.TxStatusPending}) -} diff --git a/les/helper_test.go b/les/helper_test.go deleted file mode 100644 index 8fd01a39e0..0000000000 --- a/les/helper_test.go +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// This file contains some shares testing functionality, common to multiple -// different files and modules being tested. - -package les - -import ( - "crypto/rand" - "math/big" - "sync" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/les/flowcontrol" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/params" -) - -var ( - testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) - testBankFunds = big.NewInt(1000000000000000000) - - acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") - acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") - acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey) - acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey) - - testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056") - testContractAddr common.Address - testContractCodeDeployed = testContractCode[16:] - testContractDeployed = uint64(2) - - testEventEmitterCode = common.Hex2Bytes("60606040523415600e57600080fd5b7f57050ab73f6b9ebdd9f76b8d4997793f48cf956e965ee070551b9ca0bb71584e60405160405180910390a160358060476000396000f3006060604052600080fd00a165627a7a723058203f727efcad8b5811f8cb1fc2620ce5e8c63570d697aef968172de296ea3994140029") - testEventEmitterAddr common.Address - - testBufLimit = uint64(100) -) - -/* -contract test { - - uint256[100] data; - - function Put(uint256 addr, uint256 value) { - data[addr] = value; - } - - function Get(uint256 addr) constant returns (uint256 value) { - return data[addr]; - } -} -*/ - -func testChainGen(i int, block *core.BlockGen) { - signer := types.HomesteadSigner{} - - switch i { - case 0: - // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) - block.AddTx(tx) - case 1: - // In block 2, the test bank sends some more ether to account #1. - // acc1Addr passes it on to account #2. - // acc1Addr creates a test contract. - // acc1Addr creates a test event. - nonce := block.TxNonce(acc1Addr) - - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) - tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) - tx3, _ := types.SignTx(types.NewContractCreation(nonce+1, big.NewInt(0), 200000, big.NewInt(0), testContractCode), signer, acc1Key) - testContractAddr = crypto.CreateAddress(acc1Addr, nonce+1) - tx4, _ := types.SignTx(types.NewContractCreation(nonce+2, big.NewInt(0), 200000, big.NewInt(0), testEventEmitterCode), signer, acc1Key) - testEventEmitterAddr = crypto.CreateAddress(acc1Addr, nonce+2) - block.AddTx(tx1) - block.AddTx(tx2) - block.AddTx(tx3) - block.AddTx(tx4) - case 2: - // Block 3 is empty but was mined by account #2. - block.SetCoinbase(acc2Addr) - block.SetExtra([]byte("yeehaw")) - data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001") - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey) - block.AddTx(tx) - case 3: - // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). - b2 := block.PrevBlock(1).Header() - b2.Extra = []byte("foo") - block.AddUncle(b2) - b3 := block.PrevBlock(2).Header() - b3.Extra = []byte("foo") - block.AddUncle(b3) - data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002") - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey) - block.AddTx(tx) - } -} - -func testRCL() RequestCostList { - cl := make(RequestCostList, len(reqList)) - for i, code := range reqList { - cl[i].MsgCode = code - cl[i].BaseCost = 0 - cl[i].ReqCost = 0 - } - return cl -} - -// newTestProtocolManager creates a new protocol manager for testing purposes, -// with the given number of blocks already known, and potential notification -// channels for different events. -func newTestProtocolManager(lightSync bool, blocks int, generator func(int, *core.BlockGen), peers *peerSet, odr *LesOdr, db ethdb.Database) (*ProtocolManager, error) { - var ( - evmux = new(event.TypeMux) - engine = ethash.NewFaker() - gspec = core.Genesis{ - Config: params.TestChainConfig, - Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}, - } - genesis = gspec.MustCommit(db) - chain BlockChain - ) - if peers == nil { - peers = newPeerSet() - } - - if lightSync { - chain, _ = light.NewLightChain(odr, gspec.Config, engine) - } else { - blockchain, _ := core.NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}) - - chtIndexer := light.NewChtIndexer(db, false) - chtIndexer.Start(blockchain) - - bbtIndexer := light.NewBloomTrieIndexer(db, false) - - bloomIndexer := eth.NewBloomIndexer(db, params.BloomBitsBlocks) - bloomIndexer.AddChildIndexer(bbtIndexer) - bloomIndexer.Start(blockchain) - - gchain, _ := core.GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, blocks, generator) - if _, err := blockchain.InsertChain(gchain); err != nil { - panic(err) - } - chain = blockchain - } - - var protocolVersions []uint - if lightSync { - protocolVersions = ClientProtocolVersions - } else { - protocolVersions = ServerProtocolVersions - } - pm, err := NewProtocolManager(gspec.Config, lightSync, protocolVersions, NetworkId, evmux, engine, peers, chain, nil, db, odr, nil, nil, make(chan struct{}), new(sync.WaitGroup)) - if err != nil { - return nil, err - } - if !lightSync { - srv := &LesServer{protocolManager: pm} - pm.server = srv - - srv.defParams = &flowcontrol.ServerParams{ - BufLimit: testBufLimit, - MinRecharge: 1, - } - - srv.fcManager = flowcontrol.NewClientManager(50, 10, 1000000000) - srv.fcCostStats = newCostStats(nil) - } - pm.Start(1000) - return pm, nil -} - -// newTestProtocolManagerMust creates a new protocol manager for testing purposes, -// with the given number of blocks already known, and potential notification -// channels for different events. In case of an error, the constructor force- -// fails the test. -func newTestProtocolManagerMust(t *testing.T, lightSync bool, blocks int, generator func(int, *core.BlockGen), peers *peerSet, odr *LesOdr, db ethdb.Database) *ProtocolManager { - pm, err := newTestProtocolManager(lightSync, blocks, generator, peers, odr, db) - if err != nil { - t.Fatalf("Failed to create protocol manager: %v", err) - } - return pm -} - -// testPeer is a simulated peer to allow testing direct network calls. -type testPeer struct { - net p2p.MsgReadWriter // Network layer reader/writer to simulate remote messaging - app *p2p.MsgPipeRW // Application layer reader/writer to simulate the local side - *peer -} - -// newTestPeer creates a new peer registered at the given protocol manager. -func newTestPeer(t *testing.T, name string, version int, pm *ProtocolManager, shake bool) (*testPeer, <-chan error) { - // Create a message pipe to communicate through - app, net := p2p.MsgPipe() - - // Generate a random id and create the peer - var id discover.NodeID - rand.Read(id[:]) - - peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net) - - // Start the peer on a new thread - errc := make(chan error, 1) - go func() { - select { - case pm.newPeerCh <- peer: - errc <- pm.handle(peer) - case <-pm.quitSync: - errc <- p2p.DiscQuitting - } - }() - tp := &testPeer{ - app: app, - net: net, - peer: peer, - } - // Execute any implicitly requested handshakes and return - if shake { - var ( - genesis = pm.blockchain.Genesis() - head = pm.blockchain.CurrentHeader() - td = pm.blockchain.GetTd(head.Hash(), head.Number.Uint64()) - ) - tp.handshake(t, td, head.Hash(), head.Number.Uint64(), genesis.Hash()) - } - return tp, errc -} - -func newTestPeerPair(name string, version int, pm, pm2 *ProtocolManager) (*peer, <-chan error, *peer, <-chan error) { - // Create a message pipe to communicate through - app, net := p2p.MsgPipe() - - // Generate a random id and create the peer - var id discover.NodeID - rand.Read(id[:]) - - peer := pm.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), net) - peer2 := pm2.newPeer(version, NetworkId, p2p.NewPeer(id, name, nil), app) - - // Start the peer on a new thread - errc := make(chan error, 1) - errc2 := make(chan error, 1) - go func() { - select { - case pm.newPeerCh <- peer: - errc <- pm.handle(peer) - case <-pm.quitSync: - errc <- p2p.DiscQuitting - } - }() - go func() { - select { - case pm2.newPeerCh <- peer2: - errc2 <- pm2.handle(peer2) - case <-pm2.quitSync: - errc2 <- p2p.DiscQuitting - } - }() - return peer, errc, peer2, errc2 -} - -// handshake simulates a trivial handshake that expects the same state from the -// remote side as we are simulating locally. -func (p *testPeer) handshake(t *testing.T, td *big.Int, head common.Hash, headNum uint64, genesis common.Hash) { - var expList keyValueList - expList = expList.add("protocolVersion", uint64(p.version)) - expList = expList.add("networkId", uint64(NetworkId)) - expList = expList.add("headTd", td) - expList = expList.add("headHash", head) - expList = expList.add("headNum", headNum) - expList = expList.add("genesisHash", genesis) - sendList := make(keyValueList, len(expList)) - copy(sendList, expList) - expList = expList.add("serveHeaders", nil) - expList = expList.add("serveChainSince", uint64(0)) - expList = expList.add("serveStateSince", uint64(0)) - expList = expList.add("txRelay", nil) - expList = expList.add("flowControl/BL", testBufLimit) - expList = expList.add("flowControl/MRR", uint64(1)) - expList = expList.add("flowControl/MRC", testRCL()) - - if err := p2p.ExpectMsg(p.app, StatusMsg, expList); err != nil { - t.Fatalf("status recv: %v", err) - } - if err := p2p.Send(p.app, StatusMsg, sendList); err != nil { - t.Fatalf("status send: %v", err) - } - - p.fcServerParams = &flowcontrol.ServerParams{ - BufLimit: testBufLimit, - MinRecharge: 1, - } -} - -// close terminates the local side of the peer, notifying the remote protocol -// manager of termination. -func (p *testPeer) close() { - p.app.Close() -} diff --git a/les/metrics.go b/les/metrics.go deleted file mode 100644 index c282a62a1a..0000000000 --- a/les/metrics.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package les - -import ( - "github.com/ethereum/go-ethereum/metrics" - "github.com/ethereum/go-ethereum/p2p" -) - -var ( - /* propTxnInPacketsMeter = metrics.NewMeter("eth/prop/txns/in/packets") - propTxnInTrafficMeter = metrics.NewMeter("eth/prop/txns/in/traffic") - propTxnOutPacketsMeter = metrics.NewMeter("eth/prop/txns/out/packets") - propTxnOutTrafficMeter = metrics.NewMeter("eth/prop/txns/out/traffic") - propHashInPacketsMeter = metrics.NewMeter("eth/prop/hashes/in/packets") - propHashInTrafficMeter = metrics.NewMeter("eth/prop/hashes/in/traffic") - propHashOutPacketsMeter = metrics.NewMeter("eth/prop/hashes/out/packets") - propHashOutTrafficMeter = metrics.NewMeter("eth/prop/hashes/out/traffic") - propBlockInPacketsMeter = metrics.NewMeter("eth/prop/blocks/in/packets") - propBlockInTrafficMeter = metrics.NewMeter("eth/prop/blocks/in/traffic") - propBlockOutPacketsMeter = metrics.NewMeter("eth/prop/blocks/out/packets") - propBlockOutTrafficMeter = metrics.NewMeter("eth/prop/blocks/out/traffic") - reqHashInPacketsMeter = metrics.NewMeter("eth/req/hashes/in/packets") - reqHashInTrafficMeter = metrics.NewMeter("eth/req/hashes/in/traffic") - reqHashOutPacketsMeter = metrics.NewMeter("eth/req/hashes/out/packets") - reqHashOutTrafficMeter = metrics.NewMeter("eth/req/hashes/out/traffic") - reqBlockInPacketsMeter = metrics.NewMeter("eth/req/blocks/in/packets") - reqBlockInTrafficMeter = metrics.NewMeter("eth/req/blocks/in/traffic") - reqBlockOutPacketsMeter = metrics.NewMeter("eth/req/blocks/out/packets") - reqBlockOutTrafficMeter = metrics.NewMeter("eth/req/blocks/out/traffic") - reqHeaderInPacketsMeter = metrics.NewMeter("eth/req/headers/in/packets") - reqHeaderInTrafficMeter = metrics.NewMeter("eth/req/headers/in/traffic") - reqHeaderOutPacketsMeter = metrics.NewMeter("eth/req/headers/out/packets") - reqHeaderOutTrafficMeter = metrics.NewMeter("eth/req/headers/out/traffic") - reqBodyInPacketsMeter = metrics.NewMeter("eth/req/bodies/in/packets") - reqBodyInTrafficMeter = metrics.NewMeter("eth/req/bodies/in/traffic") - reqBodyOutPacketsMeter = metrics.NewMeter("eth/req/bodies/out/packets") - reqBodyOutTrafficMeter = metrics.NewMeter("eth/req/bodies/out/traffic") - reqStateInPacketsMeter = metrics.NewMeter("eth/req/states/in/packets") - reqStateInTrafficMeter = metrics.NewMeter("eth/req/states/in/traffic") - reqStateOutPacketsMeter = metrics.NewMeter("eth/req/states/out/packets") - reqStateOutTrafficMeter = metrics.NewMeter("eth/req/states/out/traffic") - reqReceiptInPacketsMeter = metrics.NewMeter("eth/req/receipts/in/packets") - reqReceiptInTrafficMeter = metrics.NewMeter("eth/req/receipts/in/traffic") - reqReceiptOutPacketsMeter = metrics.NewMeter("eth/req/receipts/out/packets") - reqReceiptOutTrafficMeter = metrics.NewMeter("eth/req/receipts/out/traffic")*/ - miscInPacketsMeter = metrics.NewRegisteredMeter("les/misc/in/packets", nil) - miscInTrafficMeter = metrics.NewRegisteredMeter("les/misc/in/traffic", nil) - miscOutPacketsMeter = metrics.NewRegisteredMeter("les/misc/out/packets", nil) - miscOutTrafficMeter = metrics.NewRegisteredMeter("les/misc/out/traffic", nil) -) - -// meteredMsgReadWriter is a wrapper around a p2p.MsgReadWriter, capable of -// accumulating the above defined metrics based on the data stream contents. -type meteredMsgReadWriter struct { - p2p.MsgReadWriter // Wrapped message stream to meter - version int // Protocol version to select correct meters -} - -// newMeteredMsgWriter wraps a p2p MsgReadWriter with metering support. If the -// metrics system is disabled, this function returns the original object. -func newMeteredMsgWriter(rw p2p.MsgReadWriter) p2p.MsgReadWriter { - if !metrics.Enabled { - return rw - } - return &meteredMsgReadWriter{MsgReadWriter: rw} -} - -// Init sets the protocol version used by the stream to know which meters to -// increment in case of overlapping message ids between protocol versions. -func (rw *meteredMsgReadWriter) Init(version int) { - rw.version = version -} - -func (rw *meteredMsgReadWriter) ReadMsg() (p2p.Msg, error) { - // Read the message and short circuit in case of an error - msg, err := rw.MsgReadWriter.ReadMsg() - if err != nil { - return msg, err - } - // Account for the data traffic - packets, traffic := miscInPacketsMeter, miscInTrafficMeter - packets.Mark(1) - traffic.Mark(int64(msg.Size)) - - return msg, err -} - -func (rw *meteredMsgReadWriter) WriteMsg(msg p2p.Msg) error { - // Account for the data traffic - packets, traffic := miscOutPacketsMeter, miscOutTrafficMeter - packets.Mark(1) - traffic.Mark(int64(msg.Size)) - - // Send the packet to the p2p layer - return rw.MsgReadWriter.WriteMsg(msg) -} diff --git a/les/odr.go b/les/odr.go deleted file mode 100644 index f8412aaad7..0000000000 --- a/les/odr.go +++ /dev/null @@ -1,118 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package les - -import ( - "context" - - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/log" -) - -// LesOdr implements light.OdrBackend -type LesOdr struct { - db ethdb.Database - chtIndexer, bloomTrieIndexer, bloomIndexer *core.ChainIndexer - retriever *retrieveManager - stop chan struct{} -} - -func NewLesOdr(db ethdb.Database, chtIndexer, bloomTrieIndexer, bloomIndexer *core.ChainIndexer, retriever *retrieveManager) *LesOdr { - return &LesOdr{ - db: db, - chtIndexer: chtIndexer, - bloomTrieIndexer: bloomTrieIndexer, - bloomIndexer: bloomIndexer, - retriever: retriever, - stop: make(chan struct{}), - } -} - -// Stop cancels all pending retrievals -func (odr *LesOdr) Stop() { - close(odr.stop) -} - -// Database returns the backing database -func (odr *LesOdr) Database() ethdb.Database { - return odr.db -} - -// ChtIndexer returns the CHT chain indexer -func (odr *LesOdr) ChtIndexer() *core.ChainIndexer { - return odr.chtIndexer -} - -// BloomTrieIndexer returns the bloom trie chain indexer -func (odr *LesOdr) BloomTrieIndexer() *core.ChainIndexer { - return odr.bloomTrieIndexer -} - -// BloomIndexer returns the bloombits chain indexer -func (odr *LesOdr) BloomIndexer() *core.ChainIndexer { - return odr.bloomIndexer -} - -const ( - MsgBlockBodies = iota - MsgCode - MsgReceipts - MsgProofsV1 - MsgProofsV2 - MsgHeaderProofs - MsgHelperTrieProofs -) - -// Msg encodes a LES message that delivers reply data for a request -type Msg struct { - MsgType int - ReqID uint64 - Obj interface{} -} - -// Retrieve tries to fetch an object from the LES network. -// If the network retrieval was successful, it stores the object in local db. -func (odr *LesOdr) Retrieve(ctx context.Context, req light.OdrRequest) (err error) { - lreq := LesRequest(req) - - reqID := genReqID() - rq := &distReq{ - getCost: func(dp distPeer) uint64 { - return lreq.GetCost(dp.(*peer)) - }, - canSend: func(dp distPeer) bool { - p := dp.(*peer) - return lreq.CanSend(p) - }, - request: func(dp distPeer) func() { - p := dp.(*peer) - cost := lreq.GetCost(p) - p.fcServer.QueueRequest(reqID, cost) - return func() { lreq.Request(reqID, p) } - }, - } - - if err = odr.retriever.retrieve(ctx, reqID, rq, func(p distPeer, msg *Msg) error { return lreq.Validate(odr.db, msg) }, odr.stop); err == nil { - // retrieved from network, store in db - req.StoreResult(odr.db) - } else { - log.Debug("Failed to retrieve data from network", "err", err) - } - return -} diff --git a/les/odr_requests.go b/les/odr_requests.go deleted file mode 100644 index 075fcd92ca..0000000000 --- a/les/odr_requests.go +++ /dev/null @@ -1,566 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package light implements on-demand retrieval capable state and chain objects -// for the Ethereum Light Client. -package les - -import ( - "encoding/binary" - "errors" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie" -) - -var ( - errInvalidMessageType = errors.New("invalid message type") - errInvalidEntryCount = errors.New("invalid number of response entries") - errHeaderUnavailable = errors.New("header unavailable") - errTxHashMismatch = errors.New("transaction hash mismatch") - errUncleHashMismatch = errors.New("uncle hash mismatch") - errReceiptHashMismatch = errors.New("receipt hash mismatch") - errDataHashMismatch = errors.New("data hash mismatch") - errCHTHashMismatch = errors.New("cht hash mismatch") - errCHTNumberMismatch = errors.New("cht number mismatch") - errUselessNodes = errors.New("useless nodes in merkle proof nodeset") -) - -type LesOdrRequest interface { - GetCost(*peer) uint64 - CanSend(*peer) bool - Request(uint64, *peer) error - Validate(ethdb.Database, *Msg) error -} - -func LesRequest(req light.OdrRequest) LesOdrRequest { - switch r := req.(type) { - case *light.BlockRequest: - return (*BlockRequest)(r) - case *light.ReceiptsRequest: - return (*ReceiptsRequest)(r) - case *light.TrieRequest: - return (*TrieRequest)(r) - case *light.CodeRequest: - return (*CodeRequest)(r) - case *light.ChtRequest: - return (*ChtRequest)(r) - case *light.BloomRequest: - return (*BloomRequest)(r) - default: - return nil - } -} - -// BlockRequest is the ODR request type for block bodies -type BlockRequest light.BlockRequest - -// GetCost returns the cost of the given ODR request according to the serving -// peer's cost table (implementation of LesOdrRequest) -func (r *BlockRequest) GetCost(peer *peer) uint64 { - return peer.GetRequestCost(GetBlockBodiesMsg, 1) -} - -// CanSend tells if a certain peer is suitable for serving the given request -func (r *BlockRequest) CanSend(peer *peer) bool { - return peer.HasBlock(r.Hash, r.Number) -} - -// Request sends an ODR request to the LES network (implementation of LesOdrRequest) -func (r *BlockRequest) Request(reqID uint64, peer *peer) error { - peer.Log().Debug("Requesting block body", "hash", r.Hash) - return peer.RequestBodies(reqID, r.GetCost(peer), []common.Hash{r.Hash}) -} - -// Valid processes an ODR request reply message from the LES network -// returns true and stores results in memory if the message was a valid reply -// to the request (implementation of LesOdrRequest) -func (r *BlockRequest) Validate(db ethdb.Database, msg *Msg) error { - log.Debug("Validating block body", "hash", r.Hash) - - // Ensure we have a correct message with a single block body - if msg.MsgType != MsgBlockBodies { - return errInvalidMessageType - } - bodies := msg.Obj.([]*types.Body) - if len(bodies) != 1 { - return errInvalidEntryCount - } - body := bodies[0] - - // Retrieve our stored header and validate block content against it - header := rawdb.ReadHeader(db, r.Hash, r.Number) - if header == nil { - return errHeaderUnavailable - } - if header.TxHash != types.DeriveSha(types.Transactions(body.Transactions)) { - return errTxHashMismatch - } - if header.UncleHash != types.CalcUncleHash(body.Uncles) { - return errUncleHashMismatch - } - // Validations passed, encode and store RLP - data, err := rlp.EncodeToBytes(body) - if err != nil { - return err - } - r.Rlp = data - return nil -} - -// ReceiptsRequest is the ODR request type for block receipts by block hash -type ReceiptsRequest light.ReceiptsRequest - -// GetCost returns the cost of the given ODR request according to the serving -// peer's cost table (implementation of LesOdrRequest) -func (r *ReceiptsRequest) GetCost(peer *peer) uint64 { - return peer.GetRequestCost(GetReceiptsMsg, 1) -} - -// CanSend tells if a certain peer is suitable for serving the given request -func (r *ReceiptsRequest) CanSend(peer *peer) bool { - return peer.HasBlock(r.Hash, r.Number) -} - -// Request sends an ODR request to the LES network (implementation of LesOdrRequest) -func (r *ReceiptsRequest) Request(reqID uint64, peer *peer) error { - peer.Log().Debug("Requesting block receipts", "hash", r.Hash) - return peer.RequestReceipts(reqID, r.GetCost(peer), []common.Hash{r.Hash}) -} - -// Valid processes an ODR request reply message from the LES network -// returns true and stores results in memory if the message was a valid reply -// to the request (implementation of LesOdrRequest) -func (r *ReceiptsRequest) Validate(db ethdb.Database, msg *Msg) error { - log.Debug("Validating block receipts", "hash", r.Hash) - - // Ensure we have a correct message with a single block receipt - if msg.MsgType != MsgReceipts { - return errInvalidMessageType - } - receipts := msg.Obj.([]types.Receipts) - if len(receipts) != 1 { - return errInvalidEntryCount - } - receipt := receipts[0] - - // Retrieve our stored header and validate receipt content against it - header := rawdb.ReadHeader(db, r.Hash, r.Number) - if header == nil { - return errHeaderUnavailable - } - if header.ReceiptHash != types.DeriveSha(receipt) { - return errReceiptHashMismatch - } - // Validations passed, store and return - r.Receipts = receipt - return nil -} - -type ProofReq struct { - BHash common.Hash - AccKey, Key []byte - FromLevel uint -} - -// ODR request type for state/storage trie entries, see LesOdrRequest interface -type TrieRequest light.TrieRequest - -// GetCost returns the cost of the given ODR request according to the serving -// peer's cost table (implementation of LesOdrRequest) -func (r *TrieRequest) GetCost(peer *peer) uint64 { - switch peer.version { - case lpv1: - return peer.GetRequestCost(GetProofsV1Msg, 1) - case lpv2: - return peer.GetRequestCost(GetProofsV2Msg, 1) - default: - panic(nil) - } -} - -// CanSend tells if a certain peer is suitable for serving the given request -func (r *TrieRequest) CanSend(peer *peer) bool { - return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber) -} - -// Request sends an ODR request to the LES network (implementation of LesOdrRequest) -func (r *TrieRequest) Request(reqID uint64, peer *peer) error { - peer.Log().Debug("Requesting trie proof", "root", r.Id.Root, "key", r.Key) - req := ProofReq{ - BHash: r.Id.BlockHash, - AccKey: r.Id.AccKey, - Key: r.Key, - } - return peer.RequestProofs(reqID, r.GetCost(peer), []ProofReq{req}) -} - -// Valid processes an ODR request reply message from the LES network -// returns true and stores results in memory if the message was a valid reply -// to the request (implementation of LesOdrRequest) -func (r *TrieRequest) Validate(db ethdb.Database, msg *Msg) error { - log.Debug("Validating trie proof", "root", r.Id.Root, "key", r.Key) - - switch msg.MsgType { - case MsgProofsV1: - proofs := msg.Obj.([]light.NodeList) - if len(proofs) != 1 { - return errInvalidEntryCount - } - nodeSet := proofs[0].NodeSet() - // Verify the proof and store if checks out - if _, _, err := trie.VerifyProof(r.Id.Root, r.Key, nodeSet); err != nil { - return fmt.Errorf("merkle proof verification failed: %v", err) - } - r.Proof = nodeSet - return nil - - case MsgProofsV2: - proofs := msg.Obj.(light.NodeList) - // Verify the proof and store if checks out - nodeSet := proofs.NodeSet() - reads := &readTraceDB{db: nodeSet} - if _, _, err := trie.VerifyProof(r.Id.Root, r.Key, reads); err != nil { - return fmt.Errorf("merkle proof verification failed: %v", err) - } - // check if all nodes have been read by VerifyProof - if len(reads.reads) != nodeSet.KeyCount() { - return errUselessNodes - } - r.Proof = nodeSet - return nil - - default: - return errInvalidMessageType - } -} - -type CodeReq struct { - BHash common.Hash - AccKey []byte -} - -// ODR request type for node data (used for retrieving contract code), see LesOdrRequest interface -type CodeRequest light.CodeRequest - -// GetCost returns the cost of the given ODR request according to the serving -// peer's cost table (implementation of LesOdrRequest) -func (r *CodeRequest) GetCost(peer *peer) uint64 { - return peer.GetRequestCost(GetCodeMsg, 1) -} - -// CanSend tells if a certain peer is suitable for serving the given request -func (r *CodeRequest) CanSend(peer *peer) bool { - return peer.HasBlock(r.Id.BlockHash, r.Id.BlockNumber) -} - -// Request sends an ODR request to the LES network (implementation of LesOdrRequest) -func (r *CodeRequest) Request(reqID uint64, peer *peer) error { - peer.Log().Debug("Requesting code data", "hash", r.Hash) - req := CodeReq{ - BHash: r.Id.BlockHash, - AccKey: r.Id.AccKey, - } - return peer.RequestCode(reqID, r.GetCost(peer), []CodeReq{req}) -} - -// Valid processes an ODR request reply message from the LES network -// returns true and stores results in memory if the message was a valid reply -// to the request (implementation of LesOdrRequest) -func (r *CodeRequest) Validate(db ethdb.Database, msg *Msg) error { - log.Debug("Validating code data", "hash", r.Hash) - - // Ensure we have a correct message with a single code element - if msg.MsgType != MsgCode { - return errInvalidMessageType - } - reply := msg.Obj.([][]byte) - if len(reply) != 1 { - return errInvalidEntryCount - } - data := reply[0] - - // Verify the data and store if checks out - if hash := crypto.Keccak256Hash(data); r.Hash != hash { - return errDataHashMismatch - } - r.Data = data - return nil -} - -const ( - // helper trie type constants - htCanonical = iota // Canonical hash trie - htBloomBits // BloomBits trie - - // applicable for all helper trie requests - auxRoot = 1 - // applicable for htCanonical - auxHeader = 2 -) - -type HelperTrieReq struct { - Type uint - TrieIdx uint64 - Key []byte - FromLevel, AuxReq uint -} - -type HelperTrieResps struct { // describes all responses, not just a single one - Proofs light.NodeList - AuxData [][]byte -} - -// legacy LES/1 -type ChtReq struct { - ChtNum, BlockNum uint64 - FromLevel uint -} - -// legacy LES/1 -type ChtResp struct { - Header *types.Header - Proof []rlp.RawValue -} - -// ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface -type ChtRequest light.ChtRequest - -// GetCost returns the cost of the given ODR request according to the serving -// peer's cost table (implementation of LesOdrRequest) -func (r *ChtRequest) GetCost(peer *peer) uint64 { - switch peer.version { - case lpv1: - return peer.GetRequestCost(GetHeaderProofsMsg, 1) - case lpv2: - return peer.GetRequestCost(GetHelperTrieProofsMsg, 1) - default: - panic(nil) - } -} - -// CanSend tells if a certain peer is suitable for serving the given request -func (r *ChtRequest) CanSend(peer *peer) bool { - peer.lock.RLock() - defer peer.lock.RUnlock() - - return peer.headInfo.Number >= light.HelperTrieConfirmations && r.ChtNum <= (peer.headInfo.Number-light.HelperTrieConfirmations)/light.CHTFrequencyClient -} - -// Request sends an ODR request to the LES network (implementation of LesOdrRequest) -func (r *ChtRequest) Request(reqID uint64, peer *peer) error { - peer.Log().Debug("Requesting CHT", "cht", r.ChtNum, "block", r.BlockNum) - var encNum [8]byte - binary.BigEndian.PutUint64(encNum[:], r.BlockNum) - req := HelperTrieReq{ - Type: htCanonical, - TrieIdx: r.ChtNum, - Key: encNum[:], - AuxReq: auxHeader, - } - return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), []HelperTrieReq{req}) -} - -// Valid processes an ODR request reply message from the LES network -// returns true and stores results in memory if the message was a valid reply -// to the request (implementation of LesOdrRequest) -func (r *ChtRequest) Validate(db ethdb.Database, msg *Msg) error { - log.Debug("Validating CHT", "cht", r.ChtNum, "block", r.BlockNum) - - switch msg.MsgType { - case MsgHeaderProofs: // LES/1 backwards compatibility - proofs := msg.Obj.([]ChtResp) - if len(proofs) != 1 { - return errInvalidEntryCount - } - proof := proofs[0] - - // Verify the CHT - var encNumber [8]byte - binary.BigEndian.PutUint64(encNumber[:], r.BlockNum) - - value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], light.NodeList(proof.Proof).NodeSet()) - if err != nil { - return err - } - var node light.ChtNode - if err := rlp.DecodeBytes(value, &node); err != nil { - return err - } - if node.Hash != proof.Header.Hash() { - return errCHTHashMismatch - } - // Verifications passed, store and return - r.Header = proof.Header - r.Proof = light.NodeList(proof.Proof).NodeSet() - r.Td = node.Td - case MsgHelperTrieProofs: - resp := msg.Obj.(HelperTrieResps) - if len(resp.AuxData) != 1 { - return errInvalidEntryCount - } - nodeSet := resp.Proofs.NodeSet() - headerEnc := resp.AuxData[0] - if len(headerEnc) == 0 { - return errHeaderUnavailable - } - header := new(types.Header) - if err := rlp.DecodeBytes(headerEnc, header); err != nil { - return errHeaderUnavailable - } - - // Verify the CHT - var encNumber [8]byte - binary.BigEndian.PutUint64(encNumber[:], r.BlockNum) - - reads := &readTraceDB{db: nodeSet} - value, _, err := trie.VerifyProof(r.ChtRoot, encNumber[:], reads) - if err != nil { - return fmt.Errorf("merkle proof verification failed: %v", err) - } - if len(reads.reads) != nodeSet.KeyCount() { - return errUselessNodes - } - - var node light.ChtNode - if err := rlp.DecodeBytes(value, &node); err != nil { - return err - } - if node.Hash != header.Hash() { - return errCHTHashMismatch - } - if r.BlockNum != header.Number.Uint64() { - return errCHTNumberMismatch - } - // Verifications passed, store and return - r.Header = header - r.Proof = nodeSet - r.Td = node.Td - default: - return errInvalidMessageType - } - return nil -} - -type BloomReq struct { - BloomTrieNum, BitIdx, SectionIdx, FromLevel uint64 -} - -// ODR request type for requesting headers by Canonical Hash Trie, see LesOdrRequest interface -type BloomRequest light.BloomRequest - -// GetCost returns the cost of the given ODR request according to the serving -// peer's cost table (implementation of LesOdrRequest) -func (r *BloomRequest) GetCost(peer *peer) uint64 { - return peer.GetRequestCost(GetHelperTrieProofsMsg, len(r.SectionIdxList)) -} - -// CanSend tells if a certain peer is suitable for serving the given request -func (r *BloomRequest) CanSend(peer *peer) bool { - peer.lock.RLock() - defer peer.lock.RUnlock() - - if peer.version < lpv2 { - return false - } - return peer.headInfo.Number >= light.HelperTrieConfirmations && r.BloomTrieNum <= (peer.headInfo.Number-light.HelperTrieConfirmations)/light.BloomTrieFrequency -} - -// Request sends an ODR request to the LES network (implementation of LesOdrRequest) -func (r *BloomRequest) Request(reqID uint64, peer *peer) error { - peer.Log().Debug("Requesting BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIdxList) - reqs := make([]HelperTrieReq, len(r.SectionIdxList)) - - var encNumber [10]byte - binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx)) - - for i, sectionIdx := range r.SectionIdxList { - binary.BigEndian.PutUint64(encNumber[2:], sectionIdx) - reqs[i] = HelperTrieReq{ - Type: htBloomBits, - TrieIdx: r.BloomTrieNum, - Key: common.CopyBytes(encNumber[:]), - } - } - return peer.RequestHelperTrieProofs(reqID, r.GetCost(peer), reqs) -} - -// Valid processes an ODR request reply message from the LES network -// returns true and stores results in memory if the message was a valid reply -// to the request (implementation of LesOdrRequest) -func (r *BloomRequest) Validate(db ethdb.Database, msg *Msg) error { - log.Debug("Validating BloomBits", "bloomTrie", r.BloomTrieNum, "bitIdx", r.BitIdx, "sections", r.SectionIdxList) - - // Ensure we have a correct message with a single proof element - if msg.MsgType != MsgHelperTrieProofs { - return errInvalidMessageType - } - resps := msg.Obj.(HelperTrieResps) - proofs := resps.Proofs - nodeSet := proofs.NodeSet() - reads := &readTraceDB{db: nodeSet} - - r.BloomBits = make([][]byte, len(r.SectionIdxList)) - - // Verify the proofs - var encNumber [10]byte - binary.BigEndian.PutUint16(encNumber[:2], uint16(r.BitIdx)) - - for i, idx := range r.SectionIdxList { - binary.BigEndian.PutUint64(encNumber[2:], idx) - value, _, err := trie.VerifyProof(r.BloomTrieRoot, encNumber[:], reads) - if err != nil { - return err - } - r.BloomBits[i] = value - } - - if len(reads.reads) != nodeSet.KeyCount() { - return errUselessNodes - } - r.Proofs = nodeSet - return nil -} - -// readTraceDB stores the keys of database reads. We use this to check that received node -// sets contain only the trie nodes necessary to make proofs pass. -type readTraceDB struct { - db trie.DatabaseReader - reads map[string]struct{} -} - -// Get returns a stored node -func (db *readTraceDB) Get(k []byte) ([]byte, error) { - if db.reads == nil { - db.reads = make(map[string]struct{}) - } - db.reads[string(k)] = struct{}{} - return db.db.Get(k) -} - -// Has returns true if the node set contains the given key -func (db *readTraceDB) Has(key []byte) (bool, error) { - _, err := db.Get(key) - return err == nil, nil -} diff --git a/les/odr_test.go b/les/odr_test.go deleted file mode 100644 index 983f7262b0..0000000000 --- a/les/odr_test.go +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package les - -import ( - "bytes" - "context" - "math/big" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" -) - -type odrTestFn func(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte - -func TestOdrGetBlockLes1(t *testing.T) { testOdr(t, 1, 1, odrGetBlock) } - -func TestOdrGetBlockLes2(t *testing.T) { testOdr(t, 2, 1, odrGetBlock) } - -func odrGetBlock(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { - var block *types.Block - if bc != nil { - block = bc.GetBlockByHash(bhash) - } else { - block, _ = lc.GetBlockByHash(ctx, bhash) - } - if block == nil { - return nil - } - rlp, _ := rlp.EncodeToBytes(block) - return rlp -} - -func TestOdrGetReceiptsLes1(t *testing.T) { testOdr(t, 1, 1, odrGetReceipts) } - -func TestOdrGetReceiptsLes2(t *testing.T) { testOdr(t, 2, 1, odrGetReceipts) } - -func odrGetReceipts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { - var receipts types.Receipts - if bc != nil { - if number := rawdb.ReadHeaderNumber(db, bhash); number != nil { - receipts = rawdb.ReadReceipts(db, bhash, *number) - } - } else { - if number := rawdb.ReadHeaderNumber(db, bhash); number != nil { - receipts, _ = light.GetBlockReceipts(ctx, lc.Odr(), bhash, *number) - } - } - if receipts == nil { - return nil - } - rlp, _ := rlp.EncodeToBytes(receipts) - return rlp -} - -func TestOdrAccountsLes1(t *testing.T) { testOdr(t, 1, 1, odrAccounts) } - -func TestOdrAccountsLes2(t *testing.T) { testOdr(t, 2, 1, odrAccounts) } - -func odrAccounts(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { - dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678") - acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr} - - var ( - res []byte - st *state.StateDB - err error - ) - for _, addr := range acc { - if bc != nil { - header := bc.GetHeaderByHash(bhash) - st, err = state.New(header.Root, state.NewDatabase(db)) - } else { - header := lc.GetHeaderByHash(bhash) - st = light.NewState(ctx, header, lc.Odr()) - } - if err == nil { - bal := st.GetBalance(addr) - rlp, _ := rlp.EncodeToBytes(bal) - res = append(res, rlp...) - } - } - return res -} - -func TestOdrContractCallLes1(t *testing.T) { testOdr(t, 1, 2, odrContractCall) } - -func TestOdrContractCallLes2(t *testing.T) { testOdr(t, 2, 2, odrContractCall) } - -type callmsg struct { - types.Message -} - -func (callmsg) CheckNonce() bool { return false } - -func odrContractCall(ctx context.Context, db ethdb.Database, config *params.ChainConfig, bc *core.BlockChain, lc *light.LightChain, bhash common.Hash) []byte { - data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") - - var res []byte - for i := 0; i < 3; i++ { - data[35] = byte(i) - if bc != nil { - header := bc.GetHeaderByHash(bhash) - statedb, err := state.New(header.Root, state.NewDatabase(db)) - - if err == nil { - from := statedb.GetOrNewStateObject(testBankAddress) - from.SetBalance(math.MaxBig256) - - msg := callmsg{types.NewMessage(from.Address(), &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false)} - - context := core.NewEVMContext(msg, header, bc, nil) - vmenv := vm.NewEVM(context, statedb, config, vm.Config{}) - - //vmenv := core.NewEnv(statedb, config, bc, msg, header, vm.Config{}) - gp := new(core.GasPool).AddGas(math.MaxUint64) - ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp) - res = append(res, ret...) - } - } else { - header := lc.GetHeaderByHash(bhash) - state := light.NewState(ctx, header, lc.Odr()) - state.SetBalance(testBankAddress, math.MaxBig256) - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 100000, new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, lc, nil) - vmenv := vm.NewEVM(context, state, config, vm.Config{}) - gp := new(core.GasPool).AddGas(math.MaxUint64) - ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp) - if state.Error() == nil { - res = append(res, ret...) - } - } - } - return res -} - -func testOdr(t *testing.T, protocol int, expFail uint64, fn odrTestFn) { - // Assemble the test environment - peers := newPeerSet() - dist := newRequestDistributor(peers, make(chan struct{})) - rm := newRetrieveManager(peers, dist, nil) - db := ethdb.NewMemDatabase() - ldb := ethdb.NewMemDatabase() - odr := NewLesOdr(ldb, light.NewChtIndexer(db, true), light.NewBloomTrieIndexer(db, true), eth.NewBloomIndexer(db, light.BloomTrieFrequency), rm) - pm := newTestProtocolManagerMust(t, false, 4, testChainGen, nil, nil, db) - lpm := newTestProtocolManagerMust(t, true, 0, nil, peers, odr, ldb) - _, err1, lpeer, err2 := newTestPeerPair("peer", protocol, pm, lpm) - select { - case <-time.After(time.Millisecond * 100): - case err := <-err1: - t.Fatalf("peer 1 handshake error: %v", err) - case err := <-err2: - t.Fatalf("peer 1 handshake error: %v", err) - } - - lpm.synchronise(lpeer) - - test := func(expFail uint64) { - for i := uint64(0); i <= pm.blockchain.CurrentHeader().Number.Uint64(); i++ { - bhash := rawdb.ReadCanonicalHash(db, i) - b1 := fn(light.NoOdr, db, pm.chainConfig, pm.blockchain.(*core.BlockChain), nil, bhash) - - ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) - defer cancel() - b2 := fn(ctx, ldb, lpm.chainConfig, nil, lpm.blockchain.(*light.LightChain), bhash) - - eq := bytes.Equal(b1, b2) - exp := i < expFail - if exp && !eq { - t.Errorf("odr mismatch") - } - if !exp && eq { - t.Errorf("unexpected odr match") - } - } - } - - // temporarily remove peer to test odr fails - // expect retrievals to fail (except genesis block) without a les peer - peers.Unregister(lpeer.id) - time.Sleep(time.Millisecond * 10) // ensure that all peerSetNotify callbacks are executed - test(expFail) - // expect all retrievals to pass - peers.Register(lpeer) - time.Sleep(time.Millisecond * 10) // ensure that all peerSetNotify callbacks are executed - lpeer.lock.Lock() - lpeer.hasBlock = func(common.Hash, uint64) bool { return true } - lpeer.lock.Unlock() - test(5) - // still expect all retrievals to pass, now data should be cached locally - peers.Unregister(lpeer.id) - time.Sleep(time.Millisecond * 10) // ensure that all peerSetNotify callbacks are executed - test(5) -} diff --git a/les/peer.go b/les/peer.go deleted file mode 100644 index eb7452e276..0000000000 --- a/les/peer.go +++ /dev/null @@ -1,660 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package les implements the Light Ethereum Subprotocol. -package les - -import ( - "crypto/ecdsa" - "encoding/binary" - "errors" - "fmt" - "math/big" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/les/flowcontrol" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rlp" -) - -var ( - errClosed = errors.New("peer set is closed") - errAlreadyRegistered = errors.New("peer is already registered") - errNotRegistered = errors.New("peer is not registered") -) - -const maxResponseErrors = 50 // number of invalid responses tolerated (makes the protocol less brittle but still avoids spam) - -const ( - announceTypeNone = iota - announceTypeSimple - announceTypeSigned -) - -type peer struct { - *p2p.Peer - pubKey *ecdsa.PublicKey - - rw p2p.MsgReadWriter - - version int // Protocol version negotiated - network uint64 // Network ID being on - - announceType, requestAnnounceType uint64 - - id string - - headInfo *announceData - lock sync.RWMutex - - announceChn chan announceData - sendQueue *execQueue - - poolEntry *poolEntry - hasBlock func(common.Hash, uint64) bool - responseErrors int - - fcClient *flowcontrol.ClientNode // nil if the peer is server only - fcServer *flowcontrol.ServerNode // nil if the peer is client only - fcServerParams *flowcontrol.ServerParams - fcCosts requestCostTable -} - -func newPeer(version int, network uint64, p *p2p.Peer, rw p2p.MsgReadWriter) *peer { - id := p.ID() - pubKey, _ := id.Pubkey() - - return &peer{ - Peer: p, - pubKey: pubKey, - rw: rw, - version: version, - network: network, - id: fmt.Sprintf("%x", id[:8]), - announceChn: make(chan announceData, 20), - } -} - -func (p *peer) canQueue() bool { - return p.sendQueue.canQueue() -} - -func (p *peer) queueSend(f func()) { - p.sendQueue.queue(f) -} - -// Info gathers and returns a collection of metadata known about a peer. -func (p *peer) Info() *eth.PeerInfo { - return ð.PeerInfo{ - Version: p.version, - Difficulty: p.Td(), - Head: fmt.Sprintf("%x", p.Head()), - } -} - -// Head retrieves a copy of the current head (most recent) hash of the peer. -func (p *peer) Head() (hash common.Hash) { - p.lock.RLock() - defer p.lock.RUnlock() - - copy(hash[:], p.headInfo.Hash[:]) - return hash -} - -func (p *peer) HeadAndTd() (hash common.Hash, td *big.Int) { - p.lock.RLock() - defer p.lock.RUnlock() - - copy(hash[:], p.headInfo.Hash[:]) - return hash, p.headInfo.Td -} - -func (p *peer) headBlockInfo() blockInfo { - p.lock.RLock() - defer p.lock.RUnlock() - - return blockInfo{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td} -} - -// Td retrieves the current total difficulty of a peer. -func (p *peer) Td() *big.Int { - p.lock.RLock() - defer p.lock.RUnlock() - - return new(big.Int).Set(p.headInfo.Td) -} - -// waitBefore implements distPeer interface -func (p *peer) waitBefore(maxCost uint64) (time.Duration, float64) { - return p.fcServer.CanSend(maxCost) -} - -func sendRequest(w p2p.MsgWriter, msgcode, reqID, cost uint64, data interface{}) error { - type req struct { - ReqID uint64 - Data interface{} - } - return p2p.Send(w, msgcode, req{reqID, data}) -} - -func sendResponse(w p2p.MsgWriter, msgcode, reqID, bv uint64, data interface{}) error { - type resp struct { - ReqID, BV uint64 - Data interface{} - } - return p2p.Send(w, msgcode, resp{reqID, bv, data}) -} - -func (p *peer) GetRequestCost(msgcode uint64, amount int) uint64 { - p.lock.RLock() - defer p.lock.RUnlock() - - cost := p.fcCosts[msgcode].baseCost + p.fcCosts[msgcode].reqCost*uint64(amount) - if cost > p.fcServerParams.BufLimit { - cost = p.fcServerParams.BufLimit - } - return cost -} - -// HasBlock checks if the peer has a given block -func (p *peer) HasBlock(hash common.Hash, number uint64) bool { - p.lock.RLock() - hasBlock := p.hasBlock - p.lock.RUnlock() - return hasBlock != nil && hasBlock(hash, number) -} - -// SendAnnounce announces the availability of a number of blocks through -// a hash notification. -func (p *peer) SendAnnounce(request announceData) error { - return p2p.Send(p.rw, AnnounceMsg, request) -} - -// SendBlockHeaders sends a batch of block headers to the remote peer. -func (p *peer) SendBlockHeaders(reqID, bv uint64, headers []*types.Header) error { - return sendResponse(p.rw, BlockHeadersMsg, reqID, bv, headers) -} - -// SendBlockBodiesRLP sends a batch of block contents to the remote peer from -// an already RLP encoded format. -func (p *peer) SendBlockBodiesRLP(reqID, bv uint64, bodies []rlp.RawValue) error { - return sendResponse(p.rw, BlockBodiesMsg, reqID, bv, bodies) -} - -// SendCodeRLP sends a batch of arbitrary internal data, corresponding to the -// hashes requested. -func (p *peer) SendCode(reqID, bv uint64, data [][]byte) error { - return sendResponse(p.rw, CodeMsg, reqID, bv, data) -} - -// SendReceiptsRLP sends a batch of transaction receipts, corresponding to the -// ones requested from an already RLP encoded format. -func (p *peer) SendReceiptsRLP(reqID, bv uint64, receipts []rlp.RawValue) error { - return sendResponse(p.rw, ReceiptsMsg, reqID, bv, receipts) -} - -// SendProofs sends a batch of legacy LES/1 merkle proofs, corresponding to the ones requested. -func (p *peer) SendProofs(reqID, bv uint64, proofs proofsData) error { - return sendResponse(p.rw, ProofsV1Msg, reqID, bv, proofs) -} - -// SendProofsV2 sends a batch of merkle proofs, corresponding to the ones requested. -func (p *peer) SendProofsV2(reqID, bv uint64, proofs light.NodeList) error { - return sendResponse(p.rw, ProofsV2Msg, reqID, bv, proofs) -} - -// SendHeaderProofs sends a batch of legacy LES/1 header proofs, corresponding to the ones requested. -func (p *peer) SendHeaderProofs(reqID, bv uint64, proofs []ChtResp) error { - return sendResponse(p.rw, HeaderProofsMsg, reqID, bv, proofs) -} - -// SendHelperTrieProofs sends a batch of HelperTrie proofs, corresponding to the ones requested. -func (p *peer) SendHelperTrieProofs(reqID, bv uint64, resp HelperTrieResps) error { - return sendResponse(p.rw, HelperTrieProofsMsg, reqID, bv, resp) -} - -// SendTxStatus sends a batch of transaction status records, corresponding to the ones requested. -func (p *peer) SendTxStatus(reqID, bv uint64, stats []txStatus) error { - return sendResponse(p.rw, TxStatusMsg, reqID, bv, stats) -} - -// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the -// specified header query, based on the hash of an origin block. -func (p *peer) RequestHeadersByHash(reqID, cost uint64, origin common.Hash, amount int, skip int, reverse bool) error { - p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse) - return sendRequest(p.rw, GetBlockHeadersMsg, reqID, cost, &getBlockHeadersData{Origin: hashOrNumber{Hash: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) -} - -// RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the -// specified header query, based on the number of an origin block. -func (p *peer) RequestHeadersByNumber(reqID, cost, origin uint64, amount int, skip int, reverse bool) error { - p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse) - return sendRequest(p.rw, GetBlockHeadersMsg, reqID, cost, &getBlockHeadersData{Origin: hashOrNumber{Number: origin}, Amount: uint64(amount), Skip: uint64(skip), Reverse: reverse}) -} - -// RequestBodies fetches a batch of blocks' bodies corresponding to the hashes -// specified. -func (p *peer) RequestBodies(reqID, cost uint64, hashes []common.Hash) error { - p.Log().Debug("Fetching batch of block bodies", "count", len(hashes)) - return sendRequest(p.rw, GetBlockBodiesMsg, reqID, cost, hashes) -} - -// RequestCode fetches a batch of arbitrary data from a node's known state -// data, corresponding to the specified hashes. -func (p *peer) RequestCode(reqID, cost uint64, reqs []CodeReq) error { - p.Log().Debug("Fetching batch of codes", "count", len(reqs)) - return sendRequest(p.rw, GetCodeMsg, reqID, cost, reqs) -} - -// RequestReceipts fetches a batch of transaction receipts from a remote node. -func (p *peer) RequestReceipts(reqID, cost uint64, hashes []common.Hash) error { - p.Log().Debug("Fetching batch of receipts", "count", len(hashes)) - return sendRequest(p.rw, GetReceiptsMsg, reqID, cost, hashes) -} - -// RequestProofs fetches a batch of merkle proofs from a remote node. -func (p *peer) RequestProofs(reqID, cost uint64, reqs []ProofReq) error { - p.Log().Debug("Fetching batch of proofs", "count", len(reqs)) - switch p.version { - case lpv1: - return sendRequest(p.rw, GetProofsV1Msg, reqID, cost, reqs) - case lpv2: - return sendRequest(p.rw, GetProofsV2Msg, reqID, cost, reqs) - default: - panic(nil) - } -} - -// RequestHelperTrieProofs fetches a batch of HelperTrie merkle proofs from a remote node. -func (p *peer) RequestHelperTrieProofs(reqID, cost uint64, reqs []HelperTrieReq) error { - p.Log().Debug("Fetching batch of HelperTrie proofs", "count", len(reqs)) - switch p.version { - case lpv1: - reqsV1 := make([]ChtReq, len(reqs)) - for i, req := range reqs { - if req.Type != htCanonical || req.AuxReq != auxHeader || len(req.Key) != 8 { - return fmt.Errorf("Request invalid in LES/1 mode") - } - blockNum := binary.BigEndian.Uint64(req.Key) - // convert HelperTrie request to old CHT request - reqsV1[i] = ChtReq{ChtNum: (req.TrieIdx + 1) * (light.CHTFrequencyClient / light.CHTFrequencyServer), BlockNum: blockNum, FromLevel: req.FromLevel} - } - return sendRequest(p.rw, GetHeaderProofsMsg, reqID, cost, reqsV1) - case lpv2: - return sendRequest(p.rw, GetHelperTrieProofsMsg, reqID, cost, reqs) - default: - panic(nil) - } -} - -// RequestTxStatus fetches a batch of transaction status records from a remote node. -func (p *peer) RequestTxStatus(reqID, cost uint64, txHashes []common.Hash) error { - p.Log().Debug("Requesting transaction status", "count", len(txHashes)) - return sendRequest(p.rw, GetTxStatusMsg, reqID, cost, txHashes) -} - -// SendTxStatus sends a batch of transactions to be added to the remote transaction pool. -func (p *peer) SendTxs(reqID, cost uint64, txs types.Transactions) error { - p.Log().Debug("Fetching batch of transactions", "count", len(txs)) - switch p.version { - case lpv1: - return p2p.Send(p.rw, SendTxMsg, txs) // old message format does not include reqID - case lpv2: - return sendRequest(p.rw, SendTxV2Msg, reqID, cost, txs) - default: - panic(nil) - } -} - -type keyValueEntry struct { - Key string - Value rlp.RawValue -} -type keyValueList []keyValueEntry -type keyValueMap map[string]rlp.RawValue - -func (l keyValueList) add(key string, val interface{}) keyValueList { - var entry keyValueEntry - entry.Key = key - if val == nil { - val = uint64(0) - } - enc, err := rlp.EncodeToBytes(val) - if err == nil { - entry.Value = enc - } - return append(l, entry) -} - -func (l keyValueList) decode() keyValueMap { - m := make(keyValueMap) - for _, entry := range l { - m[entry.Key] = entry.Value - } - return m -} - -func (m keyValueMap) get(key string, val interface{}) error { - enc, ok := m[key] - if !ok { - return errResp(ErrMissingKey, "%s", key) - } - if val == nil { - return nil - } - return rlp.DecodeBytes(enc, val) -} - -func (p *peer) sendReceiveHandshake(sendList keyValueList) (keyValueList, error) { - // Send out own handshake in a new thread - errc := make(chan error, 1) - go func() { - errc <- p2p.Send(p.rw, StatusMsg, sendList) - }() - // In the mean time retrieve the remote status message - msg, err := p.rw.ReadMsg() - if err != nil { - return nil, err - } - if msg.Code != StatusMsg { - return nil, errResp(ErrNoStatusMsg, "first msg has code %x (!= %x)", msg.Code, StatusMsg) - } - if msg.Size > ProtocolMaxMsgSize { - return nil, errResp(ErrMsgTooLarge, "%v > %v", msg.Size, ProtocolMaxMsgSize) - } - // Decode the handshake - var recvList keyValueList - if err := msg.Decode(&recvList); err != nil { - return nil, errResp(ErrDecode, "msg %v: %v", msg, err) - } - if err := <-errc; err != nil { - return nil, err - } - return recvList, nil -} - -// Handshake executes the les protocol handshake, negotiating version number, -// network IDs, difficulties, head and genesis blocks. -func (p *peer) Handshake(td *big.Int, head common.Hash, headNum uint64, genesis common.Hash, server *LesServer) error { - p.lock.Lock() - defer p.lock.Unlock() - - var send keyValueList - send = send.add("protocolVersion", uint64(p.version)) - send = send.add("networkId", p.network) - send = send.add("headTd", td) - send = send.add("headHash", head) - send = send.add("headNum", headNum) - send = send.add("genesisHash", genesis) - if server != nil { - send = send.add("serveHeaders", nil) - send = send.add("serveChainSince", uint64(0)) - send = send.add("serveStateSince", uint64(0)) - send = send.add("txRelay", nil) - send = send.add("flowControl/BL", server.defParams.BufLimit) - send = send.add("flowControl/MRR", server.defParams.MinRecharge) - list := server.fcCostStats.getCurrentList() - send = send.add("flowControl/MRC", list) - p.fcCosts = list.decode() - } else { - p.requestAnnounceType = announceTypeSimple // set to default until "very light" client mode is implemented - send = send.add("announceType", p.requestAnnounceType) - } - recvList, err := p.sendReceiveHandshake(send) - if err != nil { - return err - } - recv := recvList.decode() - - var rGenesis, rHash common.Hash - var rVersion, rNetwork, rNum uint64 - var rTd *big.Int - - if err := recv.get("protocolVersion", &rVersion); err != nil { - return err - } - if err := recv.get("networkId", &rNetwork); err != nil { - return err - } - if err := recv.get("headTd", &rTd); err != nil { - return err - } - if err := recv.get("headHash", &rHash); err != nil { - return err - } - if err := recv.get("headNum", &rNum); err != nil { - return err - } - if err := recv.get("genesisHash", &rGenesis); err != nil { - return err - } - - if rGenesis != genesis { - return errResp(ErrGenesisBlockMismatch, "%x (!= %x)", rGenesis[:8], genesis[:8]) - } - if rNetwork != p.network { - return errResp(ErrNetworkIdMismatch, "%d (!= %d)", rNetwork, p.network) - } - if int(rVersion) != p.version { - return errResp(ErrProtocolVersionMismatch, "%d (!= %d)", rVersion, p.version) - } - if server != nil { - // until we have a proper peer connectivity API, allow LES connection to other servers - /*if recv.get("serveStateSince", nil) == nil { - return errResp(ErrUselessPeer, "wanted client, got server") - }*/ - if recv.get("announceType", &p.announceType) != nil { - p.announceType = announceTypeSimple - } - p.fcClient = flowcontrol.NewClientNode(server.fcManager, server.defParams) - } else { - if recv.get("serveChainSince", nil) != nil { - return errResp(ErrUselessPeer, "peer cannot serve chain") - } - if recv.get("serveStateSince", nil) != nil { - return errResp(ErrUselessPeer, "peer cannot serve state") - } - if recv.get("txRelay", nil) != nil { - return errResp(ErrUselessPeer, "peer cannot relay transactions") - } - params := &flowcontrol.ServerParams{} - if err := recv.get("flowControl/BL", ¶ms.BufLimit); err != nil { - return err - } - if err := recv.get("flowControl/MRR", ¶ms.MinRecharge); err != nil { - return err - } - var MRC RequestCostList - if err := recv.get("flowControl/MRC", &MRC); err != nil { - return err - } - p.fcServerParams = params - p.fcServer = flowcontrol.NewServerNode(params) - p.fcCosts = MRC.decode() - } - - p.headInfo = &announceData{Td: rTd, Hash: rHash, Number: rNum} - return nil -} - -// String implements fmt.Stringer. -func (p *peer) String() string { - return fmt.Sprintf("Peer %s [%s]", p.id, - fmt.Sprintf("les/%d", p.version), - ) -} - -// peerSetNotify is a callback interface to notify services about added or -// removed peers -type peerSetNotify interface { - registerPeer(*peer) - unregisterPeer(*peer) -} - -// peerSet represents the collection of active peers currently participating in -// the Light Ethereum sub-protocol. -type peerSet struct { - peers map[string]*peer - lock sync.RWMutex - notifyList []peerSetNotify - closed bool -} - -// newPeerSet creates a new peer set to track the active participants. -func newPeerSet() *peerSet { - return &peerSet{ - peers: make(map[string]*peer), - } -} - -// notify adds a service to be notified about added or removed peers -func (ps *peerSet) notify(n peerSetNotify) { - ps.lock.Lock() - ps.notifyList = append(ps.notifyList, n) - peers := make([]*peer, 0, len(ps.peers)) - for _, p := range ps.peers { - peers = append(peers, p) - } - ps.lock.Unlock() - - for _, p := range peers { - n.registerPeer(p) - } -} - -// Register injects a new peer into the working set, or returns an error if the -// peer is already known. -func (ps *peerSet) Register(p *peer) error { - ps.lock.Lock() - if ps.closed { - ps.lock.Unlock() - return errClosed - } - if _, ok := ps.peers[p.id]; ok { - ps.lock.Unlock() - return errAlreadyRegistered - } - ps.peers[p.id] = p - p.sendQueue = newExecQueue(100) - peers := make([]peerSetNotify, len(ps.notifyList)) - copy(peers, ps.notifyList) - ps.lock.Unlock() - - for _, n := range peers { - n.registerPeer(p) - } - return nil -} - -// Unregister removes a remote peer from the active set, disabling any further -// actions to/from that particular entity. It also initiates disconnection at the networking layer. -func (ps *peerSet) Unregister(id string) error { - ps.lock.Lock() - if p, ok := ps.peers[id]; !ok { - ps.lock.Unlock() - return errNotRegistered - } else { - delete(ps.peers, id) - peers := make([]peerSetNotify, len(ps.notifyList)) - copy(peers, ps.notifyList) - ps.lock.Unlock() - - for _, n := range peers { - n.unregisterPeer(p) - } - p.sendQueue.quit() - p.Peer.Disconnect(p2p.DiscUselessPeer) - return nil - } -} - -// AllPeerIDs returns a list of all registered peer IDs -func (ps *peerSet) AllPeerIDs() []string { - ps.lock.RLock() - defer ps.lock.RUnlock() - - res := make([]string, len(ps.peers)) - idx := 0 - for id := range ps.peers { - res[idx] = id - idx++ - } - return res -} - -// Peer retrieves the registered peer with the given id. -func (ps *peerSet) Peer(id string) *peer { - ps.lock.RLock() - defer ps.lock.RUnlock() - - return ps.peers[id] -} - -// Len returns if the current number of peers in the set. -func (ps *peerSet) Len() int { - ps.lock.RLock() - defer ps.lock.RUnlock() - - return len(ps.peers) -} - -// BestPeer retrieves the known peer with the currently highest total difficulty. -func (ps *peerSet) BestPeer() *peer { - ps.lock.RLock() - defer ps.lock.RUnlock() - - var ( - bestPeer *peer - bestTd *big.Int - ) - for _, p := range ps.peers { - if td := p.Td(); bestPeer == nil || td.Cmp(bestTd) > 0 { - bestPeer, bestTd = p, td - } - } - return bestPeer -} - -// AllPeers returns all peers in a list -func (ps *peerSet) AllPeers() []*peer { - ps.lock.RLock() - defer ps.lock.RUnlock() - - list := make([]*peer, len(ps.peers)) - i := 0 - for _, peer := range ps.peers { - list[i] = peer - i++ - } - return list -} - -// Close disconnects all peers. -// No new peers can be registered after Close has returned. -func (ps *peerSet) Close() { - ps.lock.Lock() - defer ps.lock.Unlock() - - for _, p := range ps.peers { - p.Disconnect(p2p.DiscQuitting) - } - ps.closed = true -} diff --git a/les/protocol.go b/les/protocol.go deleted file mode 100644 index ee4c223988..0000000000 --- a/les/protocol.go +++ /dev/null @@ -1,229 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package les implements the Light Ethereum Subprotocol. -package les - -import ( - "bytes" - "crypto/ecdsa" - "crypto/elliptic" - "errors" - "fmt" - "io" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/secp256k1" - "github.com/ethereum/go-ethereum/rlp" -) - -// Constants to match up protocol versions and messages -const ( - lpv1 = 1 - lpv2 = 2 -) - -// Supported versions of the les protocol (first is primary) -var ( - ClientProtocolVersions = []uint{lpv2, lpv1} - ServerProtocolVersions = []uint{lpv2, lpv1} - AdvertiseProtocolVersions = []uint{lpv2} // clients are searching for the first advertised protocol in the list -) - -// Number of implemented message corresponding to different protocol versions. -var ProtocolLengths = map[uint]uint64{lpv1: 15, lpv2: 22} - -const ( - NetworkId = 1 - ProtocolMaxMsgSize = 10 * 1024 * 1024 // Maximum cap on the size of a protocol message -) - -// les protocol message codes -const ( - // Protocol messages belonging to LPV1 - StatusMsg = 0x00 - AnnounceMsg = 0x01 - GetBlockHeadersMsg = 0x02 - BlockHeadersMsg = 0x03 - GetBlockBodiesMsg = 0x04 - BlockBodiesMsg = 0x05 - GetReceiptsMsg = 0x06 - ReceiptsMsg = 0x07 - GetProofsV1Msg = 0x08 - ProofsV1Msg = 0x09 - GetCodeMsg = 0x0a - CodeMsg = 0x0b - SendTxMsg = 0x0c - GetHeaderProofsMsg = 0x0d - HeaderProofsMsg = 0x0e - // Protocol messages belonging to LPV2 - GetProofsV2Msg = 0x0f - ProofsV2Msg = 0x10 - GetHelperTrieProofsMsg = 0x11 - HelperTrieProofsMsg = 0x12 - SendTxV2Msg = 0x13 - GetTxStatusMsg = 0x14 - TxStatusMsg = 0x15 -) - -type errCode int - -const ( - ErrMsgTooLarge = iota - ErrDecode - ErrInvalidMsgCode - ErrProtocolVersionMismatch - ErrNetworkIdMismatch - ErrGenesisBlockMismatch - ErrNoStatusMsg - ErrExtraStatusMsg - ErrSuspendedPeer - ErrUselessPeer - ErrRequestRejected - ErrUnexpectedResponse - ErrInvalidResponse - ErrTooManyTimeouts - ErrMissingKey -) - -func (e errCode) String() string { - return errorToString[int(e)] -} - -// XXX change once legacy code is out -var errorToString = map[int]string{ - ErrMsgTooLarge: "Message too long", - ErrDecode: "Invalid message", - ErrInvalidMsgCode: "Invalid message code", - ErrProtocolVersionMismatch: "Protocol version mismatch", - ErrNetworkIdMismatch: "NetworkId mismatch", - ErrGenesisBlockMismatch: "Genesis block mismatch", - ErrNoStatusMsg: "No status message", - ErrExtraStatusMsg: "Extra status message", - ErrSuspendedPeer: "Suspended peer", - ErrRequestRejected: "Request rejected", - ErrUnexpectedResponse: "Unexpected response", - ErrInvalidResponse: "Invalid response", - ErrTooManyTimeouts: "Too many request timeouts", - ErrMissingKey: "Key missing from list", -} - -type announceBlock struct { - Hash common.Hash // Hash of one particular block being announced - Number uint64 // Number of one particular block being announced - Td *big.Int // Total difficulty of one particular block being announced -} - -// announceData is the network packet for the block announcements. -type announceData struct { - Hash common.Hash // Hash of one particular block being announced - Number uint64 // Number of one particular block being announced - Td *big.Int // Total difficulty of one particular block being announced - ReorgDepth uint64 - Update keyValueList -} - -// sign adds a signature to the block announcement by the given privKey -func (a *announceData) sign(privKey *ecdsa.PrivateKey) { - rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td}) - sig, _ := crypto.Sign(crypto.Keccak256(rlp), privKey) - a.Update = a.Update.add("sign", sig) -} - -// checkSignature verifies if the block announcement has a valid signature by the given pubKey -func (a *announceData) checkSignature(pubKey *ecdsa.PublicKey) error { - var sig []byte - if err := a.Update.decode().get("sign", &sig); err != nil { - return err - } - rlp, _ := rlp.EncodeToBytes(announceBlock{a.Hash, a.Number, a.Td}) - recPubkey, err := secp256k1.RecoverPubkey(crypto.Keccak256(rlp), sig) - if err != nil { - return err - } - pbytes := elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y) - if bytes.Equal(pbytes, recPubkey) { - return nil - } - return errors.New("Wrong signature") -} - -type blockInfo struct { - Hash common.Hash // Hash of one particular block being announced - Number uint64 // Number of one particular block being announced - Td *big.Int // Total difficulty of one particular block being announced -} - -// getBlockHeadersData represents a block header query. -type getBlockHeadersData struct { - Origin hashOrNumber // Block from which to retrieve headers - Amount uint64 // Maximum number of headers to retrieve - Skip uint64 // Blocks to skip between consecutive headers - Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis) -} - -// hashOrNumber is a combined field for specifying an origin block. -type hashOrNumber struct { - Hash common.Hash // Block hash from which to retrieve headers (excludes Number) - Number uint64 // Block hash from which to retrieve headers (excludes Hash) -} - -// EncodeRLP is a specialized encoder for hashOrNumber to encode only one of the -// two contained union fields. -func (hn *hashOrNumber) EncodeRLP(w io.Writer) error { - if hn.Hash == (common.Hash{}) { - return rlp.Encode(w, hn.Number) - } - if hn.Number != 0 { - return fmt.Errorf("both origin hash (%x) and number (%d) provided", hn.Hash, hn.Number) - } - return rlp.Encode(w, hn.Hash) -} - -// DecodeRLP is a specialized decoder for hashOrNumber to decode the contents -// into either a block hash or a block number. -func (hn *hashOrNumber) DecodeRLP(s *rlp.Stream) error { - _, size, _ := s.Kind() - origin, err := s.Raw() - if err == nil { - switch { - case size == 32: - err = rlp.DecodeBytes(origin, &hn.Hash) - case size <= 8: - err = rlp.DecodeBytes(origin, &hn.Number) - default: - err = fmt.Errorf("invalid input size %d for origin", size) - } - } - return err -} - -// CodeData is the network response packet for a node data retrieval. -type CodeData []struct { - Value []byte -} - -type proofsData [][]rlp.RawValue - -type txStatus struct { - Status core.TxStatus - Lookup *rawdb.TxLookupEntry `rlp:"nil"` - Error string -} diff --git a/les/randselect.go b/les/randselect.go deleted file mode 100644 index 1cc1d3d3e0..0000000000 --- a/les/randselect.go +++ /dev/null @@ -1,170 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package les implements the Light Ethereum Subprotocol. -package les - -import ( - "math/rand" -) - -// wrsItem interface should be implemented by any entries that are to be selected from -// a weightedRandomSelect set. Note that recalculating monotonously decreasing item -// weights on-demand (without constantly calling update) is allowed -type wrsItem interface { - Weight() int64 -} - -// weightedRandomSelect is capable of weighted random selection from a set of items -type weightedRandomSelect struct { - root *wrsNode - idx map[wrsItem]int -} - -// newWeightedRandomSelect returns a new weightedRandomSelect structure -func newWeightedRandomSelect() *weightedRandomSelect { - return &weightedRandomSelect{root: &wrsNode{maxItems: wrsBranches}, idx: make(map[wrsItem]int)} -} - -// update updates an item's weight, adds it if it was non-existent or removes it if -// the new weight is zero. Note that explicitly updating decreasing weights is not necessary. -func (w *weightedRandomSelect) update(item wrsItem) { - w.setWeight(item, item.Weight()) -} - -// remove removes an item from the set -func (w *weightedRandomSelect) remove(item wrsItem) { - w.setWeight(item, 0) -} - -// setWeight sets an item's weight to a specific value (removes it if zero) -func (w *weightedRandomSelect) setWeight(item wrsItem, weight int64) { - idx, ok := w.idx[item] - if ok { - w.root.setWeight(idx, weight) - if weight == 0 { - delete(w.idx, item) - } - } else { - if weight != 0 { - if w.root.itemCnt == w.root.maxItems { - // add a new level - newRoot := &wrsNode{sumWeight: w.root.sumWeight, itemCnt: w.root.itemCnt, level: w.root.level + 1, maxItems: w.root.maxItems * wrsBranches} - newRoot.items[0] = w.root - newRoot.weights[0] = w.root.sumWeight - w.root = newRoot - } - w.idx[item] = w.root.insert(item, weight) - } - } -} - -// choose randomly selects an item from the set, with a chance proportional to its -// current weight. If the weight of the chosen element has been decreased since the -// last stored value, returns it with a newWeight/oldWeight chance, otherwise just -// updates its weight and selects another one -func (w *weightedRandomSelect) choose() wrsItem { - for { - if w.root.sumWeight == 0 { - return nil - } - val := rand.Int63n(w.root.sumWeight) - choice, lastWeight := w.root.choose(val) - weight := choice.Weight() - if weight != lastWeight { - w.setWeight(choice, weight) - } - if weight >= lastWeight || rand.Int63n(lastWeight) < weight { - return choice - } - } -} - -const wrsBranches = 8 // max number of branches in the wrsNode tree - -// wrsNode is a node of a tree structure that can store wrsItems or further wrsNodes. -type wrsNode struct { - items [wrsBranches]interface{} - weights [wrsBranches]int64 - sumWeight int64 - level, itemCnt, maxItems int -} - -// insert recursively inserts a new item to the tree and returns the item index -func (n *wrsNode) insert(item wrsItem, weight int64) int { - branch := 0 - for n.items[branch] != nil && (n.level == 0 || n.items[branch].(*wrsNode).itemCnt == n.items[branch].(*wrsNode).maxItems) { - branch++ - if branch == wrsBranches { - panic(nil) - } - } - n.itemCnt++ - n.sumWeight += weight - n.weights[branch] += weight - if n.level == 0 { - n.items[branch] = item - return branch - } - var subNode *wrsNode - if n.items[branch] == nil { - subNode = &wrsNode{maxItems: n.maxItems / wrsBranches, level: n.level - 1} - n.items[branch] = subNode - } else { - subNode = n.items[branch].(*wrsNode) - } - subIdx := subNode.insert(item, weight) - return subNode.maxItems*branch + subIdx -} - -// setWeight updates the weight of a certain item (which should exist) and returns -// the change of the last weight value stored in the tree -func (n *wrsNode) setWeight(idx int, weight int64) int64 { - if n.level == 0 { - oldWeight := n.weights[idx] - n.weights[idx] = weight - diff := weight - oldWeight - n.sumWeight += diff - if weight == 0 { - n.items[idx] = nil - n.itemCnt-- - } - return diff - } - branchItems := n.maxItems / wrsBranches - branch := idx / branchItems - diff := n.items[branch].(*wrsNode).setWeight(idx-branch*branchItems, weight) - n.weights[branch] += diff - n.sumWeight += diff - if weight == 0 { - n.itemCnt-- - } - return diff -} - -// choose recursively selects an item from the tree and returns it along with its weight -func (n *wrsNode) choose(val int64) (wrsItem, int64) { - for i, w := range n.weights { - if val < w { - if n.level == 0 { - return n.items[i].(wrsItem), n.weights[i] - } - return n.items[i].(*wrsNode).choose(val) - } - val -= w - } - panic(nil) -} diff --git a/les/randselect_test.go b/les/randselect_test.go deleted file mode 100644 index 9ae7726ddd..0000000000 --- a/les/randselect_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package les - -import ( - "math/rand" - "testing" -) - -type testWrsItem struct { - idx int - widx *int -} - -func (t *testWrsItem) Weight() int64 { - w := *t.widx - if w == -1 || w == t.idx { - return int64(t.idx + 1) - } - return 0 -} - -func TestWeightedRandomSelect(t *testing.T) { - testFn := func(cnt int) { - s := newWeightedRandomSelect() - w := -1 - list := make([]testWrsItem, cnt) - for i := range list { - list[i] = testWrsItem{idx: i, widx: &w} - s.update(&list[i]) - } - w = rand.Intn(cnt) - c := s.choose() - if c == nil { - t.Errorf("expected item, got nil") - } else { - if c.(*testWrsItem).idx != w { - t.Errorf("expected another item") - } - } - w = -2 - if s.choose() != nil { - t.Errorf("expected nil, got item") - } - } - testFn(1) - testFn(10) - testFn(100) - testFn(1000) - testFn(10000) - testFn(100000) - testFn(1000000) -} diff --git a/les/request_test.go b/les/request_test.go deleted file mode 100644 index ba2f603d8b..0000000000 --- a/les/request_test.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package les - -import ( - "context" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/light" -) - -var testBankSecureTrieKey = secAddr(testBankAddress) - -func secAddr(addr common.Address) []byte { - return crypto.Keccak256(addr[:]) -} - -type accessTestFn func(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest - -func TestBlockAccessLes1(t *testing.T) { testAccess(t, 1, tfBlockAccess) } - -func TestBlockAccessLes2(t *testing.T) { testAccess(t, 2, tfBlockAccess) } - -func tfBlockAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { - return &light.BlockRequest{Hash: bhash, Number: number} -} - -func TestReceiptsAccessLes1(t *testing.T) { testAccess(t, 1, tfReceiptsAccess) } - -func TestReceiptsAccessLes2(t *testing.T) { testAccess(t, 2, tfReceiptsAccess) } - -func tfReceiptsAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { - return &light.ReceiptsRequest{Hash: bhash, Number: number} -} - -func TestTrieEntryAccessLes1(t *testing.T) { testAccess(t, 1, tfTrieEntryAccess) } - -func TestTrieEntryAccessLes2(t *testing.T) { testAccess(t, 2, tfTrieEntryAccess) } - -func tfTrieEntryAccess(db ethdb.Database, bhash common.Hash, number uint64) light.OdrRequest { - if number := rawdb.ReadHeaderNumber(db, bhash); number != nil { - return &light.TrieRequest{Id: light.StateTrieID(rawdb.ReadHeader(db, bhash, *number)), Key: testBankSecureTrieKey} - } - return nil -} - -func TestCodeAccessLes1(t *testing.T) { testAccess(t, 1, tfCodeAccess) } - -func TestCodeAccessLes2(t *testing.T) { testAccess(t, 2, tfCodeAccess) } - -func tfCodeAccess(db ethdb.Database, bhash common.Hash, num uint64) light.OdrRequest { - number := rawdb.ReadHeaderNumber(db, bhash) - if number != nil { - return nil - } - header := rawdb.ReadHeader(db, bhash, *number) - if header.Number.Uint64() < testContractDeployed { - return nil - } - sti := light.StateTrieID(header) - ci := light.StorageTrieID(sti, crypto.Keccak256Hash(testContractAddr[:]), common.Hash{}) - return &light.CodeRequest{Id: ci, Hash: crypto.Keccak256Hash(testContractCodeDeployed)} -} - -func testAccess(t *testing.T, protocol int, fn accessTestFn) { - // Assemble the test environment - peers := newPeerSet() - dist := newRequestDistributor(peers, make(chan struct{})) - rm := newRetrieveManager(peers, dist, nil) - db := ethdb.NewMemDatabase() - ldb := ethdb.NewMemDatabase() - odr := NewLesOdr(ldb, light.NewChtIndexer(db, true), light.NewBloomTrieIndexer(db, true), eth.NewBloomIndexer(db, light.BloomTrieFrequency), rm) - - pm := newTestProtocolManagerMust(t, false, 4, testChainGen, nil, nil, db) - lpm := newTestProtocolManagerMust(t, true, 0, nil, peers, odr, ldb) - _, err1, lpeer, err2 := newTestPeerPair("peer", protocol, pm, lpm) - select { - case <-time.After(time.Millisecond * 100): - case err := <-err1: - t.Fatalf("peer 1 handshake error: %v", err) - case err := <-err2: - t.Fatalf("peer 1 handshake error: %v", err) - } - - lpm.synchronise(lpeer) - - test := func(expFail uint64) { - for i := uint64(0); i <= pm.blockchain.CurrentHeader().Number.Uint64(); i++ { - bhash := rawdb.ReadCanonicalHash(db, i) - if req := fn(ldb, bhash, i); req != nil { - ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) - defer cancel() - - err := odr.Retrieve(ctx, req) - got := err == nil - exp := i < expFail - if exp && !got { - t.Errorf("object retrieval failed") - } - if !exp && got { - t.Errorf("unexpected object retrieval success") - } - } - } - } - - // temporarily remove peer to test odr fails - peers.Unregister(lpeer.id) - time.Sleep(time.Millisecond * 10) // ensure that all peerSetNotify callbacks are executed - // expect retrievals to fail (except genesis block) without a les peer - test(0) - - peers.Register(lpeer) - time.Sleep(time.Millisecond * 10) // ensure that all peerSetNotify callbacks are executed - lpeer.lock.Lock() - lpeer.hasBlock = func(common.Hash, uint64) bool { return true } - lpeer.lock.Unlock() - // expect all retrievals to pass - test(5) -} diff --git a/les/retrieve.go b/les/retrieve.go deleted file mode 100644 index a9037a38ef..0000000000 --- a/les/retrieve.go +++ /dev/null @@ -1,402 +0,0 @@ -// 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 . - -// Package light implements on-demand retrieval capable state and chain objects -// for the Ethereum Light Client. -package les - -import ( - "context" - "crypto/rand" - "encoding/binary" - "fmt" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common/mclock" -) - -var ( - retryQueue = time.Millisecond * 100 - softRequestTimeout = time.Millisecond * 500 - hardRequestTimeout = time.Second * 10 -) - -// retrieveManager is a layer on top of requestDistributor which takes care of -// matching replies by request ID and handles timeouts and resends if necessary. -type retrieveManager struct { - dist *requestDistributor - peers *peerSet - serverPool peerSelector - - lock sync.RWMutex - sentReqs map[uint64]*sentReq -} - -// validatorFunc is a function that processes a reply message -type validatorFunc func(distPeer, *Msg) error - -// peerSelector receives feedback info about response times and timeouts -type peerSelector interface { - adjustResponseTime(*poolEntry, time.Duration, bool) -} - -// sentReq represents a request sent and tracked by retrieveManager -type sentReq struct { - rm *retrieveManager - req *distReq - id uint64 - validate validatorFunc - - eventsCh chan reqPeerEvent - stopCh chan struct{} - stopped bool - err error - - lock sync.RWMutex // protect access to sentTo map - sentTo map[distPeer]sentReqToPeer - - lastReqQueued bool // last request has been queued but not sent - lastReqSentTo distPeer // if not nil then last request has been sent to given peer but not timed out - reqSrtoCount int // number of requests that reached soft (but not hard) timeout -} - -// sentReqToPeer notifies the request-from-peer goroutine (tryRequest) about a response -// delivered by the given peer. Only one delivery is allowed per request per peer, -// after which delivered is set to true, the validity of the response is sent on the -// valid channel and no more responses are accepted. -type sentReqToPeer struct { - delivered bool - valid chan bool -} - -// reqPeerEvent is sent by the request-from-peer goroutine (tryRequest) to the -// request state machine (retrieveLoop) through the eventsCh channel. -type reqPeerEvent struct { - event int - peer distPeer -} - -const ( - rpSent = iota // if peer == nil, not sent (no suitable peers) - rpSoftTimeout - rpHardTimeout - rpDeliveredValid - rpDeliveredInvalid -) - -// newRetrieveManager creates the retrieve manager -func newRetrieveManager(peers *peerSet, dist *requestDistributor, serverPool peerSelector) *retrieveManager { - return &retrieveManager{ - peers: peers, - dist: dist, - serverPool: serverPool, - sentReqs: make(map[uint64]*sentReq), - } -} - -// retrieve sends a request (to multiple peers if necessary) and waits for an answer -// that is delivered through the deliver function and successfully validated by the -// validator callback. It returns when a valid answer is delivered or the context is -// cancelled. -func (rm *retrieveManager) retrieve(ctx context.Context, reqID uint64, req *distReq, val validatorFunc, shutdown chan struct{}) error { - sentReq := rm.sendReq(reqID, req, val) - select { - case <-sentReq.stopCh: - case <-ctx.Done(): - sentReq.stop(ctx.Err()) - case <-shutdown: - sentReq.stop(fmt.Errorf("Client is shutting down")) - } - return sentReq.getError() -} - -// sendReq starts a process that keeps trying to retrieve a valid answer for a -// request from any suitable peers until stopped or succeeded. -func (rm *retrieveManager) sendReq(reqID uint64, req *distReq, val validatorFunc) *sentReq { - r := &sentReq{ - rm: rm, - req: req, - id: reqID, - sentTo: make(map[distPeer]sentReqToPeer), - stopCh: make(chan struct{}), - eventsCh: make(chan reqPeerEvent, 10), - validate: val, - } - - canSend := req.canSend - req.canSend = func(p distPeer) bool { - // add an extra check to canSend: the request has not been sent to the same peer before - r.lock.RLock() - _, sent := r.sentTo[p] - r.lock.RUnlock() - return !sent && canSend(p) - } - - request := req.request - req.request = func(p distPeer) func() { - // before actually sending the request, put an entry into the sentTo map - r.lock.Lock() - r.sentTo[p] = sentReqToPeer{false, make(chan bool, 1)} - r.lock.Unlock() - return request(p) - } - rm.lock.Lock() - rm.sentReqs[reqID] = r - rm.lock.Unlock() - - go r.retrieveLoop() - return r -} - -// deliver is called by the LES protocol manager to deliver reply messages to waiting requests -func (rm *retrieveManager) deliver(peer distPeer, msg *Msg) error { - rm.lock.RLock() - req, ok := rm.sentReqs[msg.ReqID] - rm.lock.RUnlock() - - if ok { - return req.deliver(peer, msg) - } - return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID) -} - -// reqStateFn represents a state of the retrieve loop state machine -type reqStateFn func() reqStateFn - -// retrieveLoop is the retrieval state machine event loop -func (r *sentReq) retrieveLoop() { - go r.tryRequest() - r.lastReqQueued = true - state := r.stateRequesting - - for state != nil { - state = state() - } - - r.rm.lock.Lock() - delete(r.rm.sentReqs, r.id) - r.rm.lock.Unlock() -} - -// stateRequesting: a request has been queued or sent recently; when it reaches soft timeout, -// a new request is sent to a new peer -func (r *sentReq) stateRequesting() reqStateFn { - select { - case ev := <-r.eventsCh: - r.update(ev) - switch ev.event { - case rpSent: - if ev.peer == nil { - // request send failed, no more suitable peers - if r.waiting() { - // we are already waiting for sent requests which may succeed so keep waiting - return r.stateNoMorePeers - } - // nothing to wait for, no more peers to ask, return with error - r.stop(ErrNoPeers) - // no need to go to stopped state because waiting() already returned false - return nil - } - case rpSoftTimeout: - // last request timed out, try asking a new peer - go r.tryRequest() - r.lastReqQueued = true - return r.stateRequesting - case rpDeliveredValid: - r.stop(nil) - return r.stateStopped - } - return r.stateRequesting - case <-r.stopCh: - return r.stateStopped - } -} - -// stateNoMorePeers: could not send more requests because no suitable peers are available. -// Peers may become suitable for a certain request later or new peers may appear so we -// keep trying. -func (r *sentReq) stateNoMorePeers() reqStateFn { - select { - case <-time.After(retryQueue): - go r.tryRequest() - r.lastReqQueued = true - return r.stateRequesting - case ev := <-r.eventsCh: - r.update(ev) - if ev.event == rpDeliveredValid { - r.stop(nil) - return r.stateStopped - } - return r.stateNoMorePeers - case <-r.stopCh: - return r.stateStopped - } -} - -// stateStopped: request succeeded or cancelled, just waiting for some peers -// to either answer or time out hard -func (r *sentReq) stateStopped() reqStateFn { - for r.waiting() { - r.update(<-r.eventsCh) - } - return nil -} - -// update updates the queued/sent flags and timed out peers counter according to the event -func (r *sentReq) update(ev reqPeerEvent) { - switch ev.event { - case rpSent: - r.lastReqQueued = false - r.lastReqSentTo = ev.peer - case rpSoftTimeout: - r.lastReqSentTo = nil - r.reqSrtoCount++ - case rpHardTimeout: - r.reqSrtoCount-- - case rpDeliveredValid, rpDeliveredInvalid: - if ev.peer == r.lastReqSentTo { - r.lastReqSentTo = nil - } else { - r.reqSrtoCount-- - } - } -} - -// waiting returns true if the retrieval mechanism is waiting for an answer from -// any peer -func (r *sentReq) waiting() bool { - return r.lastReqQueued || r.lastReqSentTo != nil || r.reqSrtoCount > 0 -} - -// tryRequest tries to send the request to a new peer and waits for it to either -// succeed or time out if it has been sent. It also sends the appropriate reqPeerEvent -// messages to the request's event channel. -func (r *sentReq) tryRequest() { - sent := r.rm.dist.queue(r.req) - var p distPeer - select { - case p = <-sent: - case <-r.stopCh: - if r.rm.dist.cancel(r.req) { - p = nil - } else { - p = <-sent - } - } - - r.eventsCh <- reqPeerEvent{rpSent, p} - if p == nil { - return - } - - reqSent := mclock.Now() - srto, hrto := false, false - - r.lock.RLock() - s, ok := r.sentTo[p] - r.lock.RUnlock() - if !ok { - panic(nil) - } - - defer func() { - // send feedback to server pool and remove peer if hard timeout happened - pp, ok := p.(*peer) - if ok && r.rm.serverPool != nil { - respTime := time.Duration(mclock.Now() - reqSent) - r.rm.serverPool.adjustResponseTime(pp.poolEntry, respTime, srto) - } - if hrto { - pp.Log().Debug("Request timed out hard") - if r.rm.peers != nil { - r.rm.peers.Unregister(pp.id) - } - } - - r.lock.Lock() - delete(r.sentTo, p) - r.lock.Unlock() - }() - - select { - case ok := <-s.valid: - if ok { - r.eventsCh <- reqPeerEvent{rpDeliveredValid, p} - } else { - r.eventsCh <- reqPeerEvent{rpDeliveredInvalid, p} - } - return - case <-time.After(softRequestTimeout): - srto = true - r.eventsCh <- reqPeerEvent{rpSoftTimeout, p} - } - - select { - case ok := <-s.valid: - if ok { - r.eventsCh <- reqPeerEvent{rpDeliveredValid, p} - } else { - r.eventsCh <- reqPeerEvent{rpDeliveredInvalid, p} - } - case <-time.After(hardRequestTimeout): - hrto = true - r.eventsCh <- reqPeerEvent{rpHardTimeout, p} - } -} - -// deliver a reply belonging to this request -func (r *sentReq) deliver(peer distPeer, msg *Msg) error { - r.lock.Lock() - defer r.lock.Unlock() - - s, ok := r.sentTo[peer] - if !ok || s.delivered { - return errResp(ErrUnexpectedResponse, "reqID = %v", msg.ReqID) - } - valid := r.validate(peer, msg) == nil - r.sentTo[peer] = sentReqToPeer{true, s.valid} - s.valid <- valid - if !valid { - return errResp(ErrInvalidResponse, "reqID = %v", msg.ReqID) - } - return nil -} - -// stop stops the retrieval process and sets an error code that will be returned -// by getError -func (r *sentReq) stop(err error) { - r.lock.Lock() - if !r.stopped { - r.stopped = true - r.err = err - close(r.stopCh) - } - r.lock.Unlock() -} - -// getError returns any retrieval error (either internally generated or set by the -// stop function) after stopCh has been closed -func (r *sentReq) getError() error { - return r.err -} - -// genReqID generates a new random request ID -func genReqID() uint64 { - var rnd [8]byte - rand.Read(rnd[:]) - return binary.BigEndian.Uint64(rnd[:]) -} diff --git a/les/server.go b/les/server.go deleted file mode 100644 index fca6124c9c..0000000000 --- a/les/server.go +++ /dev/null @@ -1,383 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package les implements the Light Ethereum Subprotocol. -package les - -import ( - "crypto/ecdsa" - "encoding/binary" - "math" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/les/flowcontrol" - "github.com/ethereum/go-ethereum/light" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discv5" - "github.com/ethereum/go-ethereum/rlp" -) - -type LesServer struct { - config *eth.Config - protocolManager *ProtocolManager - fcManager *flowcontrol.ClientManager // nil if our node is client only - fcCostStats *requestCostStats - defParams *flowcontrol.ServerParams - lesTopics []discv5.Topic - privateKey *ecdsa.PrivateKey - quitSync chan struct{} - - chtIndexer, bloomTrieIndexer *core.ChainIndexer -} - -func NewLesServer(eth *eth.Ethereum, config *eth.Config) (*LesServer, error) { - quitSync := make(chan struct{}) - pm, err := NewProtocolManager(eth.BlockChain().Config(), false, ServerProtocolVersions, config.NetworkId, eth.EventMux(), eth.Engine(), newPeerSet(), eth.BlockChain(), eth.TxPool(), eth.ChainDb(), nil, nil, nil, quitSync, new(sync.WaitGroup)) - if err != nil { - return nil, err - } - - lesTopics := make([]discv5.Topic, len(AdvertiseProtocolVersions)) - for i, pv := range AdvertiseProtocolVersions { - lesTopics[i] = lesTopic(eth.BlockChain().Genesis().Hash(), pv) - } - - srv := &LesServer{ - config: config, - protocolManager: pm, - quitSync: quitSync, - lesTopics: lesTopics, - chtIndexer: light.NewChtIndexer(eth.ChainDb(), false), - bloomTrieIndexer: light.NewBloomTrieIndexer(eth.ChainDb(), false), - } - logger := log.New() - - chtV1SectionCount, _, _ := srv.chtIndexer.Sections() // indexer still uses LES/1 4k section size for backwards server compatibility - chtV2SectionCount := chtV1SectionCount / (light.CHTFrequencyClient / light.CHTFrequencyServer) - if chtV2SectionCount != 0 { - // convert to LES/2 section - chtLastSection := chtV2SectionCount - 1 - // convert last LES/2 section index back to LES/1 index for chtIndexer.SectionHead - chtLastSectionV1 := (chtLastSection+1)*(light.CHTFrequencyClient/light.CHTFrequencyServer) - 1 - chtSectionHead := srv.chtIndexer.SectionHead(chtLastSectionV1) - chtRoot := light.GetChtV2Root(pm.chainDb, chtLastSection, chtSectionHead) - logger.Info("Loaded CHT", "section", chtLastSection, "head", chtSectionHead, "root", chtRoot) - } - bloomTrieSectionCount, _, _ := srv.bloomTrieIndexer.Sections() - if bloomTrieSectionCount != 0 { - bloomTrieLastSection := bloomTrieSectionCount - 1 - bloomTrieSectionHead := srv.bloomTrieIndexer.SectionHead(bloomTrieLastSection) - bloomTrieRoot := light.GetBloomTrieRoot(pm.chainDb, bloomTrieLastSection, bloomTrieSectionHead) - logger.Info("Loaded bloom trie", "section", bloomTrieLastSection, "head", bloomTrieSectionHead, "root", bloomTrieRoot) - } - - srv.chtIndexer.Start(eth.BlockChain()) - pm.server = srv - - srv.defParams = &flowcontrol.ServerParams{ - BufLimit: 300000000, - MinRecharge: 50000, - } - srv.fcManager = flowcontrol.NewClientManager(uint64(config.LightServ), 10, 1000000000) - srv.fcCostStats = newCostStats(eth.ChainDb()) - return srv, nil -} - -func (s *LesServer) Protocols() []p2p.Protocol { - return s.protocolManager.SubProtocols -} - -// Start starts the LES server -func (s *LesServer) Start(srvr *p2p.Server) { - s.protocolManager.Start(s.config.LightPeers) - if srvr.DiscV5 != nil { - for _, topic := range s.lesTopics { - topic := topic - go func() { - logger := log.New("topic", topic) - logger.Info("Starting topic registration") - defer logger.Info("Terminated topic registration") - - srvr.DiscV5.RegisterTopic(topic, s.quitSync) - }() - } - } - s.privateKey = srvr.PrivateKey - s.protocolManager.blockLoop() -} - -func (s *LesServer) SetBloomBitsIndexer(bloomIndexer *core.ChainIndexer) { - bloomIndexer.AddChildIndexer(s.bloomTrieIndexer) -} - -// Stop stops the LES service -func (s *LesServer) Stop() { - s.chtIndexer.Close() - // bloom trie indexer is closed by parent bloombits indexer - s.fcCostStats.store() - s.fcManager.Stop() - go func() { - <-s.protocolManager.noMorePeers - }() - s.protocolManager.Stop() -} - -type requestCosts struct { - baseCost, reqCost uint64 -} - -type requestCostTable map[uint64]*requestCosts - -type RequestCostList []struct { - MsgCode, BaseCost, ReqCost uint64 -} - -func (list RequestCostList) decode() requestCostTable { - table := make(requestCostTable) - for _, e := range list { - table[e.MsgCode] = &requestCosts{ - baseCost: e.BaseCost, - reqCost: e.ReqCost, - } - } - return table -} - -type linReg struct { - sumX, sumY, sumXX, sumXY float64 - cnt uint64 -} - -const linRegMaxCnt = 100000 - -func (l *linReg) add(x, y float64) { - if l.cnt >= linRegMaxCnt { - sub := float64(l.cnt+1-linRegMaxCnt) / linRegMaxCnt - l.sumX -= l.sumX * sub - l.sumY -= l.sumY * sub - l.sumXX -= l.sumXX * sub - l.sumXY -= l.sumXY * sub - l.cnt = linRegMaxCnt - 1 - } - l.cnt++ - l.sumX += x - l.sumY += y - l.sumXX += x * x - l.sumXY += x * y -} - -func (l *linReg) calc() (b, m float64) { - if l.cnt == 0 { - return 0, 0 - } - cnt := float64(l.cnt) - d := cnt*l.sumXX - l.sumX*l.sumX - if d < 0.001 { - return l.sumY / cnt, 0 - } - m = (cnt*l.sumXY - l.sumX*l.sumY) / d - b = (l.sumY / cnt) - (m * l.sumX / cnt) - return b, m -} - -func (l *linReg) toBytes() []byte { - var arr [40]byte - binary.BigEndian.PutUint64(arr[0:8], math.Float64bits(l.sumX)) - binary.BigEndian.PutUint64(arr[8:16], math.Float64bits(l.sumY)) - binary.BigEndian.PutUint64(arr[16:24], math.Float64bits(l.sumXX)) - binary.BigEndian.PutUint64(arr[24:32], math.Float64bits(l.sumXY)) - binary.BigEndian.PutUint64(arr[32:40], l.cnt) - return arr[:] -} - -func linRegFromBytes(data []byte) *linReg { - if len(data) != 40 { - return nil - } - l := &linReg{} - l.sumX = math.Float64frombits(binary.BigEndian.Uint64(data[0:8])) - l.sumY = math.Float64frombits(binary.BigEndian.Uint64(data[8:16])) - l.sumXX = math.Float64frombits(binary.BigEndian.Uint64(data[16:24])) - l.sumXY = math.Float64frombits(binary.BigEndian.Uint64(data[24:32])) - l.cnt = binary.BigEndian.Uint64(data[32:40]) - return l -} - -type requestCostStats struct { - lock sync.RWMutex - db ethdb.Database - stats map[uint64]*linReg -} - -type requestCostStatsRlp []struct { - MsgCode uint64 - Data []byte -} - -var rcStatsKey = []byte("_requestCostStats") - -func newCostStats(db ethdb.Database) *requestCostStats { - stats := make(map[uint64]*linReg) - for _, code := range reqList { - stats[code] = &linReg{cnt: 100} - } - - if db != nil { - data, err := db.Get(rcStatsKey) - var statsRlp requestCostStatsRlp - if err == nil { - err = rlp.DecodeBytes(data, &statsRlp) - } - if err == nil { - for _, r := range statsRlp { - if stats[r.MsgCode] != nil { - if l := linRegFromBytes(r.Data); l != nil { - stats[r.MsgCode] = l - } - } - } - } - } - - return &requestCostStats{ - db: db, - stats: stats, - } -} - -func (s *requestCostStats) store() { - s.lock.Lock() - defer s.lock.Unlock() - - statsRlp := make(requestCostStatsRlp, len(reqList)) - for i, code := range reqList { - statsRlp[i].MsgCode = code - statsRlp[i].Data = s.stats[code].toBytes() - } - - if data, err := rlp.EncodeToBytes(statsRlp); err == nil { - s.db.Put(rcStatsKey, data) - } -} - -func (s *requestCostStats) getCurrentList() RequestCostList { - s.lock.Lock() - defer s.lock.Unlock() - - list := make(RequestCostList, len(reqList)) - //fmt.Println("RequestCostList") - for idx, code := range reqList { - b, m := s.stats[code].calc() - //fmt.Println(code, s.stats[code].cnt, b/1000000, m/1000000) - if m < 0 { - b += m - m = 0 - } - if b < 0 { - b = 0 - } - - list[idx].MsgCode = code - list[idx].BaseCost = uint64(b * 2) - list[idx].ReqCost = uint64(m * 2) - } - return list -} - -func (s *requestCostStats) update(msgCode, reqCnt, cost uint64) { - s.lock.Lock() - defer s.lock.Unlock() - - c, ok := s.stats[msgCode] - if !ok || reqCnt == 0 { - return - } - c.add(float64(reqCnt), float64(cost)) -} - -func (pm *ProtocolManager) blockLoop() { - pm.wg.Add(1) - headCh := make(chan core.ChainHeadEvent, 10) - headSub := pm.blockchain.SubscribeChainHeadEvent(headCh) - go func() { - var lastHead *types.Header - lastBroadcastTd := common.Big0 - for { - select { - case ev := <-headCh: - peers := pm.peers.AllPeers() - if len(peers) > 0 { - header := ev.Block.Header() - hash := header.Hash() - number := header.Number.Uint64() - td := rawdb.ReadTd(pm.chainDb, hash, number) - if td != nil && td.Cmp(lastBroadcastTd) > 0 { - var reorg uint64 - if lastHead != nil { - reorg = lastHead.Number.Uint64() - rawdb.FindCommonAncestor(pm.chainDb, header, lastHead).Number.Uint64() - } - lastHead = header - lastBroadcastTd = td - - log.Debug("Announcing block to peers", "number", number, "hash", hash, "td", td, "reorg", reorg) - - announce := announceData{Hash: hash, Number: number, Td: td, ReorgDepth: reorg} - var ( - signed bool - signedAnnounce announceData - ) - - for _, p := range peers { - switch p.announceType { - - case announceTypeSimple: - select { - case p.announceChn <- announce: - default: - pm.removePeer(p.id) - } - - case announceTypeSigned: - if !signed { - signedAnnounce = announce - signedAnnounce.sign(pm.server.privateKey) - signed = true - } - - select { - case p.announceChn <- signedAnnounce: - default: - pm.removePeer(p.id) - } - } - } - } - } - case <-pm.quitSync: - headSub.Unsubscribe() - pm.wg.Done() - return - } - } - }() -} diff --git a/les/serverpool.go b/les/serverpool.go deleted file mode 100644 index 1a4c752293..0000000000 --- a/les/serverpool.go +++ /dev/null @@ -1,816 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package les implements the Light Ethereum Subprotocol. -package les - -import ( - "fmt" - "io" - "math" - "math/rand" - "net" - "strconv" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common/mclock" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/p2p/discv5" - "github.com/ethereum/go-ethereum/rlp" -) - -const ( - // After a connection has been ended or timed out, there is a waiting period - // before it can be selected for connection again. - // waiting period = base delay * (1 + random(1)) - // base delay = shortRetryDelay for the first shortRetryCnt times after a - // successful connection, after that longRetryDelay is applied - shortRetryCnt = 5 - shortRetryDelay = time.Second * 5 - longRetryDelay = time.Minute * 10 - // maxNewEntries is the maximum number of newly discovered (never connected) nodes. - // If the limit is reached, the least recently discovered one is thrown out. - maxNewEntries = 1000 - // maxKnownEntries is the maximum number of known (already connected) nodes. - // If the limit is reached, the least recently connected one is thrown out. - // (not that unlike new entries, known entries are persistent) - maxKnownEntries = 1000 - // target for simultaneously connected servers - targetServerCount = 5 - // target for servers selected from the known table - // (we leave room for trying new ones if there is any) - targetKnownSelect = 3 - // after dialTimeout, consider the server unavailable and adjust statistics - dialTimeout = time.Second * 30 - // targetConnTime is the minimum expected connection duration before a server - // drops a client without any specific reason - targetConnTime = time.Minute * 10 - // new entry selection weight calculation based on most recent discovery time: - // unity until discoverExpireStart, then exponential decay with discoverExpireConst - discoverExpireStart = time.Minute * 20 - discoverExpireConst = time.Minute * 20 - // known entry selection weight is dropped by a factor of exp(-failDropLn) after - // each unsuccessful connection (restored after a successful one) - failDropLn = 0.1 - // known node connection success and quality statistics have a long term average - // and a short term value which is adjusted exponentially with a factor of - // pstatRecentAdjust with each dial/connection and also returned exponentially - // to the average with the time constant pstatReturnToMeanTC - pstatReturnToMeanTC = time.Hour - // node address selection weight is dropped by a factor of exp(-addrFailDropLn) after - // each unsuccessful connection (restored after a successful one) - addrFailDropLn = math.Ln2 - // responseScoreTC and delayScoreTC are exponential decay time constants for - // calculating selection chances from response times and block delay times - responseScoreTC = time.Millisecond * 100 - delayScoreTC = time.Second * 5 - timeoutPow = 10 - // initStatsWeight is used to initialize previously unknown peers with good - // statistics to give a chance to prove themselves - initStatsWeight = 1 -) - -// connReq represents a request for peer connection. -type connReq struct { - p *peer - ip net.IP - port uint16 - result chan *poolEntry -} - -// disconnReq represents a request for peer disconnection. -type disconnReq struct { - entry *poolEntry - stopped bool - done chan struct{} -} - -// registerReq represents a request for peer registration. -type registerReq struct { - entry *poolEntry - done chan struct{} -} - -// serverPool implements a pool for storing and selecting newly discovered and already -// known light server nodes. It received discovered nodes, stores statistics about -// known nodes and takes care of always having enough good quality servers connected. -type serverPool struct { - db ethdb.Database - dbKey []byte - server *p2p.Server - quit chan struct{} - wg *sync.WaitGroup - connWg sync.WaitGroup - - topic discv5.Topic - - discSetPeriod chan time.Duration - discNodes chan *discv5.Node - discLookups chan bool - - entries map[discover.NodeID]*poolEntry - timeout, enableRetry chan *poolEntry - adjustStats chan poolStatAdjust - - connCh chan *connReq - disconnCh chan *disconnReq - registerCh chan *registerReq - - knownQueue, newQueue poolEntryQueue - knownSelect, newSelect *weightedRandomSelect - knownSelected, newSelected int - fastDiscover bool -} - -// newServerPool creates a new serverPool instance -func newServerPool(db ethdb.Database, quit chan struct{}, wg *sync.WaitGroup) *serverPool { - pool := &serverPool{ - db: db, - quit: quit, - wg: wg, - entries: make(map[discover.NodeID]*poolEntry), - timeout: make(chan *poolEntry, 1), - adjustStats: make(chan poolStatAdjust, 100), - enableRetry: make(chan *poolEntry, 1), - connCh: make(chan *connReq), - disconnCh: make(chan *disconnReq), - registerCh: make(chan *registerReq), - knownSelect: newWeightedRandomSelect(), - newSelect: newWeightedRandomSelect(), - fastDiscover: true, - } - pool.knownQueue = newPoolEntryQueue(maxKnownEntries, pool.removeEntry) - pool.newQueue = newPoolEntryQueue(maxNewEntries, pool.removeEntry) - return pool -} - -func (pool *serverPool) start(server *p2p.Server, topic discv5.Topic) { - pool.server = server - pool.topic = topic - pool.dbKey = append([]byte("serverPool/"), []byte(topic)...) - pool.wg.Add(1) - pool.loadNodes() - - if pool.server.DiscV5 != nil { - pool.discSetPeriod = make(chan time.Duration, 1) - pool.discNodes = make(chan *discv5.Node, 100) - pool.discLookups = make(chan bool, 100) - go pool.server.DiscV5.SearchTopic(pool.topic, pool.discSetPeriod, pool.discNodes, pool.discLookups) - } - pool.checkDial() - go pool.eventLoop() -} - -// connect should be called upon any incoming connection. If the connection has been -// dialed by the server pool recently, the appropriate pool entry is returned. -// Otherwise, the connection should be rejected. -// Note that whenever a connection has been accepted and a pool entry has been returned, -// disconnect should also always be called. -func (pool *serverPool) connect(p *peer, ip net.IP, port uint16) *poolEntry { - log.Debug("Connect new entry", "enode", p.id) - req := &connReq{p: p, ip: ip, port: port, result: make(chan *poolEntry, 1)} - select { - case pool.connCh <- req: - case <-pool.quit: - return nil - } - return <-req.result -} - -// registered should be called after a successful handshake -func (pool *serverPool) registered(entry *poolEntry) { - log.Debug("Registered new entry", "enode", entry.id) - req := ®isterReq{entry: entry, done: make(chan struct{})} - select { - case pool.registerCh <- req: - case <-pool.quit: - return - } - <-req.done -} - -// disconnect should be called when ending a connection. Service quality statistics -// can be updated optionally (not updated if no registration happened, in this case -// only connection statistics are updated, just like in case of timeout) -func (pool *serverPool) disconnect(entry *poolEntry) { - stopped := false - select { - case <-pool.quit: - stopped = true - default: - } - log.Debug("Disconnected old entry", "enode", entry.id) - req := &disconnReq{entry: entry, stopped: stopped, done: make(chan struct{})} - - // Block until disconnection request is served. - pool.disconnCh <- req - <-req.done -} - -const ( - pseBlockDelay = iota - pseResponseTime - pseResponseTimeout -) - -// poolStatAdjust records are sent to adjust peer block delay/response time statistics -type poolStatAdjust struct { - adjustType int - entry *poolEntry - time time.Duration -} - -// adjustBlockDelay adjusts the block announce delay statistics of a node -func (pool *serverPool) adjustBlockDelay(entry *poolEntry, time time.Duration) { - if entry == nil { - return - } - pool.adjustStats <- poolStatAdjust{pseBlockDelay, entry, time} -} - -// adjustResponseTime adjusts the request response time statistics of a node -func (pool *serverPool) adjustResponseTime(entry *poolEntry, time time.Duration, timeout bool) { - if entry == nil { - return - } - if timeout { - pool.adjustStats <- poolStatAdjust{pseResponseTimeout, entry, time} - } else { - pool.adjustStats <- poolStatAdjust{pseResponseTime, entry, time} - } -} - -// eventLoop handles pool events and mutex locking for all internal functions -func (pool *serverPool) eventLoop() { - lookupCnt := 0 - var convTime mclock.AbsTime - if pool.discSetPeriod != nil { - pool.discSetPeriod <- time.Millisecond * 100 - } - - // disconnect updates service quality statistics depending on the connection time - // and disconnection initiator. - disconnect := func(req *disconnReq, stopped bool) { - // Handle peer disconnection requests. - entry := req.entry - if entry.state == psRegistered { - connAdjust := float64(mclock.Now()-entry.regTime) / float64(targetConnTime) - if connAdjust > 1 { - connAdjust = 1 - } - if stopped { - // disconnect requested by ourselves. - entry.connectStats.add(1, connAdjust) - } else { - // disconnect requested by server side. - entry.connectStats.add(connAdjust, 1) - } - } - entry.state = psNotConnected - - if entry.knownSelected { - pool.knownSelected-- - } else { - pool.newSelected-- - } - pool.setRetryDial(entry) - pool.connWg.Done() - close(req.done) - } - - for { - select { - case entry := <-pool.timeout: - if !entry.removed { - pool.checkDialTimeout(entry) - } - - case entry := <-pool.enableRetry: - if !entry.removed { - entry.delayedRetry = false - pool.updateCheckDial(entry) - } - - case adj := <-pool.adjustStats: - switch adj.adjustType { - case pseBlockDelay: - adj.entry.delayStats.add(float64(adj.time), 1) - case pseResponseTime: - adj.entry.responseStats.add(float64(adj.time), 1) - adj.entry.timeoutStats.add(0, 1) - case pseResponseTimeout: - adj.entry.timeoutStats.add(1, 1) - } - - case node := <-pool.discNodes: - entry := pool.findOrNewNode(discover.NodeID(node.ID), node.IP, node.TCP) - pool.updateCheckDial(entry) - - case conv := <-pool.discLookups: - if conv { - if lookupCnt == 0 { - convTime = mclock.Now() - } - lookupCnt++ - if pool.fastDiscover && (lookupCnt == 50 || time.Duration(mclock.Now()-convTime) > time.Minute) { - pool.fastDiscover = false - if pool.discSetPeriod != nil { - pool.discSetPeriod <- time.Minute - } - } - } - - case req := <-pool.connCh: - // Handle peer connection requests. - entry := pool.entries[req.p.ID()] - if entry == nil { - entry = pool.findOrNewNode(req.p.ID(), req.ip, req.port) - } - if entry.state == psConnected || entry.state == psRegistered { - req.result <- nil - continue - } - pool.connWg.Add(1) - entry.peer = req.p - entry.state = psConnected - addr := &poolEntryAddress{ - ip: req.ip, - port: req.port, - lastSeen: mclock.Now(), - } - entry.lastConnected = addr - entry.addr = make(map[string]*poolEntryAddress) - entry.addr[addr.strKey()] = addr - entry.addrSelect = *newWeightedRandomSelect() - entry.addrSelect.update(addr) - req.result <- entry - - case req := <-pool.registerCh: - // Handle peer registration requests. - entry := req.entry - entry.state = psRegistered - entry.regTime = mclock.Now() - if !entry.known { - pool.newQueue.remove(entry) - entry.known = true - } - pool.knownQueue.setLatest(entry) - entry.shortRetry = shortRetryCnt - close(req.done) - - case req := <-pool.disconnCh: - // Handle peer disconnection requests. - disconnect(req, req.stopped) - - case <-pool.quit: - if pool.discSetPeriod != nil { - close(pool.discSetPeriod) - } - - // Spawn a goroutine to close the disconnCh after all connections are disconnected. - go func() { - pool.connWg.Wait() - close(pool.disconnCh) - }() - - // Handle all remaining disconnection requests before exit. - for req := range pool.disconnCh { - disconnect(req, true) - } - pool.saveNodes() - pool.wg.Done() - return - } - } -} - -func (pool *serverPool) findOrNewNode(id discover.NodeID, ip net.IP, port uint16) *poolEntry { - now := mclock.Now() - entry := pool.entries[id] - if entry == nil { - log.Debug("Discovered new entry", "id", id) - entry = &poolEntry{ - id: id, - addr: make(map[string]*poolEntryAddress), - addrSelect: *newWeightedRandomSelect(), - shortRetry: shortRetryCnt, - } - pool.entries[id] = entry - // initialize previously unknown peers with good statistics to give a chance to prove themselves - entry.connectStats.add(1, initStatsWeight) - entry.delayStats.add(0, initStatsWeight) - entry.responseStats.add(0, initStatsWeight) - entry.timeoutStats.add(0, initStatsWeight) - } - entry.lastDiscovered = now - addr := &poolEntryAddress{ - ip: ip, - port: port, - } - if a, ok := entry.addr[addr.strKey()]; ok { - addr = a - } else { - entry.addr[addr.strKey()] = addr - } - addr.lastSeen = now - entry.addrSelect.update(addr) - if !entry.known { - pool.newQueue.setLatest(entry) - } - return entry -} - -// loadNodes loads known nodes and their statistics from the database -func (pool *serverPool) loadNodes() { - enc, err := pool.db.Get(pool.dbKey) - if err != nil { - return - } - var list []*poolEntry - err = rlp.DecodeBytes(enc, &list) - if err != nil { - log.Debug("Failed to decode node list", "err", err) - return - } - for _, e := range list { - log.Debug("Loaded server stats", "id", e.id, "fails", e.lastConnected.fails, - "conn", fmt.Sprintf("%v/%v", e.connectStats.avg, e.connectStats.weight), - "delay", fmt.Sprintf("%v/%v", time.Duration(e.delayStats.avg), e.delayStats.weight), - "response", fmt.Sprintf("%v/%v", time.Duration(e.responseStats.avg), e.responseStats.weight), - "timeout", fmt.Sprintf("%v/%v", e.timeoutStats.avg, e.timeoutStats.weight)) - pool.entries[e.id] = e - pool.knownQueue.setLatest(e) - pool.knownSelect.update((*knownEntry)(e)) - } -} - -// saveNodes saves known nodes and their statistics into the database. Nodes are -// ordered from least to most recently connected. -func (pool *serverPool) saveNodes() { - list := make([]*poolEntry, len(pool.knownQueue.queue)) - for i := range list { - list[i] = pool.knownQueue.fetchOldest() - } - enc, err := rlp.EncodeToBytes(list) - if err == nil { - pool.db.Put(pool.dbKey, enc) - } -} - -// removeEntry removes a pool entry when the entry count limit is reached. -// Note that it is called by the new/known queues from which the entry has already -// been removed so removing it from the queues is not necessary. -func (pool *serverPool) removeEntry(entry *poolEntry) { - pool.newSelect.remove((*discoveredEntry)(entry)) - pool.knownSelect.remove((*knownEntry)(entry)) - entry.removed = true - delete(pool.entries, entry.id) -} - -// setRetryDial starts the timer which will enable dialing a certain node again -func (pool *serverPool) setRetryDial(entry *poolEntry) { - delay := longRetryDelay - if entry.shortRetry > 0 { - entry.shortRetry-- - delay = shortRetryDelay - } - delay += time.Duration(rand.Int63n(int64(delay) + 1)) - entry.delayedRetry = true - go func() { - select { - case <-pool.quit: - case <-time.After(delay): - select { - case <-pool.quit: - case pool.enableRetry <- entry: - } - } - }() -} - -// updateCheckDial is called when an entry can potentially be dialed again. It updates -// its selection weights and checks if new dials can/should be made. -func (pool *serverPool) updateCheckDial(entry *poolEntry) { - pool.newSelect.update((*discoveredEntry)(entry)) - pool.knownSelect.update((*knownEntry)(entry)) - pool.checkDial() -} - -// checkDial checks if new dials can/should be made. It tries to select servers both -// based on good statistics and recent discovery. -func (pool *serverPool) checkDial() { - fillWithKnownSelects := !pool.fastDiscover - for pool.knownSelected < targetKnownSelect { - entry := pool.knownSelect.choose() - if entry == nil { - fillWithKnownSelects = false - break - } - pool.dial((*poolEntry)(entry.(*knownEntry)), true) - } - for pool.knownSelected+pool.newSelected < targetServerCount { - entry := pool.newSelect.choose() - if entry == nil { - break - } - pool.dial((*poolEntry)(entry.(*discoveredEntry)), false) - } - if fillWithKnownSelects { - // no more newly discovered nodes to select and since fast discover period - // is over, we probably won't find more in the near future so select more - // known entries if possible - for pool.knownSelected < targetServerCount { - entry := pool.knownSelect.choose() - if entry == nil { - break - } - pool.dial((*poolEntry)(entry.(*knownEntry)), true) - } - } -} - -// dial initiates a new connection -func (pool *serverPool) dial(entry *poolEntry, knownSelected bool) { - if pool.server == nil || entry.state != psNotConnected { - return - } - entry.state = psDialed - entry.knownSelected = knownSelected - if knownSelected { - pool.knownSelected++ - } else { - pool.newSelected++ - } - addr := entry.addrSelect.choose().(*poolEntryAddress) - log.Debug("Dialing new peer", "lesaddr", entry.id.String()+"@"+addr.strKey(), "set", len(entry.addr), "known", knownSelected) - entry.dialed = addr - go func() { - pool.server.AddPeer(discover.NewNode(entry.id, addr.ip, addr.port, addr.port)) - select { - case <-pool.quit: - case <-time.After(dialTimeout): - select { - case <-pool.quit: - case pool.timeout <- entry: - } - } - }() -} - -// checkDialTimeout checks if the node is still in dialed state and if so, resets it -// and adjusts connection statistics accordingly. -func (pool *serverPool) checkDialTimeout(entry *poolEntry) { - if entry.state != psDialed { - return - } - log.Debug("Dial timeout", "lesaddr", entry.id.String()+"@"+entry.dialed.strKey()) - entry.state = psNotConnected - if entry.knownSelected { - pool.knownSelected-- - } else { - pool.newSelected-- - } - entry.connectStats.add(0, 1) - entry.dialed.fails++ - pool.setRetryDial(entry) -} - -const ( - psNotConnected = iota - psDialed - psConnected - psRegistered -) - -// poolEntry represents a server node and stores its current state and statistics. -type poolEntry struct { - peer *peer - id discover.NodeID - addr map[string]*poolEntryAddress - lastConnected, dialed *poolEntryAddress - addrSelect weightedRandomSelect - - lastDiscovered mclock.AbsTime - known, knownSelected bool - connectStats, delayStats poolStats - responseStats, timeoutStats poolStats - state int - regTime mclock.AbsTime - queueIdx int - removed bool - - delayedRetry bool - shortRetry int -} - -func (e *poolEntry) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, []interface{}{e.id, e.lastConnected.ip, e.lastConnected.port, e.lastConnected.fails, &e.connectStats, &e.delayStats, &e.responseStats, &e.timeoutStats}) -} - -func (e *poolEntry) DecodeRLP(s *rlp.Stream) error { - var entry struct { - ID discover.NodeID - IP net.IP - Port uint16 - Fails uint - CStat, DStat, RStat, TStat poolStats - } - if err := s.Decode(&entry); err != nil { - return err - } - addr := &poolEntryAddress{ip: entry.IP, port: entry.Port, fails: entry.Fails, lastSeen: mclock.Now()} - e.id = entry.ID - e.addr = make(map[string]*poolEntryAddress) - e.addr[addr.strKey()] = addr - e.addrSelect = *newWeightedRandomSelect() - e.addrSelect.update(addr) - e.lastConnected = addr - e.connectStats = entry.CStat - e.delayStats = entry.DStat - e.responseStats = entry.RStat - e.timeoutStats = entry.TStat - e.shortRetry = shortRetryCnt - e.known = true - return nil -} - -// discoveredEntry implements wrsItem -type discoveredEntry poolEntry - -// Weight calculates random selection weight for newly discovered entries -func (e *discoveredEntry) Weight() int64 { - if e.state != psNotConnected || e.delayedRetry { - return 0 - } - t := time.Duration(mclock.Now() - e.lastDiscovered) - if t <= discoverExpireStart { - return 1000000000 - } - return int64(1000000000 * math.Exp(-float64(t-discoverExpireStart)/float64(discoverExpireConst))) -} - -// knownEntry implements wrsItem -type knownEntry poolEntry - -// Weight calculates random selection weight for known entries -func (e *knownEntry) Weight() int64 { - if e.state != psNotConnected || !e.known || e.delayedRetry { - return 0 - } - return int64(1000000000 * e.connectStats.recentAvg() * math.Exp(-float64(e.lastConnected.fails)*failDropLn-e.responseStats.recentAvg()/float64(responseScoreTC)-e.delayStats.recentAvg()/float64(delayScoreTC)) * math.Pow(1-e.timeoutStats.recentAvg(), timeoutPow)) -} - -// poolEntryAddress is a separate object because currently it is necessary to remember -// multiple potential network addresses for a pool entry. This will be removed after -// the final implementation of v5 discovery which will retrieve signed and serial -// numbered advertisements, making it clear which IP/port is the latest one. -type poolEntryAddress struct { - ip net.IP - port uint16 - lastSeen mclock.AbsTime // last time it was discovered, connected or loaded from db - fails uint // connection failures since last successful connection (persistent) -} - -func (a *poolEntryAddress) Weight() int64 { - t := time.Duration(mclock.Now() - a.lastSeen) - return int64(1000000*math.Exp(-float64(t)/float64(discoverExpireConst)-float64(a.fails)*addrFailDropLn)) + 1 -} - -func (a *poolEntryAddress) strKey() string { - return a.ip.String() + ":" + strconv.Itoa(int(a.port)) -} - -// poolStats implement statistics for a certain quantity with a long term average -// and a short term value which is adjusted exponentially with a factor of -// pstatRecentAdjust with each update and also returned exponentially to the -// average with the time constant pstatReturnToMeanTC -type poolStats struct { - sum, weight, avg, recent float64 - lastRecalc mclock.AbsTime -} - -// init initializes stats with a long term sum/update count pair retrieved from the database -func (s *poolStats) init(sum, weight float64) { - s.sum = sum - s.weight = weight - var avg float64 - if weight > 0 { - avg = s.sum / weight - } - s.avg = avg - s.recent = avg - s.lastRecalc = mclock.Now() -} - -// recalc recalculates recent value return-to-mean and long term average -func (s *poolStats) recalc() { - now := mclock.Now() - s.recent = s.avg + (s.recent-s.avg)*math.Exp(-float64(now-s.lastRecalc)/float64(pstatReturnToMeanTC)) - if s.sum == 0 { - s.avg = 0 - } else { - if s.sum > s.weight*1e30 { - s.avg = 1e30 - } else { - s.avg = s.sum / s.weight - } - } - s.lastRecalc = now -} - -// add updates the stats with a new value -func (s *poolStats) add(value, weight float64) { - s.weight += weight - s.sum += value * weight - s.recalc() -} - -// recentAvg returns the short-term adjusted average -func (s *poolStats) recentAvg() float64 { - s.recalc() - return s.recent -} - -func (s *poolStats) EncodeRLP(w io.Writer) error { - return rlp.Encode(w, []interface{}{math.Float64bits(s.sum), math.Float64bits(s.weight)}) -} - -func (s *poolStats) DecodeRLP(st *rlp.Stream) error { - var stats struct { - SumUint, WeightUint uint64 - } - if err := st.Decode(&stats); err != nil { - return err - } - s.init(math.Float64frombits(stats.SumUint), math.Float64frombits(stats.WeightUint)) - return nil -} - -// poolEntryQueue keeps track of its least recently accessed entries and removes -// them when the number of entries reaches the limit -type poolEntryQueue struct { - queue map[int]*poolEntry // known nodes indexed by their latest lastConnCnt value - newPtr, oldPtr, maxCnt int - removeFromPool func(*poolEntry) -} - -// newPoolEntryQueue returns a new poolEntryQueue -func newPoolEntryQueue(maxCnt int, removeFromPool func(*poolEntry)) poolEntryQueue { - return poolEntryQueue{queue: make(map[int]*poolEntry), maxCnt: maxCnt, removeFromPool: removeFromPool} -} - -// fetchOldest returns and removes the least recently accessed entry -func (q *poolEntryQueue) fetchOldest() *poolEntry { - if len(q.queue) == 0 { - return nil - } - for { - if e := q.queue[q.oldPtr]; e != nil { - delete(q.queue, q.oldPtr) - q.oldPtr++ - return e - } - q.oldPtr++ - } -} - -// remove removes an entry from the queue -func (q *poolEntryQueue) remove(entry *poolEntry) { - if q.queue[entry.queueIdx] == entry { - delete(q.queue, entry.queueIdx) - } -} - -// setLatest adds or updates a recently accessed entry. It also checks if an old entry -// needs to be removed and removes it from the parent pool too with a callback function. -func (q *poolEntryQueue) setLatest(entry *poolEntry) { - if q.queue[entry.queueIdx] == entry { - delete(q.queue, entry.queueIdx) - } else { - if len(q.queue) == q.maxCnt { - e := q.fetchOldest() - q.remove(e) - q.removeFromPool(e) - } - } - entry.queueIdx = q.newPtr - q.queue[entry.queueIdx] = entry - q.newPtr++ -} diff --git a/les/sync.go b/les/sync.go deleted file mode 100644 index 1ac6455852..0000000000 --- a/les/sync.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package les - -import ( - "context" - "time" - - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/light" -) - -// syncer is responsible for periodically synchronising with the network, both -// downloading hashes and blocks as well as handling the announcement handler. -func (pm *ProtocolManager) syncer() { - // Start and ensure cleanup of sync mechanisms - //pm.fetcher.Start() - //defer pm.fetcher.Stop() - defer pm.downloader.Terminate() - - // Wait for different events to fire synchronisation operations - //forceSync := time.Tick(forceSyncCycle) - for { - select { - case <-pm.newPeerCh: - /* // Make sure we have peers to select from, then sync - if pm.peers.Len() < minDesiredPeerCount { - break - } - go pm.synchronise(pm.peers.BestPeer()) - */ - /*case <-forceSync: - // Force a sync even if not enough peers are present - go pm.synchronise(pm.peers.BestPeer()) - */ - case <-pm.noMorePeers: - return - } - } -} - -func (pm *ProtocolManager) needToSync(peerHead blockInfo) bool { - head := pm.blockchain.CurrentHeader() - currentTd := rawdb.ReadTd(pm.chainDb, head.Hash(), head.Number.Uint64()) - return currentTd != nil && peerHead.Td.Cmp(currentTd) > 0 -} - -// synchronise tries to sync up our local block chain with a remote peer. -func (pm *ProtocolManager) synchronise(peer *peer) { - // Short circuit if no peers are available - if peer == nil { - return - } - - // Make sure the peer's TD is higher than our own. - if !pm.needToSync(peer.headBlockInfo()) { - return - } - - ctx, cancel := context.WithTimeout(context.Background(), time.Second*5) - defer cancel() - pm.blockchain.(*light.LightChain).SyncCht(ctx) - pm.downloader.Synchronise(peer.id, peer.Head(), peer.Td(), downloader.LightSync) -} diff --git a/les/txrelay.go b/les/txrelay.go deleted file mode 100644 index 7a02cc837e..0000000000 --- a/les/txrelay.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package les - -import ( - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - -type ltrInfo struct { - tx *types.Transaction - sentTo map[*peer]struct{} -} - -type LesTxRelay struct { - txSent map[common.Hash]*ltrInfo - txPending map[common.Hash]struct{} - ps *peerSet - peerList []*peer - peerStartPos int - lock sync.RWMutex - - reqDist *requestDistributor -} - -func NewLesTxRelay(ps *peerSet, reqDist *requestDistributor) *LesTxRelay { - r := &LesTxRelay{ - txSent: make(map[common.Hash]*ltrInfo), - txPending: make(map[common.Hash]struct{}), - ps: ps, - reqDist: reqDist, - } - ps.notify(r) - return r -} - -func (self *LesTxRelay) registerPeer(p *peer) { - self.lock.Lock() - defer self.lock.Unlock() - - self.peerList = self.ps.AllPeers() -} - -func (self *LesTxRelay) unregisterPeer(p *peer) { - self.lock.Lock() - defer self.lock.Unlock() - - self.peerList = self.ps.AllPeers() -} - -// send sends a list of transactions to at most a given number of peers at -// once, never resending any particular transaction to the same peer twice -func (self *LesTxRelay) send(txs types.Transactions, count int) { - sendTo := make(map[*peer]types.Transactions) - - self.peerStartPos++ // rotate the starting position of the peer list - if self.peerStartPos >= len(self.peerList) { - self.peerStartPos = 0 - } - - for _, tx := range txs { - hash := tx.Hash() - ltr, ok := self.txSent[hash] - if !ok { - ltr = <rInfo{ - tx: tx, - sentTo: make(map[*peer]struct{}), - } - self.txSent[hash] = ltr - self.txPending[hash] = struct{}{} - } - - if len(self.peerList) > 0 { - cnt := count - pos := self.peerStartPos - for { - peer := self.peerList[pos] - if _, ok := ltr.sentTo[peer]; !ok { - sendTo[peer] = append(sendTo[peer], tx) - ltr.sentTo[peer] = struct{}{} - cnt-- - } - if cnt == 0 { - break // sent it to the desired number of peers - } - pos++ - if pos == len(self.peerList) { - pos = 0 - } - if pos == self.peerStartPos { - break // tried all available peers - } - } - } - } - - for p, list := range sendTo { - pp := p - ll := list - - reqID := genReqID() - rq := &distReq{ - getCost: func(dp distPeer) uint64 { - peer := dp.(*peer) - return peer.GetRequestCost(SendTxMsg, len(ll)) - }, - canSend: func(dp distPeer) bool { - return dp.(*peer) == pp - }, - request: func(dp distPeer) func() { - peer := dp.(*peer) - cost := peer.GetRequestCost(SendTxMsg, len(ll)) - peer.fcServer.QueueRequest(reqID, cost) - return func() { peer.SendTxs(reqID, cost, ll) } - }, - } - self.reqDist.queue(rq) - } -} - -func (self *LesTxRelay) Send(txs types.Transactions) { - self.lock.Lock() - defer self.lock.Unlock() - - self.send(txs, 3) -} - -func (self *LesTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) { - self.lock.Lock() - defer self.lock.Unlock() - - for _, hash := range mined { - delete(self.txPending, hash) - } - - for _, hash := range rollback { - self.txPending[hash] = struct{}{} - } - - if len(self.txPending) > 0 { - txs := make(types.Transactions, len(self.txPending)) - i := 0 - for hash := range self.txPending { - txs[i] = self.txSent[hash].tx - i++ - } - self.send(txs, 1) - } -} - -func (self *LesTxRelay) Discard(hashes []common.Hash) { - self.lock.Lock() - defer self.lock.Unlock() - - for _, hash := range hashes { - delete(self.txSent, hash) - delete(self.txPending, hash) - } -} diff --git a/light/lightchain.go b/light/lightchain.go deleted file mode 100644 index 30b9bd89a6..0000000000 --- a/light/lightchain.go +++ /dev/null @@ -1,523 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package light - -import ( - "context" - "errors" - "math/big" - "sync" - "sync/atomic" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/hashicorp/golang-lru" -) - -var ( - bodyCacheLimit = 256 - blockCacheLimit = 256 -) - -// LightChain represents a canonical chain that by default only handles block -// headers, downloading block bodies and receipts on demand through an ODR -// interface. It only does header validation during chain insertion. -type LightChain struct { - hc *core.HeaderChain - chainDb ethdb.Database - odr OdrBackend - chainFeed event.Feed - chainSideFeed event.Feed - chainHeadFeed event.Feed - scope event.SubscriptionScope - genesisBlock *types.Block - - mu sync.RWMutex - chainmu sync.RWMutex - - bodyCache *lru.Cache // Cache for the most recent block bodies - bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format - blockCache *lru.Cache // Cache for the most recent entire blocks - - quit chan struct{} - running int32 // running must be called automically - // procInterrupt must be atomically called - procInterrupt int32 // interrupt signaler for block processing - wg sync.WaitGroup - - engine consensus.Engine -} - -// NewLightChain returns a fully initialised light chain using information -// available in the database. It initialises the default Ethereum header -// validator. -func NewLightChain(odr OdrBackend, config *params.ChainConfig, engine consensus.Engine) (*LightChain, error) { - bodyCache, _ := lru.New(bodyCacheLimit) - bodyRLPCache, _ := lru.New(bodyCacheLimit) - blockCache, _ := lru.New(blockCacheLimit) - - bc := &LightChain{ - chainDb: odr.Database(), - odr: odr, - quit: make(chan struct{}), - bodyCache: bodyCache, - bodyRLPCache: bodyRLPCache, - blockCache: blockCache, - engine: engine, - } - var err error - bc.hc, err = core.NewHeaderChain(odr.Database(), config, bc.engine, bc.getProcInterrupt) - if err != nil { - return nil, err - } - bc.genesisBlock, _ = bc.GetBlockByNumber(NoOdr, 0) - if bc.genesisBlock == nil { - return nil, core.ErrNoGenesis - } - if cp, ok := trustedCheckpoints[bc.genesisBlock.Hash()]; ok { - bc.addTrustedCheckpoint(cp) - } - if err := bc.loadLastState(); err != nil { - return nil, err - } - // Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain - for hash := range core.BadHashes { - if header := bc.GetHeaderByHash(hash); header != nil { - log.Error("Found bad hash, rewinding chain", "number", header.Number, "hash", header.ParentHash) - bc.SetHead(header.Number.Uint64() - 1) - log.Error("Chain rewind was successful, resuming normal operation") - } - } - return bc, nil -} - -// addTrustedCheckpoint adds a trusted checkpoint to the blockchain -func (self *LightChain) addTrustedCheckpoint(cp trustedCheckpoint) { - if self.odr.ChtIndexer() != nil { - StoreChtRoot(self.chainDb, cp.sectionIdx, cp.sectionHead, cp.chtRoot) - self.odr.ChtIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead) - } - if self.odr.BloomTrieIndexer() != nil { - StoreBloomTrieRoot(self.chainDb, cp.sectionIdx, cp.sectionHead, cp.bloomTrieRoot) - self.odr.BloomTrieIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead) - } - if self.odr.BloomIndexer() != nil { - self.odr.BloomIndexer().AddKnownSectionHead(cp.sectionIdx, cp.sectionHead) - } - log.Info("Added trusted checkpoint", "chain", cp.name, "block", (cp.sectionIdx+1)*CHTFrequencyClient-1, "hash", cp.sectionHead) -} - -func (self *LightChain) getProcInterrupt() bool { - return atomic.LoadInt32(&self.procInterrupt) == 1 -} - -// Odr returns the ODR backend of the chain -func (self *LightChain) Odr() OdrBackend { - return self.odr -} - -// loadLastState loads the last known chain state from the database. This method -// assumes that the chain manager mutex is held. -func (self *LightChain) loadLastState() error { - if head := rawdb.ReadHeadHeaderHash(self.chainDb); head == (common.Hash{}) { - // Corrupt or empty database, init from scratch - self.Reset() - } else { - if header := self.GetHeaderByHash(head); header != nil { - self.hc.SetCurrentHeader(header) - } - } - - // Issue a status log and return - header := self.hc.CurrentHeader() - headerTd := self.GetTd(header.Hash(), header.Number.Uint64()) - log.Info("Loaded most recent local header", "number", header.Number, "hash", header.Hash(), "td", headerTd) - - return nil -} - -// SetHead rewinds the local chain to a new head. Everything above the new -// head will be deleted and the new one set. -func (bc *LightChain) SetHead(head uint64) { - bc.mu.Lock() - defer bc.mu.Unlock() - - bc.hc.SetHead(head, nil) - bc.loadLastState() -} - -// GasLimit returns the gas limit of the current HEAD block. -func (self *LightChain) GasLimit() uint64 { - return self.hc.CurrentHeader().GasLimit -} - -// Reset purges the entire blockchain, restoring it to its genesis state. -func (bc *LightChain) Reset() { - bc.ResetWithGenesisBlock(bc.genesisBlock) -} - -// ResetWithGenesisBlock purges the entire blockchain, restoring it to the -// specified genesis state. -func (bc *LightChain) ResetWithGenesisBlock(genesis *types.Block) { - // Dump the entire block chain and purge the caches - bc.SetHead(0) - - bc.mu.Lock() - defer bc.mu.Unlock() - - // Prepare the genesis block and reinitialise the chain - rawdb.WriteTd(bc.chainDb, genesis.Hash(), genesis.NumberU64(), genesis.Difficulty()) - rawdb.WriteBlock(bc.chainDb, genesis) - - bc.genesisBlock = genesis - bc.hc.SetGenesis(bc.genesisBlock.Header()) - bc.hc.SetCurrentHeader(bc.genesisBlock.Header()) -} - -// Accessors - -// Engine retrieves the light chain's consensus engine. -func (bc *LightChain) Engine() consensus.Engine { return bc.engine } - -// Genesis returns the genesis block -func (bc *LightChain) Genesis() *types.Block { - return bc.genesisBlock -} - -// State returns a new mutable state based on the current HEAD block. -func (bc *LightChain) State() (*state.StateDB, error) { - return nil, errors.New("not implemented, needs client/server interface split") -} - -// GetBody retrieves a block body (transactions and uncles) from the database -// or ODR service by hash, caching it if found. -func (self *LightChain) GetBody(ctx context.Context, hash common.Hash) (*types.Body, error) { - // Short circuit if the body's already in the cache, retrieve otherwise - if cached, ok := self.bodyCache.Get(hash); ok { - body := cached.(*types.Body) - return body, nil - } - number := self.hc.GetBlockNumber(hash) - if number == nil { - return nil, errors.New("unknown block") - } - body, err := GetBody(ctx, self.odr, hash, *number) - if err != nil { - return nil, err - } - // Cache the found body for next time and return - self.bodyCache.Add(hash, body) - return body, nil -} - -// GetBodyRLP retrieves a block body in RLP encoding from the database or -// ODR service by hash, caching it if found. -func (self *LightChain) GetBodyRLP(ctx context.Context, hash common.Hash) (rlp.RawValue, error) { - // Short circuit if the body's already in the cache, retrieve otherwise - if cached, ok := self.bodyRLPCache.Get(hash); ok { - return cached.(rlp.RawValue), nil - } - number := self.hc.GetBlockNumber(hash) - if number == nil { - return nil, errors.New("unknown block") - } - body, err := GetBodyRLP(ctx, self.odr, hash, *number) - if err != nil { - return nil, err - } - // Cache the found body for next time and return - self.bodyRLPCache.Add(hash, body) - return body, nil -} - -// HasBlock checks if a block is fully present in the database or not, caching -// it if present. -func (bc *LightChain) HasBlock(hash common.Hash, number uint64) bool { - blk, _ := bc.GetBlock(NoOdr, hash, number) - return blk != nil -} - -// GetBlock retrieves a block from the database or ODR service by hash and number, -// caching it if found. -func (self *LightChain) GetBlock(ctx context.Context, hash common.Hash, number uint64) (*types.Block, error) { - // Short circuit if the block's already in the cache, retrieve otherwise - if block, ok := self.blockCache.Get(hash); ok { - return block.(*types.Block), nil - } - block, err := GetBlock(ctx, self.odr, hash, number) - if err != nil { - return nil, err - } - // Cache the found block for next time and return - self.blockCache.Add(block.Hash(), block) - return block, nil -} - -// GetBlockByHash retrieves a block from the database or ODR service by hash, -// caching it if found. -func (self *LightChain) GetBlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) { - number := self.hc.GetBlockNumber(hash) - if number == nil { - return nil, errors.New("unknown block") - } - return self.GetBlock(ctx, hash, *number) -} - -// GetBlockByNumber retrieves a block from the database or ODR service by -// number, caching it (associated with its hash) if found. -func (self *LightChain) GetBlockByNumber(ctx context.Context, number uint64) (*types.Block, error) { - hash, err := GetCanonicalHash(ctx, self.odr, number) - if hash == (common.Hash{}) || err != nil { - return nil, err - } - return self.GetBlock(ctx, hash, number) -} - -// Stop stops the blockchain service. If any imports are currently in progress -// it will abort them using the procInterrupt. -func (bc *LightChain) Stop() { - if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) { - return - } - close(bc.quit) - atomic.StoreInt32(&bc.procInterrupt, 1) - - bc.wg.Wait() - log.Info("Blockchain manager stopped") -} - -// Rollback is designed to remove a chain of links from the database that aren't -// certain enough to be valid. -func (self *LightChain) Rollback(chain []common.Hash) { - self.mu.Lock() - defer self.mu.Unlock() - - for i := len(chain) - 1; i >= 0; i-- { - hash := chain[i] - - if head := self.hc.CurrentHeader(); head.Hash() == hash { - self.hc.SetCurrentHeader(self.GetHeader(head.ParentHash, head.Number.Uint64()-1)) - } - } -} - -// postChainEvents iterates over the events generated by a chain insertion and -// posts them into the event feed. -func (self *LightChain) postChainEvents(events []interface{}) { - for _, event := range events { - switch ev := event.(type) { - case core.ChainEvent: - if self.CurrentHeader().Hash() == ev.Hash { - self.chainHeadFeed.Send(core.ChainHeadEvent{Block: ev.Block}) - } - self.chainFeed.Send(ev) - case core.ChainSideEvent: - self.chainSideFeed.Send(ev) - } - } -} - -// InsertHeaderChain attempts to insert the given header chain in to the local -// chain, possibly creating a reorg. If an error is returned, it will return the -// index number of the failing header as well an error describing what went wrong. -// -// The verify parameter can be used to fine tune whether nonce verification -// should be done or not. The reason behind the optional check is because some -// of the header retrieval mechanisms already need to verfy nonces, as well as -// because nonces can be verified sparsely, not needing to check each. -// -// In the case of a light chain, InsertHeaderChain also creates and posts light -// chain events when necessary. -func (self *LightChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (int, error) { - start := time.Now() - if i, err := self.hc.ValidateHeaderChain(chain, checkFreq); err != nil { - return i, err - } - - // Make sure only one thread manipulates the chain at once - self.chainmu.Lock() - defer func() { - self.chainmu.Unlock() - time.Sleep(time.Millisecond * 10) // ugly hack; do not hog chain lock in case syncing is CPU-limited by validation - }() - - self.wg.Add(1) - defer self.wg.Done() - - var events []interface{} - whFunc := func(header *types.Header) error { - self.mu.Lock() - defer self.mu.Unlock() - - status, err := self.hc.WriteHeader(header) - - switch status { - case core.CanonStatTy: - log.Debug("Inserted new header", "number", header.Number, "hash", header.Hash()) - events = append(events, core.ChainEvent{Block: types.NewBlockWithHeader(header), Hash: header.Hash()}) - - case core.SideStatTy: - log.Debug("Inserted forked header", "number", header.Number, "hash", header.Hash()) - events = append(events, core.ChainSideEvent{Block: types.NewBlockWithHeader(header)}) - } - return err - } - i, err := self.hc.InsertHeaderChain(chain, whFunc, start) - self.postChainEvents(events) - return i, err -} - -// CurrentHeader retrieves the current head header of the canonical chain. The -// header is retrieved from the HeaderChain's internal cache. -func (self *LightChain) CurrentHeader() *types.Header { - return self.hc.CurrentHeader() -} - -// GetTd retrieves a block's total difficulty in the canonical chain from the -// database by hash and number, caching it if found. -func (self *LightChain) GetTd(hash common.Hash, number uint64) *big.Int { - return self.hc.GetTd(hash, number) -} - -// GetTdByHash retrieves a block's total difficulty in the canonical chain from the -// database by hash, caching it if found. -func (self *LightChain) GetTdByHash(hash common.Hash) *big.Int { - return self.hc.GetTdByHash(hash) -} - -// GetHeader retrieves a block header from the database by hash and number, -// caching it if found. -func (self *LightChain) GetHeader(hash common.Hash, number uint64) *types.Header { - return self.hc.GetHeader(hash, number) -} - -// GetHeaderByHash retrieves a block header from the database by hash, caching it if -// found. -func (self *LightChain) GetHeaderByHash(hash common.Hash) *types.Header { - return self.hc.GetHeaderByHash(hash) -} - -// HasHeader checks if a block header is present in the database or not, caching -// it if present. -func (bc *LightChain) HasHeader(hash common.Hash, number uint64) bool { - return bc.hc.HasHeader(hash, number) -} - -// GetBlockHashesFromHash retrieves a number of block hashes starting at a given -// hash, fetching towards the genesis block. -func (self *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash { - return self.hc.GetBlockHashesFromHash(hash, max) -} - -// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or -// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the -// number of blocks to be individually checked before we reach the canonical chain. -// -// Note: ancestor == 0 returns the same block, 1 returns its parent and so on. -func (bc *LightChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) { - bc.chainmu.Lock() - defer bc.chainmu.Unlock() - - return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical) -} - -// GetHeaderByNumber retrieves a block header from the database by number, -// caching it (associated with its hash) if found. -func (self *LightChain) GetHeaderByNumber(number uint64) *types.Header { - return self.hc.GetHeaderByNumber(number) -} - -// GetHeaderByNumberOdr retrieves a block header from the database or network -// by number, caching it (associated with its hash) if found. -func (self *LightChain) GetHeaderByNumberOdr(ctx context.Context, number uint64) (*types.Header, error) { - if header := self.hc.GetHeaderByNumber(number); header != nil { - return header, nil - } - return GetHeaderByNumber(ctx, self.odr, number) -} - -// Config retrieves the header chain's chain configuration. -func (self *LightChain) Config() *params.ChainConfig { return self.hc.Config() } - -func (self *LightChain) SyncCht(ctx context.Context) bool { - if self.odr.ChtIndexer() == nil { - return false - } - headNum := self.CurrentHeader().Number.Uint64() - chtCount, _, _ := self.odr.ChtIndexer().Sections() - if headNum+1 < chtCount*CHTFrequencyClient { - num := chtCount*CHTFrequencyClient - 1 - header, err := GetHeaderByNumber(ctx, self.odr, num) - if header != nil && err == nil { - self.mu.Lock() - if self.hc.CurrentHeader().Number.Uint64() < header.Number.Uint64() { - self.hc.SetCurrentHeader(header) - } - self.mu.Unlock() - return true - } - } - return false -} - -// LockChain locks the chain mutex for reading so that multiple canonical hashes can be -// retrieved while it is guaranteed that they belong to the same version of the chain -func (self *LightChain) LockChain() { - self.chainmu.RLock() -} - -// UnlockChain unlocks the chain mutex -func (self *LightChain) UnlockChain() { - self.chainmu.RUnlock() -} - -// SubscribeChainEvent registers a subscription of ChainEvent. -func (self *LightChain) SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription { - return self.scope.Track(self.chainFeed.Subscribe(ch)) -} - -// SubscribeChainHeadEvent registers a subscription of ChainHeadEvent. -func (self *LightChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent) event.Subscription { - return self.scope.Track(self.chainHeadFeed.Subscribe(ch)) -} - -// SubscribeChainSideEvent registers a subscription of ChainSideEvent. -func (self *LightChain) SubscribeChainSideEvent(ch chan<- core.ChainSideEvent) event.Subscription { - return self.scope.Track(self.chainSideFeed.Subscribe(ch)) -} - -// SubscribeLogsEvent implements the interface of filters.Backend -// LightChain does not send logs events, so return an empty subscription. -func (self *LightChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription { - return self.scope.Track(new(event.Feed).Subscribe(ch)) -} - -// SubscribeRemovedLogsEvent implements the interface of filters.Backend -// LightChain does not send core.RemovedLogsEvent, so return an empty subscription. -func (self *LightChain) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEvent) event.Subscription { - return self.scope.Track(new(event.Feed).Subscribe(ch)) -} diff --git a/light/lightchain_test.go b/light/lightchain_test.go deleted file mode 100644 index c0aa51da29..0000000000 --- a/light/lightchain_test.go +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package light - -import ( - "context" - "math/big" - "testing" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/params" -) - -// So we can deterministically seed different blockchains -var ( - canonicalSeed = 1 - forkSeed = 2 -) - -// makeHeaderChain creates a deterministic chain of headers rooted at parent. -func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) []*types.Header { - blocks, _ := core.GenerateChain(params.TestChainConfig, types.NewBlockWithHeader(parent), ethash.NewFaker(), db, n, func(i int, b *core.BlockGen) { - b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)}) - }) - headers := make([]*types.Header, len(blocks)) - for i, block := range blocks { - headers[i] = block.Header() - } - return headers -} - -// newCanonical creates a chain database, and injects a deterministic canonical -// chain. Depending on the full flag, if creates either a full block chain or a -// header only chain. -func newCanonical(n int) (ethdb.Database, *LightChain, error) { - db := ethdb.NewMemDatabase() - gspec := core.Genesis{Config: params.TestChainConfig} - genesis := gspec.MustCommit(db) - blockchain, _ := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFaker()) - - // Create and inject the requested chain - if n == 0 { - return db, blockchain, nil - } - // Header-only chain requested - headers := makeHeaderChain(genesis.Header(), n, db, canonicalSeed) - _, err := blockchain.InsertHeaderChain(headers, 1) - return db, blockchain, err -} - -// newTestLightChain creates a LightChain that doesn't validate anything. -func newTestLightChain() *LightChain { - db := ethdb.NewMemDatabase() - gspec := &core.Genesis{ - Difficulty: big.NewInt(1), - Config: params.TestChainConfig, - } - gspec.MustCommit(db) - lc, err := NewLightChain(&dummyOdr{db: db}, gspec.Config, ethash.NewFullFaker()) - if err != nil { - panic(err) - } - return lc -} - -// Test fork of length N starting from block i -func testFork(t *testing.T, LightChain *LightChain, i, n int, comparator func(td1, td2 *big.Int)) { - // Copy old chain up to #i into a new db - db, LightChain2, err := newCanonical(i) - if err != nil { - t.Fatal("could not make new canonical in testFork", err) - } - // Assert the chains have the same header/block at #i - var hash1, hash2 common.Hash - hash1 = LightChain.GetHeaderByNumber(uint64(i)).Hash() - hash2 = LightChain2.GetHeaderByNumber(uint64(i)).Hash() - if hash1 != hash2 { - t.Errorf("chain content mismatch at %d: have hash %v, want hash %v", i, hash2, hash1) - } - // Extend the newly created chain - headerChainB := makeHeaderChain(LightChain2.CurrentHeader(), n, db, forkSeed) - if _, err := LightChain2.InsertHeaderChain(headerChainB, 1); err != nil { - t.Fatalf("failed to insert forking chain: %v", err) - } - // Sanity check that the forked chain can be imported into the original - var tdPre, tdPost *big.Int - - tdPre = LightChain.GetTdByHash(LightChain.CurrentHeader().Hash()) - if err := testHeaderChainImport(headerChainB, LightChain); err != nil { - t.Fatalf("failed to import forked header chain: %v", err) - } - tdPost = LightChain.GetTdByHash(headerChainB[len(headerChainB)-1].Hash()) - // Compare the total difficulties of the chains - comparator(tdPre, tdPost) -} - -// testHeaderChainImport tries to process a chain of header, writing them into -// the database if successful. -func testHeaderChainImport(chain []*types.Header, lightchain *LightChain) error { - for _, header := range chain { - // Try and validate the header - if err := lightchain.engine.VerifyHeader(lightchain.hc, header, true); err != nil { - return err - } - // Manually insert the header into the database, but don't reorganize (allows subsequent testing) - lightchain.mu.Lock() - rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash))) - rawdb.WriteHeader(lightchain.chainDb, header) - lightchain.mu.Unlock() - } - return nil -} - -// Tests that given a starting canonical chain of a given size, it can be extended -// with various length chains. -func TestExtendCanonicalHeaders(t *testing.T) { - length := 5 - - // Make first chain starting from genesis - _, processor, err := newCanonical(length) - if err != nil { - t.Fatalf("failed to make new canonical chain: %v", err) - } - // Define the difficulty comparator - better := func(td1, td2 *big.Int) { - if td2.Cmp(td1) <= 0 { - t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) - } - } - // Start fork from current height - testFork(t, processor, length, 1, better) - testFork(t, processor, length, 2, better) - testFork(t, processor, length, 5, better) - testFork(t, processor, length, 10, better) -} - -// Tests that given a starting canonical chain of a given size, creating shorter -// forks do not take canonical ownership. -func TestShorterForkHeaders(t *testing.T) { - length := 10 - - // Make first chain starting from genesis - _, processor, err := newCanonical(length) - if err != nil { - t.Fatalf("failed to make new canonical chain: %v", err) - } - // Define the difficulty comparator - worse := func(td1, td2 *big.Int) { - if td2.Cmp(td1) >= 0 { - t.Errorf("total difficulty mismatch: have %v, expected less than %v", td2, td1) - } - } - // Sum of numbers must be less than `length` for this to be a shorter fork - testFork(t, processor, 0, 3, worse) - testFork(t, processor, 0, 7, worse) - testFork(t, processor, 1, 1, worse) - testFork(t, processor, 1, 7, worse) - testFork(t, processor, 5, 3, worse) - testFork(t, processor, 5, 4, worse) -} - -// Tests that given a starting canonical chain of a given size, creating longer -// forks do take canonical ownership. -func TestLongerForkHeaders(t *testing.T) { - length := 10 - - // Make first chain starting from genesis - _, processor, err := newCanonical(length) - if err != nil { - t.Fatalf("failed to make new canonical chain: %v", err) - } - // Define the difficulty comparator - better := func(td1, td2 *big.Int) { - if td2.Cmp(td1) <= 0 { - t.Errorf("total difficulty mismatch: have %v, expected more than %v", td2, td1) - } - } - // Sum of numbers must be greater than `length` for this to be a longer fork - testFork(t, processor, 0, 11, better) - testFork(t, processor, 0, 15, better) - testFork(t, processor, 1, 10, better) - testFork(t, processor, 1, 12, better) - testFork(t, processor, 5, 6, better) - testFork(t, processor, 5, 8, better) -} - -// Tests that given a starting canonical chain of a given size, creating equal -// forks do take canonical ownership. -func TestEqualForkHeaders(t *testing.T) { - length := 10 - - // Make first chain starting from genesis - _, processor, err := newCanonical(length) - if err != nil { - t.Fatalf("failed to make new canonical chain: %v", err) - } - // Define the difficulty comparator - equal := func(td1, td2 *big.Int) { - if td2.Cmp(td1) != 0 { - t.Errorf("total difficulty mismatch: have %v, want %v", td2, td1) - } - } - // Sum of numbers must be equal to `length` for this to be an equal fork - testFork(t, processor, 0, 10, equal) - testFork(t, processor, 1, 9, equal) - testFork(t, processor, 2, 8, equal) - testFork(t, processor, 5, 5, equal) - testFork(t, processor, 6, 4, equal) - testFork(t, processor, 9, 1, equal) -} - -// Tests that chains missing links do not get accepted by the processor. -func TestBrokenHeaderChain(t *testing.T) { - // Make chain starting from genesis - db, LightChain, err := newCanonical(10) - if err != nil { - t.Fatalf("failed to make new canonical chain: %v", err) - } - // Create a forked chain, and try to insert with a missing link - chain := makeHeaderChain(LightChain.CurrentHeader(), 5, db, forkSeed)[1:] - if err := testHeaderChainImport(chain, LightChain); err == nil { - t.Errorf("broken header chain not reported") - } -} - -func makeHeaderChainWithDiff(genesis *types.Block, d []int, seed byte) []*types.Header { - var chain []*types.Header - for i, difficulty := range d { - header := &types.Header{ - Coinbase: common.Address{seed}, - Number: big.NewInt(int64(i + 1)), - Difficulty: big.NewInt(int64(difficulty)), - UncleHash: types.EmptyUncleHash, - TxHash: types.EmptyRootHash, - ReceiptHash: types.EmptyRootHash, - } - if i == 0 { - header.ParentHash = genesis.Hash() - } else { - header.ParentHash = chain[i-1].Hash() - } - chain = append(chain, types.CopyHeader(header)) - } - return chain -} - -type dummyOdr struct { - OdrBackend - db ethdb.Database -} - -func (odr *dummyOdr) Database() ethdb.Database { - return odr.db -} - -func (odr *dummyOdr) Retrieve(ctx context.Context, req OdrRequest) error { - return nil -} - -// Tests that reorganizing a long difficult chain after a short easy one -// overwrites the canonical numbers and links in the database. -func TestReorgLongHeaders(t *testing.T) { - testReorg(t, []int{1, 2, 4}, []int{1, 2, 3, 4}, 10) -} - -// Tests that reorganizing a short difficult chain after a long easy one -// overwrites the canonical numbers and links in the database. -func TestReorgShortHeaders(t *testing.T) { - testReorg(t, []int{1, 2, 3, 4}, []int{1, 10}, 11) -} - -func testReorg(t *testing.T, first, second []int, td int64) { - bc := newTestLightChain() - - // Insert an easy and a difficult chain afterwards - bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, first, 11), 1) - bc.InsertHeaderChain(makeHeaderChainWithDiff(bc.genesisBlock, second, 22), 1) - // Check that the chain is valid number and link wise - prev := bc.CurrentHeader() - for header := bc.GetHeaderByNumber(bc.CurrentHeader().Number.Uint64() - 1); header.Number.Uint64() != 0; prev, header = header, bc.GetHeaderByNumber(header.Number.Uint64()-1) { - if prev.ParentHash != header.Hash() { - t.Errorf("parent header hash mismatch: have %x, want %x", prev.ParentHash, header.Hash()) - } - } - // Make sure the chain total difficulty is the correct one - want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td)) - if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 { - t.Errorf("total difficulty mismatch: have %v, want %v", have, want) - } -} - -// Tests that the insertion functions detect banned hashes. -func TestBadHeaderHashes(t *testing.T) { - bc := newTestLightChain() - - // Create a chain, ban a hash and try to import - var err error - headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 4}, 10) - core.BadHashes[headers[2].Hash()] = true - if _, err = bc.InsertHeaderChain(headers, 1); err != core.ErrBlacklistedHash { - t.Errorf("error mismatch: have: %v, want %v", err, core.ErrBlacklistedHash) - } -} - -// Tests that bad hashes are detected on boot, and the chan rolled back to a -// good state prior to the bad hash. -func TestReorgBadHeaderHashes(t *testing.T) { - bc := newTestLightChain() - - // Create a chain, import and ban aferwards - headers := makeHeaderChainWithDiff(bc.genesisBlock, []int{1, 2, 3, 4}, 10) - - if _, err := bc.InsertHeaderChain(headers, 1); err != nil { - t.Fatalf("failed to import headers: %v", err) - } - if bc.CurrentHeader().Hash() != headers[3].Hash() { - t.Errorf("last header hash mismatch: have: %x, want %x", bc.CurrentHeader().Hash(), headers[3].Hash()) - } - core.BadHashes[headers[3].Hash()] = true - defer func() { delete(core.BadHashes, headers[3].Hash()) }() - - // Create a new LightChain and check that it rolled back the state. - ncm, err := NewLightChain(&dummyOdr{db: bc.chainDb}, params.TestChainConfig, ethash.NewFaker()) - if err != nil { - t.Fatalf("failed to create new chain manager: %v", err) - } - if ncm.CurrentHeader().Hash() != headers[2].Hash() { - t.Errorf("last header hash mismatch: have: %x, want %x", ncm.CurrentHeader().Hash(), headers[2].Hash()) - } -} diff --git a/light/nodeset.go b/light/nodeset.go deleted file mode 100644 index 6f25219c13..0000000000 --- a/light/nodeset.go +++ /dev/null @@ -1,148 +0,0 @@ -// 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 . - -package light - -import ( - "errors" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/rlp" -) - -// NodeSet stores a set of trie nodes. It implements trie.Database and can also -// act as a cache for another trie.Database. -type NodeSet struct { - nodes map[string][]byte - order []string - - dataSize int - lock sync.RWMutex -} - -// NewNodeSet creates an empty node set -func NewNodeSet() *NodeSet { - return &NodeSet{ - nodes: make(map[string][]byte), - } -} - -// Put stores a new node in the set -func (db *NodeSet) Put(key []byte, value []byte) error { - db.lock.Lock() - defer db.lock.Unlock() - - if _, ok := db.nodes[string(key)]; ok { - return nil - } - keystr := string(key) - - db.nodes[keystr] = common.CopyBytes(value) - db.order = append(db.order, keystr) - db.dataSize += len(value) - - return nil -} - -// Get returns a stored node -func (db *NodeSet) Get(key []byte) ([]byte, error) { - db.lock.RLock() - defer db.lock.RUnlock() - - if entry, ok := db.nodes[string(key)]; ok { - return entry, nil - } - return nil, errors.New("not found") -} - -// Has returns true if the node set contains the given key -func (db *NodeSet) Has(key []byte) (bool, error) { - _, err := db.Get(key) - return err == nil, nil -} - -// KeyCount returns the number of nodes in the set -func (db *NodeSet) KeyCount() int { - db.lock.RLock() - defer db.lock.RUnlock() - - return len(db.nodes) -} - -// DataSize returns the aggregated data size of nodes in the set -func (db *NodeSet) DataSize() int { - db.lock.RLock() - defer db.lock.RUnlock() - - return db.dataSize -} - -// NodeList converts the node set to a NodeList -func (db *NodeSet) NodeList() NodeList { - db.lock.RLock() - defer db.lock.RUnlock() - - var values NodeList - for _, key := range db.order { - values = append(values, db.nodes[key]) - } - return values -} - -// Store writes the contents of the set to the given database -func (db *NodeSet) Store(target ethdb.Putter) { - db.lock.RLock() - defer db.lock.RUnlock() - - for key, value := range db.nodes { - target.Put([]byte(key), value) - } -} - -// NodeList stores an ordered list of trie nodes. It implements ethdb.Putter. -type NodeList []rlp.RawValue - -// Store writes the contents of the list to the given database -func (n NodeList) Store(db ethdb.Putter) { - for _, node := range n { - db.Put(crypto.Keccak256(node), node) - } -} - -// NodeSet converts the node list to a NodeSet -func (n NodeList) NodeSet() *NodeSet { - db := NewNodeSet() - n.Store(db) - return db -} - -// Put stores a new node at the end of the list -func (n *NodeList) Put(key []byte, value []byte) error { - *n = append(*n, value) - return nil -} - -// DataSize returns the aggregated data size of nodes in the list -func (n NodeList) DataSize() int { - var size int - for _, node := range n { - size += len(node) - } - return size -} diff --git a/light/odr.go b/light/odr.go deleted file mode 100644 index 8f1e50b817..0000000000 --- a/light/odr.go +++ /dev/null @@ -1,172 +0,0 @@ -// Copyright 2015 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 . - -// Package light implements on-demand retrieval capable state and chain objects -// for the Ethereum Light Client. -package light - -import ( - "context" - "math/big" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" -) - -// NoOdr is the default context passed to an ODR capable function when the ODR -// service is not required. -var NoOdr = context.Background() - -// OdrBackend is an interface to a backend service that handles ODR retrievals type -type OdrBackend interface { - Database() ethdb.Database - ChtIndexer() *core.ChainIndexer - BloomTrieIndexer() *core.ChainIndexer - BloomIndexer() *core.ChainIndexer - Retrieve(ctx context.Context, req OdrRequest) error -} - -// OdrRequest is an interface for retrieval requests -type OdrRequest interface { - StoreResult(db ethdb.Database) -} - -// TrieID identifies a state or account storage trie -type TrieID struct { - BlockHash, Root common.Hash - BlockNumber uint64 - AccKey []byte -} - -// StateTrieID returns a TrieID for a state trie belonging to a certain block -// header. -func StateTrieID(header *types.Header) *TrieID { - return &TrieID{ - BlockHash: header.Hash(), - BlockNumber: header.Number.Uint64(), - AccKey: nil, - Root: header.Root, - } -} - -// StorageTrieID returns a TrieID for a contract storage trie at a given account -// of a given state trie. It also requires the root hash of the trie for -// checking Merkle proofs. -func StorageTrieID(state *TrieID, addrHash, root common.Hash) *TrieID { - return &TrieID{ - BlockHash: state.BlockHash, - BlockNumber: state.BlockNumber, - AccKey: addrHash[:], - Root: root, - } -} - -// TrieRequest is the ODR request type for state/storage trie entries -type TrieRequest struct { - OdrRequest - Id *TrieID - Key []byte - Proof *NodeSet -} - -// StoreResult stores the retrieved data in local database -func (req *TrieRequest) StoreResult(db ethdb.Database) { - req.Proof.Store(db) -} - -// CodeRequest is the ODR request type for retrieving contract code -type CodeRequest struct { - OdrRequest - Id *TrieID // references storage trie of the account - Hash common.Hash - Data []byte -} - -// StoreResult stores the retrieved data in local database -func (req *CodeRequest) StoreResult(db ethdb.Database) { - db.Put(req.Hash[:], req.Data) -} - -// BlockRequest is the ODR request type for retrieving block bodies -type BlockRequest struct { - OdrRequest - Hash common.Hash - Number uint64 - Rlp []byte -} - -// StoreResult stores the retrieved data in local database -func (req *BlockRequest) StoreResult(db ethdb.Database) { - rawdb.WriteBodyRLP(db, req.Hash, req.Number, req.Rlp) -} - -// ReceiptsRequest is the ODR request type for retrieving block bodies -type ReceiptsRequest struct { - OdrRequest - Hash common.Hash - Number uint64 - Receipts types.Receipts -} - -// StoreResult stores the retrieved data in local database -func (req *ReceiptsRequest) StoreResult(db ethdb.Database) { - rawdb.WriteReceipts(db, req.Hash, req.Number, req.Receipts) -} - -// ChtRequest is the ODR request type for state/storage trie entries -type ChtRequest struct { - OdrRequest - ChtNum, BlockNum uint64 - ChtRoot common.Hash - Header *types.Header - Td *big.Int - Proof *NodeSet -} - -// StoreResult stores the retrieved data in local database -func (req *ChtRequest) StoreResult(db ethdb.Database) { - hash, num := req.Header.Hash(), req.Header.Number.Uint64() - - rawdb.WriteHeader(db, req.Header) - rawdb.WriteTd(db, hash, num, req.Td) - rawdb.WriteCanonicalHash(db, hash, num) -} - -// BloomRequest is the ODR request type for retrieving bloom filters from a CHT structure -type BloomRequest struct { - OdrRequest - BloomTrieNum uint64 - BitIdx uint - SectionIdxList []uint64 - BloomTrieRoot common.Hash - BloomBits [][]byte - Proofs *NodeSet -} - -// StoreResult stores the retrieved data in local database -func (req *BloomRequest) StoreResult(db ethdb.Database) { - for i, sectionIdx := range req.SectionIdxList { - sectionHead := rawdb.ReadCanonicalHash(db, (sectionIdx+1)*BloomTrieFrequency-1) - // if we don't have the canonical hash stored for this section head number, we'll still store it under - // a key with a zero sectionHead. GetBloomBits will look there too if we still don't have the canonical - // hash. In the unlikely case we've retrieved the section head hash since then, we'll just retrieve the - // bit vector again from the network. - rawdb.WriteBloomBits(db, req.BitIdx, sectionIdx, sectionHead, req.BloomBits[i]) - } -} diff --git a/light/odr_test.go b/light/odr_test.go deleted file mode 100644 index 3e7ac10118..0000000000 --- a/light/odr_test.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package light - -import ( - "bytes" - "context" - "errors" - "math/big" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie" -) - -var ( - testBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") - testBankAddress = crypto.PubkeyToAddress(testBankKey.PublicKey) - testBankFunds = big.NewInt(100000000) - - acc1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a") - acc2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee") - acc1Addr = crypto.PubkeyToAddress(acc1Key.PublicKey) - acc2Addr = crypto.PubkeyToAddress(acc2Key.PublicKey) - - testContractCode = common.Hex2Bytes("606060405260cc8060106000396000f360606040526000357c01000000000000000000000000000000000000000000000000000000009004806360cd2685146041578063c16431b914606b57603f565b005b6055600480803590602001909190505060a9565b6040518082815260200191505060405180910390f35b60886004808035906020019091908035906020019091905050608a565b005b80600060005083606481101560025790900160005b50819055505b5050565b6000600060005082606481101560025790900160005b5054905060c7565b91905056") - testContractAddr common.Address -) - -type testOdr struct { - OdrBackend - sdb, ldb ethdb.Database - disable bool -} - -func (odr *testOdr) Database() ethdb.Database { - return odr.ldb -} - -var ErrOdrDisabled = errors.New("ODR disabled") - -func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error { - if odr.disable { - return ErrOdrDisabled - } - switch req := req.(type) { - case *BlockRequest: - number := rawdb.ReadHeaderNumber(odr.sdb, req.Hash) - if number != nil { - req.Rlp = rawdb.ReadBodyRLP(odr.sdb, req.Hash, *number) - } - case *ReceiptsRequest: - number := rawdb.ReadHeaderNumber(odr.sdb, req.Hash) - if number != nil { - req.Receipts = rawdb.ReadReceipts(odr.sdb, req.Hash, *number) - } - case *TrieRequest: - t, _ := trie.New(req.Id.Root, trie.NewDatabase(odr.sdb)) - nodes := NewNodeSet() - t.Prove(req.Key, 0, nodes) - req.Proof = nodes - case *CodeRequest: - req.Data, _ = odr.sdb.Get(req.Hash[:]) - } - req.StoreResult(odr.ldb) - return nil -} - -type odrTestFn func(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) - -func TestOdrGetBlockLes1(t *testing.T) { testChainOdr(t, 1, odrGetBlock) } - -func odrGetBlock(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { - var block *types.Block - if bc != nil { - block = bc.GetBlockByHash(bhash) - } else { - block, _ = lc.GetBlockByHash(ctx, bhash) - } - if block == nil { - return nil, nil - } - rlp, _ := rlp.EncodeToBytes(block) - return rlp, nil -} - -func TestOdrGetReceiptsLes1(t *testing.T) { testChainOdr(t, 1, odrGetReceipts) } - -func odrGetReceipts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { - var receipts types.Receipts - if bc != nil { - number := rawdb.ReadHeaderNumber(db, bhash) - if number != nil { - receipts = rawdb.ReadReceipts(db, bhash, *number) - } - } else { - number := rawdb.ReadHeaderNumber(db, bhash) - if number != nil { - receipts, _ = GetBlockReceipts(ctx, lc.Odr(), bhash, *number) - } - } - if receipts == nil { - return nil, nil - } - rlp, _ := rlp.EncodeToBytes(receipts) - return rlp, nil -} - -func TestOdrAccountsLes1(t *testing.T) { testChainOdr(t, 1, odrAccounts) } - -func odrAccounts(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { - dummyAddr := common.HexToAddress("1234567812345678123456781234567812345678") - acc := []common.Address{testBankAddress, acc1Addr, acc2Addr, dummyAddr} - - var st *state.StateDB - if bc == nil { - header := lc.GetHeaderByHash(bhash) - st = NewState(ctx, header, lc.Odr()) - } else { - header := bc.GetHeaderByHash(bhash) - st, _ = state.New(header.Root, state.NewDatabase(db)) - } - - var res []byte - for _, addr := range acc { - bal := st.GetBalance(addr) - rlp, _ := rlp.EncodeToBytes(bal) - res = append(res, rlp...) - } - return res, st.Error() -} - -func TestOdrContractCallLes1(t *testing.T) { testChainOdr(t, 1, odrContractCall) } - -type callmsg struct { - types.Message -} - -func (callmsg) CheckNonce() bool { return false } - -func odrContractCall(ctx context.Context, db ethdb.Database, bc *core.BlockChain, lc *LightChain, bhash common.Hash) ([]byte, error) { - data := common.Hex2Bytes("60CD26850000000000000000000000000000000000000000000000000000000000000000") - config := params.TestChainConfig - - var res []byte - for i := 0; i < 3; i++ { - data[35] = byte(i) - - var ( - st *state.StateDB - header *types.Header - chain core.ChainContext - ) - if bc == nil { - chain = lc - header = lc.GetHeaderByHash(bhash) - st = NewState(ctx, header, lc.Odr()) - } else { - chain = bc - header = bc.GetHeaderByHash(bhash) - st, _ = state.New(header.Root, state.NewDatabase(db)) - } - - // Perform read-only call. - st.SetBalance(testBankAddress, math.MaxBig256) - msg := callmsg{types.NewMessage(testBankAddress, &testContractAddr, 0, new(big.Int), 1000000, new(big.Int), data, false)} - context := core.NewEVMContext(msg, header, chain, nil) - vmenv := vm.NewEVM(context, st, config, vm.Config{}) - gp := new(core.GasPool).AddGas(math.MaxUint64) - ret, _, _, _ := core.ApplyMessage(vmenv, msg, gp) - res = append(res, ret...) - if st.Error() != nil { - return res, st.Error() - } - } - return res, nil -} - -func testChainGen(i int, block *core.BlockGen) { - signer := types.HomesteadSigner{} - switch i { - case 0: - // In block 1, the test bank sends account #1 some ether. - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), signer, testBankKey) - block.AddTx(tx) - case 1: - // In block 2, the test bank sends some more ether to account #1. - // acc1Addr passes it on to account #2. - // acc1Addr creates a test contract. - tx1, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), acc1Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, testBankKey) - nonce := block.TxNonce(acc1Addr) - tx2, _ := types.SignTx(types.NewTransaction(nonce, acc2Addr, big.NewInt(1000), params.TxGas, nil, nil), signer, acc1Key) - nonce++ - tx3, _ := types.SignTx(types.NewContractCreation(nonce, big.NewInt(0), 1000000, big.NewInt(0), testContractCode), signer, acc1Key) - testContractAddr = crypto.CreateAddress(acc1Addr, nonce) - block.AddTx(tx1) - block.AddTx(tx2) - block.AddTx(tx3) - case 2: - // Block 3 is empty but was mined by account #2. - block.SetCoinbase(acc2Addr) - block.SetExtra([]byte("yeehaw")) - data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000001") - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey) - block.AddTx(tx) - case 3: - // Block 4 includes blocks 2 and 3 as uncle headers (with modified extra data). - b2 := block.PrevBlock(1).Header() - b2.Extra = []byte("foo") - block.AddUncle(b2) - b3 := block.PrevBlock(2).Header() - b3.Extra = []byte("foo") - block.AddUncle(b3) - data := common.Hex2Bytes("C16431B900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000002") - tx, _ := types.SignTx(types.NewTransaction(block.TxNonce(testBankAddress), testContractAddr, big.NewInt(0), 100000, nil, data), signer, testBankKey) - block.AddTx(tx) - } -} - -func testChainOdr(t *testing.T, protocol int, fn odrTestFn) { - var ( - sdb = ethdb.NewMemDatabase() - ldb = ethdb.NewMemDatabase() - gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} - genesis = gspec.MustCommit(sdb) - ) - gspec.MustCommit(ldb) - // Assemble the test environment - blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}) - gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, 4, testChainGen) - if _, err := blockchain.InsertChain(gchain); err != nil { - t.Fatal(err) - } - - odr := &testOdr{sdb: sdb, ldb: ldb} - lightchain, err := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker()) - if err != nil { - t.Fatal(err) - } - headers := make([]*types.Header, len(gchain)) - for i, block := range gchain { - headers[i] = block.Header() - } - if _, err := lightchain.InsertHeaderChain(headers, 1); err != nil { - t.Fatal(err) - } - - test := func(expFail int) { - for i := uint64(0); i <= blockchain.CurrentHeader().Number.Uint64(); i++ { - bhash := rawdb.ReadCanonicalHash(sdb, i) - b1, err := fn(NoOdr, sdb, blockchain, nil, bhash) - if err != nil { - t.Fatalf("error in full-node test for block %d: %v", i, err) - } - - ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond) - defer cancel() - - exp := i < uint64(expFail) - b2, err := fn(ctx, ldb, nil, lightchain, bhash) - if err != nil && exp { - t.Errorf("error in ODR test for block %d: %v", i, err) - } - - eq := bytes.Equal(b1, b2) - if exp && !eq { - t.Errorf("ODR test output for block %d doesn't match full node", i) - } - } - } - - // expect retrievals to fail (except genesis block) without a les peer - t.Log("checking without ODR") - odr.disable = true - test(1) - - // expect all retrievals to pass with ODR enabled - t.Log("checking with ODR") - odr.disable = false - test(len(gchain)) - - // still expect all retrievals to pass, now data should be cached locally - t.Log("checking without ODR, should be cached") - odr.disable = true - test(len(gchain)) -} diff --git a/light/odr_util.go b/light/odr_util.go deleted file mode 100644 index 620af63835..0000000000 --- a/light/odr_util.go +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package light - -import ( - "bytes" - "context" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" -) - -var sha3_nil = crypto.Keccak256Hash(nil) - -func GetHeaderByNumber(ctx context.Context, odr OdrBackend, number uint64) (*types.Header, error) { - db := odr.Database() - hash := rawdb.ReadCanonicalHash(db, number) - if (hash != common.Hash{}) { - // if there is a canonical hash, there is a header too - header := rawdb.ReadHeader(db, hash, number) - if header == nil { - panic("Canonical hash present but header not found") - } - return header, nil - } - - var ( - chtCount, sectionHeadNum uint64 - sectionHead common.Hash - ) - if odr.ChtIndexer() != nil { - chtCount, sectionHeadNum, sectionHead = odr.ChtIndexer().Sections() - canonicalHash := rawdb.ReadCanonicalHash(db, sectionHeadNum) - // if the CHT was injected as a trusted checkpoint, we have no canonical hash yet so we accept zero hash too - for chtCount > 0 && canonicalHash != sectionHead && canonicalHash != (common.Hash{}) { - chtCount-- - if chtCount > 0 { - sectionHeadNum = chtCount*CHTFrequencyClient - 1 - sectionHead = odr.ChtIndexer().SectionHead(chtCount - 1) - canonicalHash = rawdb.ReadCanonicalHash(db, sectionHeadNum) - } - } - } - if number >= chtCount*CHTFrequencyClient { - return nil, ErrNoTrustedCht - } - r := &ChtRequest{ChtRoot: GetChtRoot(db, chtCount-1, sectionHead), ChtNum: chtCount - 1, BlockNum: number} - if err := odr.Retrieve(ctx, r); err != nil { - return nil, err - } - return r.Header, nil -} - -func GetCanonicalHash(ctx context.Context, odr OdrBackend, number uint64) (common.Hash, error) { - hash := rawdb.ReadCanonicalHash(odr.Database(), number) - if (hash != common.Hash{}) { - return hash, nil - } - header, err := GetHeaderByNumber(ctx, odr, number) - if header != nil { - return header.Hash(), nil - } - return common.Hash{}, err -} - -// GetBodyRLP retrieves the block body (transactions and uncles) in RLP encoding. -func GetBodyRLP(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (rlp.RawValue, error) { - if data := rawdb.ReadBodyRLP(odr.Database(), hash, number); data != nil { - return data, nil - } - r := &BlockRequest{Hash: hash, Number: number} - if err := odr.Retrieve(ctx, r); err != nil { - return nil, err - } else { - return r.Rlp, nil - } -} - -// GetBody retrieves the block body (transactons, uncles) corresponding to the -// hash. -func GetBody(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Body, error) { - data, err := GetBodyRLP(ctx, odr, hash, number) - if err != nil { - return nil, err - } - body := new(types.Body) - if err := rlp.Decode(bytes.NewReader(data), body); err != nil { - return nil, err - } - return body, nil -} - -// GetBlock retrieves an entire block corresponding to the hash, assembling it -// back from the stored header and body. -func GetBlock(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (*types.Block, error) { - // Retrieve the block header and body contents - header := rawdb.ReadHeader(odr.Database(), hash, number) - if header == nil { - return nil, ErrNoHeader - } - body, err := GetBody(ctx, odr, hash, number) - if err != nil { - return nil, err - } - // Reassemble the block and return - return types.NewBlockWithHeader(header).WithBody(body.Transactions, body.Uncles), nil -} - -// GetBlockReceipts retrieves the receipts generated by the transactions included -// in a block given by its hash. -func GetBlockReceipts(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) (types.Receipts, error) { - // Retrieve the potentially incomplete receipts from disk or network - receipts := rawdb.ReadReceipts(odr.Database(), hash, number) - if receipts == nil { - r := &ReceiptsRequest{Hash: hash, Number: number} - if err := odr.Retrieve(ctx, r); err != nil { - return nil, err - } - receipts = r.Receipts - } - // If the receipts are incomplete, fill the derived fields - if len(receipts) > 0 && receipts[0].TxHash == (common.Hash{}) { - block, err := GetBlock(ctx, odr, hash, number) - if err != nil { - return nil, err - } - genesis := rawdb.ReadCanonicalHash(odr.Database(), 0) - config := rawdb.ReadChainConfig(odr.Database(), genesis) - - if err := core.SetReceiptsData(config, block, receipts); err != nil { - return nil, err - } - rawdb.WriteReceipts(odr.Database(), hash, number, receipts) - } - return receipts, nil -} - -// GetBlockLogs retrieves the logs generated by the transactions included in a -// block given by its hash. -func GetBlockLogs(ctx context.Context, odr OdrBackend, hash common.Hash, number uint64) ([][]*types.Log, error) { - // Retrieve the potentially incomplete receipts from disk or network - receipts := rawdb.ReadReceipts(odr.Database(), hash, number) - if receipts == nil { - r := &ReceiptsRequest{Hash: hash, Number: number} - if err := odr.Retrieve(ctx, r); err != nil { - return nil, err - } - receipts = r.Receipts - } - // Return the logs without deriving any computed fields on the receipts - logs := make([][]*types.Log, len(receipts)) - for i, receipt := range receipts { - logs[i] = receipt.Logs - } - return logs, nil -} - -// GetBloomBits retrieves a batch of compressed bloomBits vectors belonging to the given bit index and section indexes -func GetBloomBits(ctx context.Context, odr OdrBackend, bitIdx uint, sectionIdxList []uint64) ([][]byte, error) { - db := odr.Database() - result := make([][]byte, len(sectionIdxList)) - var ( - reqList []uint64 - reqIdx []int - ) - - var ( - bloomTrieCount, sectionHeadNum uint64 - sectionHead common.Hash - ) - if odr.BloomTrieIndexer() != nil { - bloomTrieCount, sectionHeadNum, sectionHead = odr.BloomTrieIndexer().Sections() - canonicalHash := rawdb.ReadCanonicalHash(db, sectionHeadNum) - // if the BloomTrie was injected as a trusted checkpoint, we have no canonical hash yet so we accept zero hash too - for bloomTrieCount > 0 && canonicalHash != sectionHead && canonicalHash != (common.Hash{}) { - bloomTrieCount-- - if bloomTrieCount > 0 { - sectionHeadNum = bloomTrieCount*BloomTrieFrequency - 1 - sectionHead = odr.BloomTrieIndexer().SectionHead(bloomTrieCount - 1) - canonicalHash = rawdb.ReadCanonicalHash(db, sectionHeadNum) - } - } - } - - for i, sectionIdx := range sectionIdxList { - sectionHead := rawdb.ReadCanonicalHash(db, (sectionIdx+1)*BloomTrieFrequency-1) - // if we don't have the canonical hash stored for this section head number, we'll still look for - // an entry with a zero sectionHead (we store it with zero section head too if we don't know it - // at the time of the retrieval) - bloomBits, err := rawdb.ReadBloomBits(db, bitIdx, sectionIdx, sectionHead) - if err == nil { - result[i] = bloomBits - } else { - if sectionIdx >= bloomTrieCount { - return nil, ErrNoTrustedBloomTrie - } - reqList = append(reqList, sectionIdx) - reqIdx = append(reqIdx, i) - } - } - if reqList == nil { - return result, nil - } - - r := &BloomRequest{BloomTrieRoot: GetBloomTrieRoot(db, bloomTrieCount-1, sectionHead), BloomTrieNum: bloomTrieCount - 1, BitIdx: bitIdx, SectionIdxList: reqList} - if err := odr.Retrieve(ctx, r); err != nil { - return nil, err - } else { - for i, idx := range reqIdx { - result[idx] = r.BloomBits[i] - } - return result, nil - } -} diff --git a/light/postprocess.go b/light/postprocess.go deleted file mode 100644 index 121321ae66..0000000000 --- a/light/postprocess.go +++ /dev/null @@ -1,307 +0,0 @@ -// 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 . - -package light - -import ( - "encoding/binary" - "errors" - "math/big" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/bitutil" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/trie" -) - -const ( - // CHTFrequencyClient is the block frequency for creating CHTs on the client side. - CHTFrequencyClient = 32768 - - // CHTFrequencyServer is the block frequency for creating CHTs on the server side. - // Eventually this can be merged back with the client version, but that requires a - // full database upgrade, so that should be left for a suitable moment. - CHTFrequencyServer = 4096 - - HelperTrieConfirmations = 2048 // number of confirmations before a server is expected to have the given HelperTrie available - HelperTrieProcessConfirmations = 256 // number of confirmations before a HelperTrie is generated -) - -// trustedCheckpoint represents a set of post-processed trie roots (CHT and BloomTrie) associated with -// the appropriate section index and head hash. It is used to start light syncing from this checkpoint -// and avoid downloading the entire header chain while still being able to securely access old headers/logs. -type trustedCheckpoint struct { - name string - sectionIdx uint64 - sectionHead, chtRoot, bloomTrieRoot common.Hash -} - -var ( - mainnetCheckpoint = trustedCheckpoint{ - name: "mainnet", - sectionIdx: 174, - sectionHead: common.HexToHash("a3ef48cd8f1c3a08419f0237fc7763491fe89497b3144b17adf87c1c43664613"), - chtRoot: common.HexToHash("dcbeed9f4dea1b3cb75601bb27c51b9960c28e5850275402ac49a150a667296e"), - bloomTrieRoot: common.HexToHash("6b7497a4a03e33870a2383cb6f5e70570f12b1bf5699063baf8c71d02ca90b02"), - } - - ropstenCheckpoint = trustedCheckpoint{ - name: "ropsten", - sectionIdx: 102, - sectionHead: common.HexToHash("9017ab08465cb2b2dee035ee5b817bbd7fa28e2c8d2cd903e0aed1cccb249e89"), - chtRoot: common.HexToHash("f61c10a7a787a5ef15f0ae1ae6c13c64331e57e79d0466d2bd9b0c06833fe956"), - bloomTrieRoot: common.HexToHash("69f2ad19aa46d5213a90137b3d2c9bff8a7c9483f7170f0125096ff450c9a873"), - } -) - -// trustedCheckpoints associates each known checkpoint with the genesis hash of the chain it belongs to -var trustedCheckpoints = map[common.Hash]trustedCheckpoint{ - params.MainnetGenesisHash: mainnetCheckpoint, - params.TestnetGenesisHash: ropstenCheckpoint, -} - -var ( - ErrNoTrustedCht = errors.New("No trusted canonical hash trie") - ErrNoTrustedBloomTrie = errors.New("No trusted bloom trie") - ErrNoHeader = errors.New("Header not found") - chtPrefix = []byte("chtRoot-") // chtPrefix + chtNum (uint64 big endian) -> trie root hash - ChtTablePrefix = "cht-" -) - -// ChtNode structures are stored in the Canonical Hash Trie in an RLP encoded format -type ChtNode struct { - Hash common.Hash - Td *big.Int -} - -// GetChtRoot reads the CHT root assoctiated to the given section from the database -// Note that sectionIdx is specified according to LES/1 CHT section size -func GetChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { - var encNumber [8]byte - binary.BigEndian.PutUint64(encNumber[:], sectionIdx) - data, _ := db.Get(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...)) - return common.BytesToHash(data) -} - -// GetChtV2Root reads the CHT root assoctiated to the given section from the database -// Note that sectionIdx is specified according to LES/2 CHT section size -func GetChtV2Root(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { - return GetChtRoot(db, (sectionIdx+1)*(CHTFrequencyClient/CHTFrequencyServer)-1, sectionHead) -} - -// StoreChtRoot writes the CHT root assoctiated to the given section into the database -// Note that sectionIdx is specified according to LES/1 CHT section size -func StoreChtRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) { - var encNumber [8]byte - binary.BigEndian.PutUint64(encNumber[:], sectionIdx) - db.Put(append(append(chtPrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes()) -} - -// ChtIndexerBackend implements core.ChainIndexerBackend -type ChtIndexerBackend struct { - diskdb ethdb.Database - triedb *trie.Database - section, sectionSize uint64 - lastHash common.Hash - trie *trie.Trie -} - -// NewBloomTrieIndexer creates a BloomTrie chain indexer -func NewChtIndexer(db ethdb.Database, clientMode bool) *core.ChainIndexer { - var sectionSize, confirmReq uint64 - if clientMode { - sectionSize = CHTFrequencyClient - confirmReq = HelperTrieConfirmations - } else { - sectionSize = CHTFrequencyServer - confirmReq = HelperTrieProcessConfirmations - } - idb := ethdb.NewTable(db, "chtIndex-") - backend := &ChtIndexerBackend{ - diskdb: db, - triedb: trie.NewDatabase(ethdb.NewTable(db, ChtTablePrefix)), - sectionSize: sectionSize, - } - return core.NewChainIndexer(db, idb, backend, sectionSize, confirmReq, time.Millisecond*100, "cht") -} - -// Reset implements core.ChainIndexerBackend -func (c *ChtIndexerBackend) Reset(section uint64, lastSectionHead common.Hash) error { - var root common.Hash - if section > 0 { - root = GetChtRoot(c.diskdb, section-1, lastSectionHead) - } - var err error - c.trie, err = trie.New(root, c.triedb) - c.section = section - return err -} - -// Process implements core.ChainIndexerBackend -func (c *ChtIndexerBackend) Process(header *types.Header) { - hash, num := header.Hash(), header.Number.Uint64() - c.lastHash = hash - - td := rawdb.ReadTd(c.diskdb, hash, num) - if td == nil { - panic(nil) - } - var encNumber [8]byte - binary.BigEndian.PutUint64(encNumber[:], num) - data, _ := rlp.EncodeToBytes(ChtNode{hash, td}) - c.trie.Update(encNumber[:], data) -} - -// Commit implements core.ChainIndexerBackend -func (c *ChtIndexerBackend) Commit() error { - root, err := c.trie.Commit(nil) - if err != nil { - return err - } - c.triedb.Commit(root, false) - - if ((c.section+1)*c.sectionSize)%CHTFrequencyClient == 0 { - log.Info("Storing CHT", "section", c.section*c.sectionSize/CHTFrequencyClient, "head", c.lastHash, "root", root) - } - StoreChtRoot(c.diskdb, c.section, c.lastHash, root) - return nil -} - -const ( - BloomTrieFrequency = 32768 - ethBloomBitsSection = 4096 - ethBloomBitsConfirmations = 256 -) - -var ( - bloomTriePrefix = []byte("bltRoot-") // bloomTriePrefix + bloomTrieNum (uint64 big endian) -> trie root hash - BloomTrieTablePrefix = "blt-" -) - -// GetBloomTrieRoot reads the BloomTrie root assoctiated to the given section from the database -func GetBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead common.Hash) common.Hash { - var encNumber [8]byte - binary.BigEndian.PutUint64(encNumber[:], sectionIdx) - data, _ := db.Get(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...)) - return common.BytesToHash(data) -} - -// StoreBloomTrieRoot writes the BloomTrie root assoctiated to the given section into the database -func StoreBloomTrieRoot(db ethdb.Database, sectionIdx uint64, sectionHead, root common.Hash) { - var encNumber [8]byte - binary.BigEndian.PutUint64(encNumber[:], sectionIdx) - db.Put(append(append(bloomTriePrefix, encNumber[:]...), sectionHead.Bytes()...), root.Bytes()) -} - -// BloomTrieIndexerBackend implements core.ChainIndexerBackend -type BloomTrieIndexerBackend struct { - diskdb ethdb.Database - triedb *trie.Database - section, parentSectionSize, bloomTrieRatio uint64 - trie *trie.Trie - sectionHeads []common.Hash -} - -// NewBloomTrieIndexer creates a BloomTrie chain indexer -func NewBloomTrieIndexer(db ethdb.Database, clientMode bool) *core.ChainIndexer { - backend := &BloomTrieIndexerBackend{ - diskdb: db, - triedb: trie.NewDatabase(ethdb.NewTable(db, BloomTrieTablePrefix)), - } - idb := ethdb.NewTable(db, "bltIndex-") - - var confirmReq uint64 - if clientMode { - backend.parentSectionSize = BloomTrieFrequency - confirmReq = HelperTrieConfirmations - } else { - backend.parentSectionSize = ethBloomBitsSection - confirmReq = HelperTrieProcessConfirmations - } - backend.bloomTrieRatio = BloomTrieFrequency / backend.parentSectionSize - backend.sectionHeads = make([]common.Hash, backend.bloomTrieRatio) - return core.NewChainIndexer(db, idb, backend, BloomTrieFrequency, confirmReq-ethBloomBitsConfirmations, time.Millisecond*100, "bloomtrie") -} - -// Reset implements core.ChainIndexerBackend -func (b *BloomTrieIndexerBackend) Reset(section uint64, lastSectionHead common.Hash) error { - var root common.Hash - if section > 0 { - root = GetBloomTrieRoot(b.diskdb, section-1, lastSectionHead) - } - var err error - b.trie, err = trie.New(root, b.triedb) - b.section = section - return err -} - -// Process implements core.ChainIndexerBackend -func (b *BloomTrieIndexerBackend) Process(header *types.Header) { - num := header.Number.Uint64() - b.section*BloomTrieFrequency - if (num+1)%b.parentSectionSize == 0 { - b.sectionHeads[num/b.parentSectionSize] = header.Hash() - } -} - -// Commit implements core.ChainIndexerBackend -func (b *BloomTrieIndexerBackend) Commit() error { - var compSize, decompSize uint64 - - for i := uint(0); i < types.BloomBitLength; i++ { - var encKey [10]byte - binary.BigEndian.PutUint16(encKey[0:2], uint16(i)) - binary.BigEndian.PutUint64(encKey[2:10], b.section) - var decomp []byte - for j := uint64(0); j < b.bloomTrieRatio; j++ { - data, err := rawdb.ReadBloomBits(b.diskdb, i, b.section*b.bloomTrieRatio+j, b.sectionHeads[j]) - if err != nil { - return err - } - decompData, err2 := bitutil.DecompressBytes(data, int(b.parentSectionSize/8)) - if err2 != nil { - return err2 - } - decomp = append(decomp, decompData...) - } - comp := bitutil.CompressBytes(decomp) - - decompSize += uint64(len(decomp)) - compSize += uint64(len(comp)) - if len(comp) > 0 { - b.trie.Update(encKey[:], comp) - } else { - b.trie.Delete(encKey[:]) - } - } - root, err := b.trie.Commit(nil) - if err != nil { - return err - } - b.triedb.Commit(root, false) - - sectionHead := b.sectionHeads[b.bloomTrieRatio-1] - log.Info("Storing bloom trie", "section", b.section, "head", sectionHead, "root", root, "compression", float64(compSize)/float64(decompSize)) - StoreBloomTrieRoot(b.diskdb, b.section, sectionHead, root) - - return nil -} diff --git a/light/trie.go b/light/trie.go deleted file mode 100644 index c07e99461c..0000000000 --- a/light/trie.go +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2015 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 . - -package light - -import ( - "context" - "errors" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/trie" -) - -func NewState(ctx context.Context, head *types.Header, odr OdrBackend) *state.StateDB { - state, _ := state.New(head.Root, NewStateDatabase(ctx, head, odr)) - return state -} - -func NewStateDatabase(ctx context.Context, head *types.Header, odr OdrBackend) state.Database { - return &odrDatabase{ctx, StateTrieID(head), odr} -} - -type odrDatabase struct { - ctx context.Context - id *TrieID - backend OdrBackend -} - -func (db *odrDatabase) OpenTrie(root common.Hash) (state.Trie, error) { - return &odrTrie{db: db, id: db.id}, nil -} - -func (db *odrDatabase) OpenStorageTrie(addrHash, root common.Hash) (state.Trie, error) { - return &odrTrie{db: db, id: StorageTrieID(db.id, addrHash, root)}, nil -} - -func (db *odrDatabase) CopyTrie(t state.Trie) state.Trie { - switch t := t.(type) { - case *odrTrie: - cpy := &odrTrie{db: t.db, id: t.id} - if t.trie != nil { - cpytrie := *t.trie - cpy.trie = &cpytrie - } - return cpy - default: - panic(fmt.Errorf("unknown trie type %T", t)) - } -} - -func (db *odrDatabase) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) { - if codeHash == sha3_nil { - return nil, nil - } - if code, err := db.backend.Database().Get(codeHash[:]); err == nil { - return code, nil - } - id := *db.id - id.AccKey = addrHash[:] - req := &CodeRequest{Id: &id, Hash: codeHash} - err := db.backend.Retrieve(db.ctx, req) - return req.Data, err -} - -func (db *odrDatabase) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) { - code, err := db.ContractCode(addrHash, codeHash) - return len(code), err -} - -func (db *odrDatabase) TrieDB() *trie.Database { - return nil -} - -type odrTrie struct { - db *odrDatabase - id *TrieID - trie *trie.Trie -} - -func (t *odrTrie) TryGet(key []byte) ([]byte, error) { - key = crypto.Keccak256(key) - var res []byte - err := t.do(key, func() (err error) { - res, err = t.trie.TryGet(key) - return err - }) - return res, err -} - -func (t *odrTrie) TryUpdate(key, value []byte) error { - key = crypto.Keccak256(key) - return t.do(key, func() error { - return t.trie.TryDelete(key) - }) -} - -func (t *odrTrie) TryDelete(key []byte) error { - key = crypto.Keccak256(key) - return t.do(key, func() error { - return t.trie.TryDelete(key) - }) -} - -func (t *odrTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) { - if t.trie == nil { - return t.id.Root, nil - } - return t.trie.Commit(onleaf) -} - -func (t *odrTrie) Hash() common.Hash { - if t.trie == nil { - return t.id.Root - } - return t.trie.Hash() -} - -func (t *odrTrie) NodeIterator(startkey []byte) trie.NodeIterator { - return newNodeIterator(t, startkey) -} - -func (t *odrTrie) GetKey(sha []byte) []byte { - return nil -} - -func (t *odrTrie) Prove(key []byte, fromLevel uint, proofDb ethdb.Putter) error { - return errors.New("not implemented, needs client/server interface split") -} - -// do tries and retries to execute a function until it returns with no error or -// an error type other than MissingNodeError -func (t *odrTrie) do(key []byte, fn func() error) error { - for { - var err error - if t.trie == nil { - t.trie, err = trie.New(t.id.Root, trie.NewDatabase(t.db.backend.Database())) - } - if err == nil { - err = fn() - } - if _, ok := err.(*trie.MissingNodeError); !ok { - return err - } - r := &TrieRequest{Id: t.id, Key: key} - if err := t.db.backend.Retrieve(t.db.ctx, r); err != nil { - return err - } - } -} - -type nodeIterator struct { - trie.NodeIterator - t *odrTrie - err error -} - -func newNodeIterator(t *odrTrie, startkey []byte) trie.NodeIterator { - it := &nodeIterator{t: t} - // Open the actual non-ODR trie if that hasn't happened yet. - if t.trie == nil { - it.do(func() error { - t, err := trie.New(t.id.Root, trie.NewDatabase(t.db.backend.Database())) - if err == nil { - it.t.trie = t - } - return err - }) - } - it.do(func() error { - it.NodeIterator = it.t.trie.NodeIterator(startkey) - return it.NodeIterator.Error() - }) - return it -} - -func (it *nodeIterator) Next(descend bool) bool { - var ok bool - it.do(func() error { - ok = it.NodeIterator.Next(descend) - return it.NodeIterator.Error() - }) - return ok -} - -// do runs fn and attempts to fill in missing nodes by retrieving. -func (it *nodeIterator) do(fn func() error) { - var lasthash common.Hash - for { - it.err = fn() - missing, ok := it.err.(*trie.MissingNodeError) - if !ok { - return - } - if missing.NodeHash == lasthash { - it.err = fmt.Errorf("retrieve loop for trie node %x", missing.NodeHash) - return - } - lasthash = missing.NodeHash - r := &TrieRequest{Id: it.t.id, Key: nibblesToKey(missing.Path)} - if it.err = it.t.db.backend.Retrieve(it.t.db.ctx, r); it.err != nil { - return - } - } -} - -func (it *nodeIterator) Error() error { - if it.err != nil { - return it.err - } - return it.NodeIterator.Error() -} - -func nibblesToKey(nib []byte) []byte { - if len(nib) > 0 && nib[len(nib)-1] == 0x10 { - nib = nib[:len(nib)-1] // drop terminator - } - if len(nib)&1 == 1 { - nib = append(nib, 0) // make even - } - key := make([]byte, len(nib)/2) - for bi, ni := 0, 0; ni < len(nib); bi, ni = bi+1, ni+2 { - key[bi] = nib[ni]<<4 | nib[ni+1] - } - return key -} diff --git a/light/trie_test.go b/light/trie_test.go deleted file mode 100644 index 84c6f162fb..0000000000 --- a/light/trie_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// 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 . - -package light - -import ( - "bytes" - "context" - "fmt" - "testing" - - "github.com/davecgh/go-spew/spew" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/trie" -) - -func TestNodeIterator(t *testing.T) { - var ( - fulldb = ethdb.NewMemDatabase() - lightdb = ethdb.NewMemDatabase() - gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} - genesis = gspec.MustCommit(fulldb) - ) - gspec.MustCommit(lightdb) - blockchain, _ := core.NewBlockChain(fulldb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}) - gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), fulldb, 4, testChainGen) - if _, err := blockchain.InsertChain(gchain); err != nil { - panic(err) - } - - ctx := context.Background() - odr := &testOdr{sdb: fulldb, ldb: lightdb} - head := blockchain.CurrentHeader() - lightTrie, _ := NewStateDatabase(ctx, head, odr).OpenTrie(head.Root) - fullTrie, _ := state.NewDatabase(fulldb).OpenTrie(head.Root) - if err := diffTries(fullTrie, lightTrie); err != nil { - t.Fatal(err) - } -} - -func diffTries(t1, t2 state.Trie) error { - i1 := trie.NewIterator(t1.NodeIterator(nil)) - i2 := trie.NewIterator(t2.NodeIterator(nil)) - for i1.Next() && i2.Next() { - if !bytes.Equal(i1.Key, i2.Key) { - spew.Dump(i2) - return fmt.Errorf("tries have different keys %x, %x", i1.Key, i2.Key) - } - if !bytes.Equal(i2.Value, i2.Value) { - return fmt.Errorf("tries differ at key %x", i1.Key) - } - } - switch { - case i1.Err != nil: - return fmt.Errorf("full trie iterator error: %v", i1.Err) - case i2.Err != nil: - return fmt.Errorf("light trie iterator error: %v", i1.Err) - case i1.Next(): - return fmt.Errorf("full trie iterator has more k/v pairs") - case i2.Next(): - return fmt.Errorf("light trie iterator has more k/v pairs") - } - return nil -} diff --git a/light/txpool.go b/light/txpool.go deleted file mode 100644 index 767a797bdc..0000000000 --- a/light/txpool.go +++ /dev/null @@ -1,530 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package light - -import ( - "context" - "fmt" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/rawdb" - "github.com/ethereum/go-ethereum/core/state" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/event" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/params" - "github.com/ethereum/go-ethereum/rlp" -) - -const ( - // chainHeadChanSize is the size of channel listening to ChainHeadEvent. - chainHeadChanSize = 10 -) - -// txPermanent is the number of mined blocks after a mined transaction is -// considered permanent and no rollback is expected -var txPermanent = uint64(500) - -// TxPool implements the transaction pool for light clients, which keeps track -// of the status of locally created transactions, detecting if they are included -// in a block (mined) or rolled back. There are no queued transactions since we -// always receive all locally signed transactions in the same order as they are -// created. -type TxPool struct { - config *params.ChainConfig - signer types.Signer - quit chan bool - txFeed event.Feed - scope event.SubscriptionScope - chainHeadCh chan core.ChainHeadEvent - chainHeadSub event.Subscription - mu sync.RWMutex - chain *LightChain - odr OdrBackend - chainDb ethdb.Database - relay TxRelayBackend - head common.Hash - nonce map[common.Address]uint64 // "pending" nonce - pending map[common.Hash]*types.Transaction // pending transactions by tx hash - mined map[common.Hash][]*types.Transaction // mined transactions by block hash - clearIdx uint64 // earliest block nr that can contain mined tx info - - homestead bool -} - -// TxRelayBackend provides an interface to the mechanism that forwards transacions -// to the ETH network. The implementations of the functions should be non-blocking. -// -// Send instructs backend to forward new transactions -// NewHead notifies backend about a new head after processed by the tx pool, -// including mined and rolled back transactions since the last event -// Discard notifies backend about transactions that should be discarded either -// because they have been replaced by a re-send or because they have been mined -// long ago and no rollback is expected -type TxRelayBackend interface { - Send(txs types.Transactions) - NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) - Discard(hashes []common.Hash) -} - -// NewTxPool creates a new light transaction pool -func NewTxPool(config *params.ChainConfig, chain *LightChain, relay TxRelayBackend) *TxPool { - pool := &TxPool{ - config: config, - signer: types.NewEIP155Signer(config.ChainID), - nonce: make(map[common.Address]uint64), - pending: make(map[common.Hash]*types.Transaction), - mined: make(map[common.Hash][]*types.Transaction), - quit: make(chan bool), - chainHeadCh: make(chan core.ChainHeadEvent, chainHeadChanSize), - chain: chain, - relay: relay, - odr: chain.Odr(), - chainDb: chain.Odr().Database(), - head: chain.CurrentHeader().Hash(), - clearIdx: chain.CurrentHeader().Number.Uint64(), - } - // Subscribe events from blockchain - pool.chainHeadSub = pool.chain.SubscribeChainHeadEvent(pool.chainHeadCh) - go pool.eventLoop() - - return pool -} - -// currentState returns the light state of the current head header -func (pool *TxPool) currentState(ctx context.Context) *state.StateDB { - return NewState(ctx, pool.chain.CurrentHeader(), pool.odr) -} - -// GetNonce returns the "pending" nonce of a given address. It always queries -// the nonce belonging to the latest header too in order to detect if another -// client using the same key sent a transaction. -func (pool *TxPool) GetNonce(ctx context.Context, addr common.Address) (uint64, error) { - state := pool.currentState(ctx) - nonce := state.GetNonce(addr) - if state.Error() != nil { - return 0, state.Error() - } - sn, ok := pool.nonce[addr] - if ok && sn > nonce { - nonce = sn - } - if !ok || sn < nonce { - pool.nonce[addr] = nonce - } - return nonce, nil -} - -// txStateChanges stores the recent changes between pending/mined states of -// transactions. True means mined, false means rolled back, no entry means no change -type txStateChanges map[common.Hash]bool - -// setState sets the status of a tx to either recently mined or recently rolled back -func (txc txStateChanges) setState(txHash common.Hash, mined bool) { - val, ent := txc[txHash] - if ent && (val != mined) { - delete(txc, txHash) - } else { - txc[txHash] = mined - } -} - -// getLists creates lists of mined and rolled back tx hashes -func (txc txStateChanges) getLists() (mined []common.Hash, rollback []common.Hash) { - for hash, val := range txc { - if val { - mined = append(mined, hash) - } else { - rollback = append(rollback, hash) - } - } - return -} - -// checkMinedTxs checks newly added blocks for the currently pending transactions -// and marks them as mined if necessary. It also stores block position in the db -// and adds them to the received txStateChanges map. -func (pool *TxPool) checkMinedTxs(ctx context.Context, hash common.Hash, number uint64, txc txStateChanges) error { - // If no transactions are pending, we don't care about anything - if len(pool.pending) == 0 { - return nil - } - block, err := GetBlock(ctx, pool.odr, hash, number) - if err != nil { - return err - } - // Gather all the local transaction mined in this block - list := pool.mined[hash] - for _, tx := range block.Transactions() { - if _, ok := pool.pending[tx.Hash()]; ok { - list = append(list, tx) - } - } - // If some transactions have been mined, write the needed data to disk and update - if list != nil { - // Retrieve all the receipts belonging to this block and write the loopup table - if _, err := GetBlockReceipts(ctx, pool.odr, hash, number); err != nil { // ODR caches, ignore results - return err - } - rawdb.WriteTxLookupEntries(pool.chainDb, block) - - // Update the transaction pool's state - for _, tx := range list { - delete(pool.pending, tx.Hash()) - txc.setState(tx.Hash(), true) - } - pool.mined[hash] = list - } - return nil -} - -// rollbackTxs marks the transactions contained in recently rolled back blocks -// as rolled back. It also removes any positional lookup entries. -func (pool *TxPool) rollbackTxs(hash common.Hash, txc txStateChanges) { - batch := pool.chainDb.NewBatch() - if list, ok := pool.mined[hash]; ok { - for _, tx := range list { - txHash := tx.Hash() - rawdb.DeleteTxLookupEntry(batch, txHash) - pool.pending[txHash] = tx - txc.setState(txHash, false) - } - delete(pool.mined, hash) - } - batch.Write() -} - -// reorgOnNewHead sets a new head header, processing (and rolling back if necessary) -// the blocks since the last known head and returns a txStateChanges map containing -// the recently mined and rolled back transaction hashes. If an error (context -// timeout) occurs during checking new blocks, it leaves the locally known head -// at the latest checked block and still returns a valid txStateChanges, making it -// possible to continue checking the missing blocks at the next chain head event -func (pool *TxPool) reorgOnNewHead(ctx context.Context, newHeader *types.Header) (txStateChanges, error) { - txc := make(txStateChanges) - oldh := pool.chain.GetHeaderByHash(pool.head) - newh := newHeader - // find common ancestor, create list of rolled back and new block hashes - var oldHashes, newHashes []common.Hash - for oldh.Hash() != newh.Hash() { - if oldh.Number.Uint64() >= newh.Number.Uint64() { - oldHashes = append(oldHashes, oldh.Hash()) - oldh = pool.chain.GetHeader(oldh.ParentHash, oldh.Number.Uint64()-1) - } - if oldh.Number.Uint64() < newh.Number.Uint64() { - newHashes = append(newHashes, newh.Hash()) - newh = pool.chain.GetHeader(newh.ParentHash, newh.Number.Uint64()-1) - if newh == nil { - // happens when CHT syncing, nothing to do - newh = oldh - } - } - } - if oldh.Number.Uint64() < pool.clearIdx { - pool.clearIdx = oldh.Number.Uint64() - } - // roll back old blocks - for _, hash := range oldHashes { - pool.rollbackTxs(hash, txc) - } - pool.head = oldh.Hash() - // check mined txs of new blocks (array is in reversed order) - for i := len(newHashes) - 1; i >= 0; i-- { - hash := newHashes[i] - if err := pool.checkMinedTxs(ctx, hash, newHeader.Number.Uint64()-uint64(i), txc); err != nil { - return txc, err - } - pool.head = hash - } - - // clear old mined tx entries of old blocks - if idx := newHeader.Number.Uint64(); idx > pool.clearIdx+txPermanent { - idx2 := idx - txPermanent - if len(pool.mined) > 0 { - for i := pool.clearIdx; i < idx2; i++ { - hash := rawdb.ReadCanonicalHash(pool.chainDb, i) - if list, ok := pool.mined[hash]; ok { - hashes := make([]common.Hash, len(list)) - for i, tx := range list { - hashes[i] = tx.Hash() - } - pool.relay.Discard(hashes) - delete(pool.mined, hash) - } - } - } - pool.clearIdx = idx2 - } - - return txc, nil -} - -// blockCheckTimeout is the time limit for checking new blocks for mined -// transactions. Checking resumes at the next chain head event if timed out. -const blockCheckTimeout = time.Second * 3 - -// eventLoop processes chain head events and also notifies the tx relay backend -// about the new head hash and tx state changes -func (pool *TxPool) eventLoop() { - for { - select { - case ev := <-pool.chainHeadCh: - pool.setNewHead(ev.Block.Header()) - // hack in order to avoid hogging the lock; this part will - // be replaced by a subsequent PR. - time.Sleep(time.Millisecond) - - // System stopped - case <-pool.chainHeadSub.Err(): - return - } - } -} - -func (pool *TxPool) setNewHead(head *types.Header) { - pool.mu.Lock() - defer pool.mu.Unlock() - - ctx, cancel := context.WithTimeout(context.Background(), blockCheckTimeout) - defer cancel() - - txc, _ := pool.reorgOnNewHead(ctx, head) - m, r := txc.getLists() - pool.relay.NewHead(pool.head, m, r) - pool.homestead = pool.config.IsHomestead(head.Number) - pool.signer = types.MakeSigner(pool.config, head.Number) -} - -// Stop stops the light transaction pool -func (pool *TxPool) Stop() { - // Unsubscribe all subscriptions registered from txpool - pool.scope.Close() - // Unsubscribe subscriptions registered from blockchain - pool.chainHeadSub.Unsubscribe() - close(pool.quit) - log.Info("Transaction pool stopped") -} - -// SubscribeNewTxsEvent registers a subscription of core.NewTxsEvent and -// starts sending event to the given channel. -func (pool *TxPool) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.Subscription { - return pool.scope.Track(pool.txFeed.Subscribe(ch)) -} - -// Stats returns the number of currently pending (locally created) transactions -func (pool *TxPool) Stats() (pending int) { - pool.mu.RLock() - defer pool.mu.RUnlock() - - pending = len(pool.pending) - return -} - -// validateTx checks whether a transaction is valid according to the consensus rules. -func (pool *TxPool) validateTx(ctx context.Context, tx *types.Transaction) error { - // Validate sender - var ( - from common.Address - err error - ) - - // Validate the transaction sender and it's sig. Throw - // if the from fields is invalid. - if from, err = types.Sender(pool.signer, tx); err != nil { - return core.ErrInvalidSender - } - // Last but not least check for nonce errors - currentState := pool.currentState(ctx) - if n := currentState.GetNonce(from); n > tx.Nonce() { - return core.ErrNonceTooLow - } - - // Check the transaction doesn't exceed the current - // block limit gas. - header := pool.chain.GetHeaderByHash(pool.head) - if header.GasLimit < tx.Gas() { - return core.ErrGasLimit - } - - // Transactions can't be negative. This may never happen - // using RLP decoded transactions but may occur if you create - // a transaction using the RPC for example. - if tx.Value().Sign() < 0 { - return core.ErrNegativeValue - } - - // Transactor should have enough funds to cover the costs - // cost == V + GP * GL - if b := currentState.GetBalance(from); b.Cmp(tx.Cost()) < 0 { - return core.ErrInsufficientFunds - } - - // Should supply enough intrinsic gas - gas, err := core.IntrinsicGas(tx.Data(), tx.To() == nil, pool.homestead) - if err != nil { - return err - } - if tx.Gas() < gas { - return core.ErrIntrinsicGas - } - return currentState.Error() -} - -// add validates a new transaction and sets its state pending if processable. -// It also updates the locally stored nonce if necessary. -func (self *TxPool) add(ctx context.Context, tx *types.Transaction) error { - hash := tx.Hash() - - if self.pending[hash] != nil { - return fmt.Errorf("Known transaction (%x)", hash[:4]) - } - err := self.validateTx(ctx, tx) - if err != nil { - return err - } - - if _, ok := self.pending[hash]; !ok { - self.pending[hash] = tx - - nonce := tx.Nonce() + 1 - - addr, _ := types.Sender(self.signer, tx) - if nonce > self.nonce[addr] { - self.nonce[addr] = nonce - } - - // Notify the subscribers. This event is posted in a goroutine - // because it's possible that somewhere during the post "Remove transaction" - // gets called which will then wait for the global tx pool lock and deadlock. - go self.txFeed.Send(core.NewTxsEvent{Txs: types.Transactions{tx}}) - } - - // Print a log message if low enough level is set - log.Debug("Pooled new transaction", "hash", hash, "from", log.Lazy{Fn: func() common.Address { from, _ := types.Sender(self.signer, tx); return from }}, "to", tx.To()) - return nil -} - -// Add adds a transaction to the pool if valid and passes it to the tx relay -// backend -func (self *TxPool) Add(ctx context.Context, tx *types.Transaction) error { - self.mu.Lock() - defer self.mu.Unlock() - - data, err := rlp.EncodeToBytes(tx) - if err != nil { - return err - } - - if err := self.add(ctx, tx); err != nil { - return err - } - //fmt.Println("Send", tx.Hash()) - self.relay.Send(types.Transactions{tx}) - - self.chainDb.Put(tx.Hash().Bytes(), data) - return nil -} - -// AddTransactions adds all valid transactions to the pool and passes them to -// the tx relay backend -func (self *TxPool) AddBatch(ctx context.Context, txs []*types.Transaction) { - self.mu.Lock() - defer self.mu.Unlock() - var sendTx types.Transactions - - for _, tx := range txs { - if err := self.add(ctx, tx); err == nil { - sendTx = append(sendTx, tx) - } - } - if len(sendTx) > 0 { - self.relay.Send(sendTx) - } -} - -// GetTransaction returns a transaction if it is contained in the pool -// and nil otherwise. -func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction { - // check the txs first - if tx, ok := tp.pending[hash]; ok { - return tx - } - return nil -} - -// GetTransactions returns all currently processable transactions. -// The returned slice may be modified by the caller. -func (self *TxPool) GetTransactions() (txs types.Transactions, err error) { - self.mu.RLock() - defer self.mu.RUnlock() - - txs = make(types.Transactions, len(self.pending)) - i := 0 - for _, tx := range self.pending { - txs[i] = tx - i++ - } - return txs, nil -} - -// Content retrieves the data content of the transaction pool, returning all the -// pending as well as queued transactions, grouped by account and nonce. -func (self *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) { - self.mu.RLock() - defer self.mu.RUnlock() - - // Retrieve all the pending transactions and sort by account and by nonce - pending := make(map[common.Address]types.Transactions) - for _, tx := range self.pending { - account, _ := types.Sender(self.signer, tx) - pending[account] = append(pending[account], tx) - } - // There are no queued transactions in a light pool, just return an empty map - queued := make(map[common.Address]types.Transactions) - return pending, queued -} - -// RemoveTransactions removes all given transactions from the pool. -func (self *TxPool) RemoveTransactions(txs types.Transactions) { - self.mu.Lock() - defer self.mu.Unlock() - - var hashes []common.Hash - batch := self.chainDb.NewBatch() - for _, tx := range txs { - hash := tx.Hash() - delete(self.pending, hash) - batch.Delete(hash.Bytes()) - hashes = append(hashes, hash) - } - batch.Write() - self.relay.Discard(hashes) -} - -// RemoveTx removes the transaction with the given hash from the pool. -func (pool *TxPool) RemoveTx(hash common.Hash) { - pool.mu.Lock() - defer pool.mu.Unlock() - // delete from pending pool - delete(pool.pending, hash) - pool.chainDb.Delete(hash[:]) - pool.relay.Discard([]common.Hash{hash}) -} diff --git a/light/txpool_test.go b/light/txpool_test.go deleted file mode 100644 index ccbd83a943..0000000000 --- a/light/txpool_test.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package light - -import ( - "context" - "math" - "math/big" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/consensus/ethash" - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/core/vm" - "github.com/ethereum/go-ethereum/ethdb" - "github.com/ethereum/go-ethereum/params" -) - -type testTxRelay struct { - send, discard, mined chan int -} - -func (self *testTxRelay) Send(txs types.Transactions) { - self.send <- len(txs) -} - -func (self *testTxRelay) NewHead(head common.Hash, mined []common.Hash, rollback []common.Hash) { - m := len(mined) - if m != 0 { - self.mined <- m - } -} - -func (self *testTxRelay) Discard(hashes []common.Hash) { - self.discard <- len(hashes) -} - -const poolTestTxs = 1000 -const poolTestBlocks = 100 - -// test tx 0..n-1 -var testTx [poolTestTxs]*types.Transaction - -// txs sent before block i -func sentTx(i int) int { - return int(math.Pow(float64(i)/float64(poolTestBlocks), 0.9) * poolTestTxs) -} - -// txs included in block i or before that (minedTx(i) <= sentTx(i)) -func minedTx(i int) int { - return int(math.Pow(float64(i)/float64(poolTestBlocks), 1.1) * poolTestTxs) -} - -func txPoolTestChainGen(i int, block *core.BlockGen) { - s := minedTx(i) - e := minedTx(i + 1) - for i := s; i < e; i++ { - block.AddTx(testTx[i]) - } -} - -func TestTxPool(t *testing.T) { - for i := range testTx { - testTx[i], _ = types.SignTx(types.NewTransaction(uint64(i), acc1Addr, big.NewInt(10000), params.TxGas, nil, nil), types.HomesteadSigner{}, testBankKey) - } - - var ( - sdb = ethdb.NewMemDatabase() - ldb = ethdb.NewMemDatabase() - gspec = core.Genesis{Alloc: core.GenesisAlloc{testBankAddress: {Balance: testBankFunds}}} - genesis = gspec.MustCommit(sdb) - ) - gspec.MustCommit(ldb) - // Assemble the test environment - blockchain, _ := core.NewBlockChain(sdb, nil, params.TestChainConfig, ethash.NewFullFaker(), vm.Config{}) - gchain, _ := core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), sdb, poolTestBlocks, txPoolTestChainGen) - if _, err := blockchain.InsertChain(gchain); err != nil { - panic(err) - } - - odr := &testOdr{sdb: sdb, ldb: ldb} - relay := &testTxRelay{ - send: make(chan int, 1), - discard: make(chan int, 1), - mined: make(chan int, 1), - } - lightchain, _ := NewLightChain(odr, params.TestChainConfig, ethash.NewFullFaker()) - txPermanent = 50 - pool := NewTxPool(params.TestChainConfig, lightchain, relay) - ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second) - defer cancel() - - for ii, block := range gchain { - i := ii + 1 - s := sentTx(i - 1) - e := sentTx(i) - for i := s; i < e; i++ { - pool.Add(ctx, testTx[i]) - got := <-relay.send - exp := 1 - if got != exp { - t.Errorf("relay.Send expected len = %d, got %d", exp, got) - } - } - - if _, err := lightchain.InsertHeaderChain([]*types.Header{block.Header()}, 1); err != nil { - panic(err) - } - - got := <-relay.mined - exp := minedTx(i) - minedTx(i-1) - if got != exp { - t.Errorf("relay.NewHead expected len(mined) = %d, got %d", exp, got) - } - - exp = 0 - if i > int(txPermanent)+1 { - exp = minedTx(i-int(txPermanent)-1) - minedTx(i-int(txPermanent)-2) - } - if exp != 0 { - got = <-relay.discard - if got != exp { - t.Errorf("relay.Discard expected len = %d, got %d", exp, got) - } - } - } -} diff --git a/mobile/accounts.go b/mobile/accounts.go deleted file mode 100644 index 4d979bffff..0000000000 --- a/mobile/accounts.go +++ /dev/null @@ -1,221 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the accounts package to support client side key -// management on mobile platforms. - -package geth - -import ( - "errors" - "time" - - "github.com/ethereum/go-ethereum/accounts" - "github.com/ethereum/go-ethereum/accounts/keystore" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -const ( - // StandardScryptN is the N parameter of Scrypt encryption algorithm, using 256MB - // memory and taking approximately 1s CPU time on a modern processor. - StandardScryptN = int(keystore.StandardScryptN) - - // StandardScryptP is the P parameter of Scrypt encryption algorithm, using 256MB - // memory and taking approximately 1s CPU time on a modern processor. - StandardScryptP = int(keystore.StandardScryptP) - - // LightScryptN is the N parameter of Scrypt encryption algorithm, using 4MB - // memory and taking approximately 100ms CPU time on a modern processor. - LightScryptN = int(keystore.LightScryptN) - - // LightScryptP is the P parameter of Scrypt encryption algorithm, using 4MB - // memory and taking approximately 100ms CPU time on a modern processor. - LightScryptP = int(keystore.LightScryptP) -) - -// Account represents a stored key. -type Account struct{ account accounts.Account } - -// Accounts represents a slice of accounts. -type Accounts struct{ accounts []accounts.Account } - -// Size returns the number of accounts in the slice. -func (a *Accounts) Size() int { - return len(a.accounts) -} - -// Get returns the account at the given index from the slice. -func (a *Accounts) Get(index int) (account *Account, _ error) { - if index < 0 || index >= len(a.accounts) { - return nil, errors.New("index out of bounds") - } - return &Account{a.accounts[index]}, nil -} - -// Set sets the account at the given index in the slice. -func (a *Accounts) Set(index int, account *Account) error { - if index < 0 || index >= len(a.accounts) { - return errors.New("index out of bounds") - } - a.accounts[index] = account.account - return nil -} - -// GetAddress retrieves the address associated with the account. -func (a *Account) GetAddress() *Address { - return &Address{a.account.Address} -} - -// GetURL retrieves the canonical URL of the account. -func (a *Account) GetURL() string { - return a.account.URL.String() -} - -// KeyStore manages a key storage directory on disk. -type KeyStore struct{ keystore *keystore.KeyStore } - -// NewKeyStore creates a keystore for the given directory. -func NewKeyStore(keydir string, scryptN, scryptP int) *KeyStore { - return &KeyStore{keystore: keystore.NewKeyStore(keydir, scryptN, scryptP)} -} - -// HasAddress reports whether a key with the given address is present. -func (ks *KeyStore) HasAddress(address *Address) bool { - return ks.keystore.HasAddress(address.address) -} - -// GetAccounts returns all key files present in the directory. -func (ks *KeyStore) GetAccounts() *Accounts { - return &Accounts{ks.keystore.Accounts()} -} - -// DeleteAccount deletes the key matched by account if the passphrase is correct. -// If a contains no filename, the address must match a unique key. -func (ks *KeyStore) DeleteAccount(account *Account, passphrase string) error { - return ks.keystore.Delete(account.account, passphrase) -} - -// SignHash calculates a ECDSA signature for the given hash. The produced signature -// is in the [R || S || V] format where V is 0 or 1. -func (ks *KeyStore) SignHash(address *Address, hash []byte) (signature []byte, _ error) { - return ks.keystore.SignHash(accounts.Account{Address: address.address}, common.CopyBytes(hash)) -} - -// SignTx signs the given transaction with the requested account. -func (ks *KeyStore) SignTx(account *Account, tx *Transaction, chainID *BigInt) (*Transaction, error) { - if chainID == nil { // Null passed from mobile app - chainID = new(BigInt) - } - signed, err := ks.keystore.SignTx(account.account, tx.tx, chainID.bigint) - if err != nil { - return nil, err - } - return &Transaction{signed}, nil -} - -// SignHashPassphrase signs hash if the private key matching the given address can -// be decrypted with the given passphrase. The produced signature is in the -// [R || S || V] format where V is 0 or 1. -func (ks *KeyStore) SignHashPassphrase(account *Account, passphrase string, hash []byte) (signature []byte, _ error) { - return ks.keystore.SignHashWithPassphrase(account.account, passphrase, common.CopyBytes(hash)) -} - -// SignTxPassphrase signs the transaction if the private key matching the -// given address can be decrypted with the given passphrase. -func (ks *KeyStore) SignTxPassphrase(account *Account, passphrase string, tx *Transaction, chainID *BigInt) (*Transaction, error) { - if chainID == nil { // Null passed from mobile app - chainID = new(BigInt) - } - signed, err := ks.keystore.SignTxWithPassphrase(account.account, passphrase, tx.tx, chainID.bigint) - if err != nil { - return nil, err - } - return &Transaction{signed}, nil -} - -// Unlock unlocks the given account indefinitely. -func (ks *KeyStore) Unlock(account *Account, passphrase string) error { - return ks.keystore.TimedUnlock(account.account, passphrase, 0) -} - -// Lock removes the private key with the given address from memory. -func (ks *KeyStore) Lock(address *Address) error { - return ks.keystore.Lock(address.address) -} - -// TimedUnlock unlocks the given account with the passphrase. The account stays -// unlocked for the duration of timeout (nanoseconds). A timeout of 0 unlocks the -// account until the program exits. The account must match a unique key file. -// -// If the account address is already unlocked for a duration, TimedUnlock extends or -// shortens the active unlock timeout. If the address was previously unlocked -// indefinitely the timeout is not altered. -func (ks *KeyStore) TimedUnlock(account *Account, passphrase string, timeout int64) error { - return ks.keystore.TimedUnlock(account.account, passphrase, time.Duration(timeout)) -} - -// NewAccount generates a new key and stores it into the key directory, -// encrypting it with the passphrase. -func (ks *KeyStore) NewAccount(passphrase string) (*Account, error) { - account, err := ks.keystore.NewAccount(passphrase) - if err != nil { - return nil, err - } - return &Account{account}, nil -} - -// UpdateAccount changes the passphrase of an existing account. -func (ks *KeyStore) UpdateAccount(account *Account, passphrase, newPassphrase string) error { - return ks.keystore.Update(account.account, passphrase, newPassphrase) -} - -// ExportKey exports as a JSON key, encrypted with newPassphrase. -func (ks *KeyStore) ExportKey(account *Account, passphrase, newPassphrase string) (key []byte, _ error) { - return ks.keystore.Export(account.account, passphrase, newPassphrase) -} - -// ImportKey stores the given encrypted JSON key into the key directory. -func (ks *KeyStore) ImportKey(keyJSON []byte, passphrase, newPassphrase string) (account *Account, _ error) { - acc, err := ks.keystore.Import(common.CopyBytes(keyJSON), passphrase, newPassphrase) - if err != nil { - return nil, err - } - return &Account{acc}, nil -} - -// ImportECDSAKey stores the given encrypted JSON key into the key directory. -func (ks *KeyStore) ImportECDSAKey(key []byte, passphrase string) (account *Account, _ error) { - privkey, err := crypto.ToECDSA(common.CopyBytes(key)) - if err != nil { - return nil, err - } - acc, err := ks.keystore.ImportECDSA(privkey, passphrase) - if err != nil { - return nil, err - } - return &Account{acc}, nil -} - -// ImportPreSaleKey decrypts the given Ethereum presale wallet and stores -// a key file in the key directory. The key file is encrypted with the same passphrase. -func (ks *KeyStore) ImportPreSaleKey(keyJSON []byte, passphrase string) (ccount *Account, _ error) { - account, err := ks.keystore.ImportPreSaleKey(common.CopyBytes(keyJSON), passphrase) - if err != nil { - return nil, err - } - return &Account{account}, nil -} diff --git a/mobile/android_test.go b/mobile/android_test.go deleted file mode 100644 index 3d3bd66d08..0000000000 --- a/mobile/android_test.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package geth - -import ( - "io/ioutil" - "os" - "os/exec" - "path/filepath" - "runtime" - "testing" - "time" - - "github.com/ethereum/go-ethereum/internal/build" -) - -// androidTestClass is a Java class to do some lightweight tests against the Android -// bindings. The goal is not to test each individual functionality, rather just to -// catch breaking API and/or implementation changes. -const androidTestClass = ` -package go; - -import android.test.InstrumentationTestCase; -import android.test.MoreAsserts; - -import java.math.BigInteger; -import java.util.Arrays; - -import org.ethereum.geth.*; - -public class AndroidTest extends InstrumentationTestCase { - public AndroidTest() {} - - public void testAccountManagement() { - // Create an encrypted keystore with light crypto parameters. - KeyStore ks = new KeyStore(getInstrumentation().getContext().getFilesDir() + "/keystore", Geth.LightScryptN, Geth.LightScryptP); - - try { - // Create a new account with the specified encryption passphrase. - Account newAcc = ks.newAccount("Creation password"); - - // Export the newly created account with a different passphrase. The returned - // data from this method invocation is a JSON encoded, encrypted key-file. - byte[] jsonAcc = ks.exportKey(newAcc, "Creation password", "Export password"); - - // Update the passphrase on the account created above inside the local keystore. - ks.updateAccount(newAcc, "Creation password", "Update password"); - - // Delete the account updated above from the local keystore. - ks.deleteAccount(newAcc, "Update password"); - - // Import back the account we've exported (and then deleted) above with yet - // again a fresh passphrase. - Account impAcc = ks.importKey(jsonAcc, "Export password", "Import password"); - - // Create a new account to sign transactions with - Account signer = ks.newAccount("Signer password"); - - Transaction tx = new Transaction( - 1, new Address("0x0000000000000000000000000000000000000000"), - new BigInt(0), 0, new BigInt(1), null); // Random empty transaction - BigInt chain = new BigInt(1); // Chain identifier of the main net - - // Sign a transaction with a single authorization - Transaction signed = ks.signTxPassphrase(signer, "Signer password", tx, chain); - - // Sign a transaction with multiple manually cancelled authorizations - ks.unlock(signer, "Signer password"); - signed = ks.signTx(signer, tx, chain); - ks.lock(signer.getAddress()); - - // Sign a transaction with multiple automatically cancelled authorizations - ks.timedUnlock(signer, "Signer password", 1000000000); - signed = ks.signTx(signer, tx, chain); - } catch (Exception e) { - fail(e.toString()); - } - } - - public void testInprocNode() { - Context ctx = new Context(); - - try { - // Start up a new inprocess node - Node node = new Node(getInstrumentation().getContext().getFilesDir() + "/.ethereum", new NodeConfig()); - node.start(); - - // Retrieve some data via function calls (we don't really care about the results) - NodeInfo info = node.getNodeInfo(); - info.getName(); - info.getListenerAddress(); - info.getProtocols(); - - // Retrieve some data via the APIs (we don't really care about the results) - EthereumClient ec = node.getEthereumClient(); - ec.getBlockByNumber(ctx, -1).getNumber(); - - NewHeadHandler handler = new NewHeadHandler() { - @Override public void onError(String error) {} - @Override public void onNewHead(final Header header) {} - }; - ec.subscribeNewHead(ctx, handler, 16); - } catch (Exception e) { - fail(e.toString()); - } - } - - // Tests that recovering transaction signers works for both Homestead and EIP155 - // signatures too. Regression test for go-ethereum issue #14599. - public void testIssue14599() { - try { - byte[] preEIP155RLP = new BigInteger("f901fc8032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b561ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884", 16).toByteArray(); - preEIP155RLP = Arrays.copyOfRange(preEIP155RLP, 1, preEIP155RLP.length); - - byte[] postEIP155RLP = new BigInteger("f86b80847735940082520894ef5bbb9bba2e1ca69ef81b23a8727d889f3ef0a1880de0b6b3a7640000802ba06fef16c44726a102e6d55a651740636ef8aec6df3ebf009e7b0c1f29e4ac114aa057e7fbc69760b522a78bb568cfc37a58bfdcf6ea86cb8f9b550263f58074b9cc", 16).toByteArray(); - postEIP155RLP = Arrays.copyOfRange(postEIP155RLP, 1, postEIP155RLP.length); - - Transaction preEIP155 = new Transaction(preEIP155RLP); - Transaction postEIP155 = new Transaction(postEIP155RLP); - - preEIP155.getFrom(null); // Homestead should accept homestead - preEIP155.getFrom(new BigInt(4)); // EIP155 should accept homestead (missing chain ID) - postEIP155.getFrom(new BigInt(4)); // EIP155 should accept EIP 155 - - try { - postEIP155.getFrom(null); - fail("EIP155 transaction accepted by Homestead"); - } catch (Exception e) {} - } catch (Exception e) { - fail(e.toString()); - } - } -} -` - -// TestAndroid runs the Android java test class specified above. -// -// This requires the gradle command in PATH and the Android SDK whose path is available -// through ANDROID_HOME environment variable. To successfully run the tests, an Android -// device must also be available with debugging enabled. -// -// This method has been adapted from golang.org/x/mobile/bind/java/seq_test.go/runTest -func TestAndroid(t *testing.T) { - // Skip tests on Windows altogether - if runtime.GOOS == "windows" { - t.Skip("cannot test Android bindings on Windows, skipping") - } - // Make sure all the Android tools are installed - if _, err := exec.Command("which", "gradle").CombinedOutput(); err != nil { - t.Skip("command gradle not found, skipping") - } - if sdk := os.Getenv("ANDROID_HOME"); sdk == "" { - // Android SDK not explicitly given, try to auto-resolve - autopath := filepath.Join(os.Getenv("HOME"), "Android", "Sdk") - if _, err := os.Stat(autopath); err != nil { - t.Skip("ANDROID_HOME environment var not set, skipping") - } - os.Setenv("ANDROID_HOME", autopath) - } - if _, err := exec.Command("which", "gomobile").CombinedOutput(); err != nil { - t.Log("gomobile missing, installing it...") - if out, err := exec.Command("go", "get", "golang.org/x/mobile/cmd/gomobile").CombinedOutput(); err != nil { - t.Fatalf("install failed: %v\n%s", err, string(out)) - } - t.Log("initializing gomobile...") - start := time.Now() - if _, err := exec.Command("gomobile", "init").CombinedOutput(); err != nil { - t.Fatalf("initialization failed: %v", err) - } - t.Logf("initialization took %v", time.Since(start)) - } - // Create and switch to a temporary workspace - workspace, err := ioutil.TempDir("", "geth-android-") - if err != nil { - t.Fatalf("failed to create temporary workspace: %v", err) - } - defer os.RemoveAll(workspace) - - pwd, err := os.Getwd() - if err != nil { - t.Fatalf("failed to get current working directory: %v", err) - } - if err := os.Chdir(workspace); err != nil { - t.Fatalf("failed to switch to temporary workspace: %v", err) - } - defer os.Chdir(pwd) - - // Create the skeleton of the Android project - for _, dir := range []string{"src/main", "src/androidTest/java/org/ethereum/gethtest", "libs"} { - err = os.MkdirAll(dir, os.ModePerm) - if err != nil { - t.Fatal(err) - } - } - // Generate the mobile bindings for Geth and add the tester class - gobind := exec.Command("gomobile", "bind", "-javapkg", "org.ethereum", "github.com/ethereum/go-ethereum/mobile") - if output, err := gobind.CombinedOutput(); err != nil { - t.Logf("%s", output) - t.Fatalf("failed to run gomobile bind: %v", err) - } - build.CopyFile(filepath.Join("libs", "geth.aar"), "geth.aar", os.ModePerm) - - if err = ioutil.WriteFile(filepath.Join("src", "androidTest", "java", "org", "ethereum", "gethtest", "AndroidTest.java"), []byte(androidTestClass), os.ModePerm); err != nil { - t.Fatalf("failed to write Android test class: %v", err) - } - // Finish creating the project and run the tests via gradle - if err = ioutil.WriteFile(filepath.Join("src", "main", "AndroidManifest.xml"), []byte(androidManifest), os.ModePerm); err != nil { - t.Fatalf("failed to write Android manifest: %v", err) - } - if err = ioutil.WriteFile("build.gradle", []byte(gradleConfig), os.ModePerm); err != nil { - t.Fatalf("failed to write gradle build file: %v", err) - } - if output, err := exec.Command("gradle", "connectedAndroidTest").CombinedOutput(); err != nil { - t.Logf("%s", output) - t.Errorf("failed to run gradle test: %v", err) - } -} - -const androidManifest = ` - - - -` - -const gradleConfig = `buildscript { - repositories { - jcenter() - } - dependencies { - classpath 'com.android.tools.build:gradle:2.2.3' - } -} -allprojects { - repositories { jcenter() } -} -apply plugin: 'com.android.library' -android { - compileSdkVersion 'android-19' - buildToolsVersion '21.1.2' - defaultConfig { minSdkVersion 15 } -} -repositories { - flatDir { dirs 'libs' } -} -dependencies { - compile 'com.android.support:appcompat-v7:19.0.0' - compile(name: "geth", ext: "aar") -} -` diff --git a/mobile/big.go b/mobile/big.go deleted file mode 100644 index dd7b158786..0000000000 --- a/mobile/big.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the math/big package. - -package geth - -import ( - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/common" -) - -// A BigInt represents a signed multi-precision integer. -type BigInt struct { - bigint *big.Int -} - -// NewBigInt allocates and returns a new BigInt set to x. -func NewBigInt(x int64) *BigInt { - return &BigInt{big.NewInt(x)} -} - -// GetBytes returns the absolute value of x as a big-endian byte slice. -func (bi *BigInt) GetBytes() []byte { - return bi.bigint.Bytes() -} - -// String returns the value of x as a formatted decimal string. -func (bi *BigInt) String() string { - return bi.bigint.String() -} - -// GetInt64 returns the int64 representation of x. If x cannot be represented in -// an int64, the result is undefined. -func (bi *BigInt) GetInt64() int64 { - return bi.bigint.Int64() -} - -// SetBytes interprets buf as the bytes of a big-endian unsigned integer and sets -// the big int to that value. -func (bi *BigInt) SetBytes(buf []byte) { - bi.bigint.SetBytes(common.CopyBytes(buf)) -} - -// SetInt64 sets the big int to x. -func (bi *BigInt) SetInt64(x int64) { - bi.bigint.SetInt64(x) -} - -// Sign returns: -// -// -1 if x < 0 -// 0 if x == 0 -// +1 if x > 0 -// -func (bi *BigInt) Sign() int { - return bi.bigint.Sign() -} - -// SetString sets the big int to x. -// -// The string prefix determines the actual conversion base. A prefix of "0x" or -// "0X" selects base 16; the "0" prefix selects base 8, and a "0b" or "0B" prefix -// selects base 2. Otherwise the selected base is 10. -func (bi *BigInt) SetString(x string, base int) { - bi.bigint.SetString(x, base) -} - -// BigInts represents a slice of big ints. -type BigInts struct{ bigints []*big.Int } - -// Size returns the number of big ints in the slice. -func (bi *BigInts) Size() int { - return len(bi.bigints) -} - -// Get returns the bigint at the given index from the slice. -func (bi *BigInts) Get(index int) (bigint *BigInt, _ error) { - if index < 0 || index >= len(bi.bigints) { - return nil, errors.New("index out of bounds") - } - return &BigInt{bi.bigints[index]}, nil -} - -// Set sets the big int at the given index in the slice. -func (bi *BigInts) Set(index int, bigint *BigInt) error { - if index < 0 || index >= len(bi.bigints) { - return errors.New("index out of bounds") - } - bi.bigints[index] = bigint.bigint - return nil -} - -// GetString returns the value of x as a formatted string in some number base. -func (bi *BigInt) GetString(base int) string { - return bi.bigint.Text(base) -} diff --git a/mobile/bind.go b/mobile/bind.go deleted file mode 100644 index d6e621a258..0000000000 --- a/mobile/bind.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the bind package. - -package geth - -import ( - "math/big" - "strings" - - "github.com/ethereum/go-ethereum/accounts/abi" - "github.com/ethereum/go-ethereum/accounts/abi/bind" - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" -) - -// Signer is an interaface defining the callback when a contract requires a -// method to sign the transaction before submission. -type Signer interface { - Sign(*Address, *Transaction) (tx *Transaction, _ error) -} - -type signer struct { - sign bind.SignerFn -} - -func (s *signer) Sign(addr *Address, unsignedTx *Transaction) (signedTx *Transaction, _ error) { - sig, err := s.sign(types.HomesteadSigner{}, addr.address, unsignedTx.tx) - if err != nil { - return nil, err - } - return &Transaction{sig}, nil -} - -// CallOpts is the collection of options to fine tune a contract call request. -type CallOpts struct { - opts bind.CallOpts -} - -// NewCallOpts creates a new option set for contract calls. -func NewCallOpts() *CallOpts { - return new(CallOpts) -} - -func (opts *CallOpts) IsPending() bool { return opts.opts.Pending } -func (opts *CallOpts) GetGasLimit() int64 { return 0 /* TODO(karalabe) */ } - -// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) -// Even then it's awkward to unpack the subtleties of a Go context out to Java. -// func (opts *CallOpts) GetContext() *Context { return &Context{opts.opts.Context} } - -func (opts *CallOpts) SetPending(pending bool) { opts.opts.Pending = pending } -func (opts *CallOpts) SetGasLimit(limit int64) { /* TODO(karalabe) */ } -func (opts *CallOpts) SetContext(context *Context) { opts.opts.Context = context.context } - -// TransactOpts is the collection of authorization data required to create a -// valid Ethereum transaction. -type TransactOpts struct { - opts bind.TransactOpts -} - -func (opts *TransactOpts) GetFrom() *Address { return &Address{opts.opts.From} } -func (opts *TransactOpts) GetNonce() int64 { return opts.opts.Nonce.Int64() } -func (opts *TransactOpts) GetValue() *BigInt { return &BigInt{opts.opts.Value} } -func (opts *TransactOpts) GetGasPrice() *BigInt { return &BigInt{opts.opts.GasPrice} } -func (opts *TransactOpts) GetGasLimit() int64 { return int64(opts.opts.GasLimit) } - -// GetSigner cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) -// func (opts *TransactOpts) GetSigner() Signer { return &signer{opts.opts.Signer} } - -// GetContext cannot be reliably implemented without identity preservation (https://github.com/golang/go/issues/16876) -// Even then it's awkward to unpack the subtleties of a Go context out to Java. -//func (opts *TransactOpts) GetContext() *Context { return &Context{opts.opts.Context} } - -func (opts *TransactOpts) SetFrom(from *Address) { opts.opts.From = from.address } -func (opts *TransactOpts) SetNonce(nonce int64) { opts.opts.Nonce = big.NewInt(nonce) } -func (opts *TransactOpts) SetSigner(s Signer) { - opts.opts.Signer = func(signer types.Signer, addr common.Address, tx *types.Transaction) (*types.Transaction, error) { - sig, err := s.Sign(&Address{addr}, &Transaction{tx}) - if err != nil { - return nil, err - } - return sig.tx, nil - } -} -func (opts *TransactOpts) SetValue(value *BigInt) { opts.opts.Value = value.bigint } -func (opts *TransactOpts) SetGasPrice(price *BigInt) { opts.opts.GasPrice = price.bigint } -func (opts *TransactOpts) SetGasLimit(limit int64) { opts.opts.GasLimit = uint64(limit) } -func (opts *TransactOpts) SetContext(context *Context) { opts.opts.Context = context.context } - -// BoundContract is the base wrapper object that reflects a contract on the -// Ethereum network. It contains a collection of methods that are used by the -// higher level contract bindings to operate. -type BoundContract struct { - contract *bind.BoundContract - address common.Address - deployer *types.Transaction -} - -// DeployContract deploys a contract onto the Ethereum blockchain and binds the -// deployment address with a wrapper. -func DeployContract(opts *TransactOpts, abiJSON string, bytecode []byte, client *EthereumClient, args *Interfaces) (contract *BoundContract, _ error) { - // Deploy the contract to the network - parsed, err := abi.JSON(strings.NewReader(abiJSON)) - if err != nil { - return nil, err - } - addr, tx, bound, err := bind.DeployContract(&opts.opts, parsed, common.CopyBytes(bytecode), client.client, args.objects...) - if err != nil { - return nil, err - } - return &BoundContract{ - contract: bound, - address: addr, - deployer: tx, - }, nil -} - -// BindContract creates a low level contract interface through which calls and -// transactions may be made through. -func BindContract(address *Address, abiJSON string, client *EthereumClient) (contract *BoundContract, _ error) { - parsed, err := abi.JSON(strings.NewReader(abiJSON)) - if err != nil { - return nil, err - } - return &BoundContract{ - contract: bind.NewBoundContract(address.address, parsed, client.client, client.client, client.client), - address: address.address, - }, nil -} - -func (c *BoundContract) GetAddress() *Address { return &Address{c.address} } -func (c *BoundContract) GetDeployer() *Transaction { - if c.deployer == nil { - return nil - } - return &Transaction{c.deployer} -} - -// Call invokes the (constant) contract method with params as input values and -// sets the output to result. -func (c *BoundContract) Call(opts *CallOpts, out *Interfaces, method string, args *Interfaces) error { - if len(out.objects) == 1 { - result := out.objects[0] - if err := c.contract.Call(&opts.opts, result, method, args.objects...); err != nil { - return err - } - out.objects[0] = result - } else { - results := make([]interface{}, len(out.objects)) - copy(results, out.objects) - if err := c.contract.Call(&opts.opts, &results, method, args.objects...); err != nil { - return err - } - copy(out.objects, results) - } - return nil -} - -// Transact invokes the (paid) contract method with params as input values. -func (c *BoundContract) Transact(opts *TransactOpts, method string, args *Interfaces) (tx *Transaction, _ error) { - rawTx, err := c.contract.Transact(&opts.opts, method, args.objects...) - if err != nil { - return nil, err - } - return &Transaction{rawTx}, nil -} - -// Transfer initiates a plain transaction to move funds to the contract, calling -// its default method if one is available. -func (c *BoundContract) Transfer(opts *TransactOpts) (tx *Transaction, _ error) { - rawTx, err := c.contract.Transfer(&opts.opts) - if err != nil { - return nil, err - } - return &Transaction{rawTx}, nil -} diff --git a/mobile/common.go b/mobile/common.go deleted file mode 100644 index 047d8e1f65..0000000000 --- a/mobile/common.go +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the common package. - -package geth - -import ( - "encoding/hex" - "errors" - "fmt" - "strings" - - "github.com/ethereum/go-ethereum/common" -) - -// Hash represents the 32 byte Keccak256 hash of arbitrary data. -type Hash struct { - hash common.Hash -} - -// NewHashFromBytes converts a slice of bytes to a hash value. -func NewHashFromBytes(binary []byte) (hash *Hash, _ error) { - h := new(Hash) - if err := h.SetBytes(common.CopyBytes(binary)); err != nil { - return nil, err - } - return h, nil -} - -// NewHashFromHex converts a hex string to a hash value. -func NewHashFromHex(hex string) (hash *Hash, _ error) { - h := new(Hash) - if err := h.SetHex(hex); err != nil { - return nil, err - } - return h, nil -} - -// SetBytes sets the specified slice of bytes as the hash value. -func (h *Hash) SetBytes(hash []byte) error { - if length := len(hash); length != common.HashLength { - return fmt.Errorf("invalid hash length: %v != %v", length, common.HashLength) - } - copy(h.hash[:], hash) - return nil -} - -// GetBytes retrieves the byte representation of the hash. -func (h *Hash) GetBytes() []byte { - return h.hash[:] -} - -// SetHex sets the specified hex string as the hash value. -func (h *Hash) SetHex(hash string) error { - hash = strings.ToLower(hash) - if len(hash) >= 2 && hash[:2] == "0x" { - hash = hash[2:] - } - if length := len(hash); length != 2*common.HashLength { - return fmt.Errorf("invalid hash hex length: %v != %v", length, 2*common.HashLength) - } - bin, err := hex.DecodeString(hash) - if err != nil { - return err - } - copy(h.hash[:], bin) - return nil -} - -// GetHex retrieves the hex string representation of the hash. -func (h *Hash) GetHex() string { - return h.hash.Hex() -} - -// Hashes represents a slice of hashes. -type Hashes struct{ hashes []common.Hash } - -// NewHashes creates a slice of uninitialized Hashes. -func NewHashes(size int) *Hashes { - return &Hashes{ - hashes: make([]common.Hash, size), - } -} - -// NewHashesEmpty creates an empty slice of Hashes values. -func NewHashesEmpty() *Hashes { - return NewHashes(0) -} - -// Size returns the number of hashes in the slice. -func (h *Hashes) Size() int { - return len(h.hashes) -} - -// Get returns the hash at the given index from the slice. -func (h *Hashes) Get(index int) (hash *Hash, _ error) { - if index < 0 || index >= len(h.hashes) { - return nil, errors.New("index out of bounds") - } - return &Hash{h.hashes[index]}, nil -} - -// Set sets the Hash at the given index in the slice. -func (h *Hashes) Set(index int, hash *Hash) error { - if index < 0 || index >= len(h.hashes) { - return errors.New("index out of bounds") - } - h.hashes[index] = hash.hash - return nil -} - -// Append adds a new Hash element to the end of the slice. -func (h *Hashes) Append(hash *Hash) { - h.hashes = append(h.hashes, hash.hash) -} - -// Address represents the 20 byte address of an Ethereum account. -type Address struct { - address common.Address -} - -// NewAddressFromBytes converts a slice of bytes to a hash value. -func NewAddressFromBytes(binary []byte) (address *Address, _ error) { - a := new(Address) - if err := a.SetBytes(common.CopyBytes(binary)); err != nil { - return nil, err - } - return a, nil -} - -// NewAddressFromHex converts a hex string to a address value. -func NewAddressFromHex(hex string) (address *Address, _ error) { - a := new(Address) - if err := a.SetHex(hex); err != nil { - return nil, err - } - return a, nil -} - -// SetBytes sets the specified slice of bytes as the address value. -func (a *Address) SetBytes(address []byte) error { - if length := len(address); length != common.AddressLength { - return fmt.Errorf("invalid address length: %v != %v", length, common.AddressLength) - } - copy(a.address[:], address) - return nil -} - -// GetBytes retrieves the byte representation of the address. -func (a *Address) GetBytes() []byte { - return a.address[:] -} - -// SetHex sets the specified hex string as the address value. -func (a *Address) SetHex(address string) error { - address = strings.ToLower(address) - if len(address) >= 2 && address[:2] == "0x" { - address = address[2:] - } - if length := len(address); length != 2*common.AddressLength { - return fmt.Errorf("invalid address hex length: %v != %v", length, 2*common.AddressLength) - } - bin, err := hex.DecodeString(address) - if err != nil { - return err - } - copy(a.address[:], bin) - return nil -} - -// GetHex retrieves the hex string representation of the address. -func (a *Address) GetHex() string { - return a.address.Hex() -} - -// Addresses represents a slice of addresses. -type Addresses struct{ addresses []common.Address } - -// NewAddresses creates a slice of uninitialized addresses. -func NewAddresses(size int) *Addresses { - return &Addresses{ - addresses: make([]common.Address, size), - } -} - -// NewAddressesEmpty creates an empty slice of Addresses values. -func NewAddressesEmpty() *Addresses { - return NewAddresses(0) -} - -// Size returns the number of addresses in the slice. -func (a *Addresses) Size() int { - return len(a.addresses) -} - -// Get returns the address at the given index from the slice. -func (a *Addresses) Get(index int) (address *Address, _ error) { - if index < 0 || index >= len(a.addresses) { - return nil, errors.New("index out of bounds") - } - return &Address{a.addresses[index]}, nil -} - -// Set sets the address at the given index in the slice. -func (a *Addresses) Set(index int, address *Address) error { - if index < 0 || index >= len(a.addresses) { - return errors.New("index out of bounds") - } - a.addresses[index] = address.address - return nil -} - -// Append adds a new address element to the end of the slice. -func (a *Addresses) Append(address *Address) { - a.addresses = append(a.addresses, address.address) -} diff --git a/mobile/context.go b/mobile/context.go deleted file mode 100644 index f1fff90114..0000000000 --- a/mobile/context.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the golang.org/x/net/context package to support -// client side context management on mobile platforms. - -package geth - -import ( - "context" - "time" -) - -// Context carries a deadline, a cancelation signal, and other values across API -// boundaries. -type Context struct { - context context.Context - cancel context.CancelFunc -} - -// NewContext returns a non-nil, empty Context. It is never canceled, has no -// values, and has no deadline. It is typically used by the main function, -// initialization, and tests, and as the top-level Context for incoming requests. -func NewContext() *Context { - return &Context{ - context: context.Background(), - } -} - -// WithCancel returns a copy of the original context with cancellation mechanism -// included. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func (c *Context) WithCancel() *Context { - child, cancel := context.WithCancel(c.context) - return &Context{ - context: child, - cancel: cancel, - } -} - -// WithDeadline returns a copy of the original context with the deadline adjusted -// to be no later than the specified time. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func (c *Context) WithDeadline(sec int64, nsec int64) *Context { - child, cancel := context.WithDeadline(c.context, time.Unix(sec, nsec)) - return &Context{ - context: child, - cancel: cancel, - } -} - -// WithTimeout returns a copy of the original context with the deadline adjusted -// to be no later than now + the duration specified. -// -// Canceling this context releases resources associated with it, so code should -// call cancel as soon as the operations running in this Context complete. -func (c *Context) WithTimeout(nsec int64) *Context { - child, cancel := context.WithTimeout(c.context, time.Duration(nsec)) - return &Context{ - context: child, - cancel: cancel, - } -} diff --git a/mobile/discover.go b/mobile/discover.go deleted file mode 100644 index 9b3c93ccd9..0000000000 --- a/mobile/discover.go +++ /dev/null @@ -1,104 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the accounts package to support client side enode -// management on mobile platforms. - -package geth - -import ( - "errors" - - "github.com/ethereum/go-ethereum/p2p/discv5" -) - -// Enode represents a host on the network. -type Enode struct { - node *discv5.Node -} - -// NewEnode parses a node designator. -// -// There are two basic forms of node designators -// - incomplete nodes, which only have the public key (node ID) -// - complete nodes, which contain the public key and IP/Port information -// -// For incomplete nodes, the designator must look like one of these -// -// enode:// -// -// -// For complete nodes, the node ID is encoded in the username portion -// of the URL, separated from the host by an @ sign. The hostname can -// only be given as an IP address, DNS domain names are not allowed. -// The port in the host name section is the TCP listening port. If the -// TCP and UDP (discovery) ports differ, the UDP port is specified as -// query parameter "discport". -// -// In the following example, the node URL describes -// a node with IP address 10.3.58.6, TCP listening port 30303 -// and UDP discovery port 30301. -// -// enode://@10.3.58.6:30303?discport=30301 -func NewEnode(rawurl string) (enode *Enode, _ error) { - node, err := discv5.ParseNode(rawurl) - if err != nil { - return nil, err - } - return &Enode{node}, nil -} - -// Enodes represents a slice of accounts. -type Enodes struct{ nodes []*discv5.Node } - -// NewEnodes creates a slice of uninitialized enodes. -func NewEnodes(size int) *Enodes { - return &Enodes{ - nodes: make([]*discv5.Node, size), - } -} - -// NewEnodesEmpty creates an empty slice of Enode values. -func NewEnodesEmpty() *Enodes { - return NewEnodes(0) -} - -// Size returns the number of enodes in the slice. -func (e *Enodes) Size() int { - return len(e.nodes) -} - -// Get returns the enode at the given index from the slice. -func (e *Enodes) Get(index int) (enode *Enode, _ error) { - if index < 0 || index >= len(e.nodes) { - return nil, errors.New("index out of bounds") - } - return &Enode{e.nodes[index]}, nil -} - -// Set sets the enode at the given index in the slice. -func (e *Enodes) Set(index int, enode *Enode) error { - if index < 0 || index >= len(e.nodes) { - return errors.New("index out of bounds") - } - e.nodes[index] = enode.node - return nil -} - -// Append adds a new enode element to the end of the slice. -func (e *Enodes) Append(enode *Enode) { - e.nodes = append(e.nodes, enode.node) -} diff --git a/mobile/doc.go b/mobile/doc.go deleted file mode 100644 index 64d47bec2a..0000000000 --- a/mobile/doc.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Package geth contains the simplified mobile APIs to go-ethereum. -// -// The scope of this package is *not* to allow writing a custom Ethereum client -// with pieces plucked from go-ethereum, rather to allow writing native dapps on -// mobile platforms. Keep this in mind when using or extending this package! -// -// API limitations -// -// Since gomobile cannot bridge arbitrary types between Go and Android/iOS, the -// exposed APIs need to be manually wrapped into simplified types, with custom -// constructors and getters/setters to ensure that they can be meaninfully used -// from Java/ObjC too. -// -// With this in mind, please try to limit the scope of this package and only add -// essentials without which mobile support cannot work, especially since manually -// syncing the code will be unwieldy otherwise. In the long term we might consider -// writing custom library generators, but those are out of scope now. -// -// Content wise each file in this package corresponds to an entire Go package -// from the go-ethereum repository. Please adhere to this scoping to prevent this -// package getting unmaintainable. -// -// Wrapping guidelines: -// -// Every type that is to be exposed should be wrapped into its own plain struct, -// which internally contains a single field: the original go-ethereum version. -// This is needed because gomobile cannot expose named types for now. -// -// Whenever a method argument or a return type is a custom struct, the pointer -// variant should always be used as value types crossing over between language -// boundaries might have strange behaviors. -// -// Slices of types should be converted into a single multiplicative type wrapping -// a go slice with the methods `Size`, `Get` and `Set`. Further slice operations -// should not be provided to limit the remote code complexity. Arrays should be -// avoided as much as possible since they complicate bounds checking. -// -// If a method has multiple return values (e.g. some return + an error), those -// are generated as output arguments in ObjC. To avoid weird generated names like -// ret_0 for them, please always assign names to output variables if tuples. -// -// Note, a panic *cannot* cross over language boundaries, instead will result in -// an undebuggable SEGFAULT in the process. For error handling only ever use error -// returns, which may be the only or the second return. -package geth diff --git a/mobile/ethclient.go b/mobile/ethclient.go deleted file mode 100644 index 66399c6b56..0000000000 --- a/mobile/ethclient.go +++ /dev/null @@ -1,312 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains a wrapper for the Ethereum client. - -package geth - -import ( - "math/big" - - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/ethclient" -) - -// EthereumClient provides access to the Ethereum APIs. -type EthereumClient struct { - client *ethclient.Client -} - -// NewEthereumClient connects a client to the given URL. -func NewEthereumClient(rawurl string) (client *EthereumClient, _ error) { - rawClient, err := ethclient.Dial(rawurl) - return &EthereumClient{rawClient}, err -} - -// GetBlockByHash returns the given full block. -func (ec *EthereumClient) GetBlockByHash(ctx *Context, hash *Hash) (block *Block, _ error) { - rawBlock, err := ec.client.BlockByHash(ctx.context, hash.hash) - return &Block{rawBlock}, err -} - -// GetBlockByNumber returns a block from the current canonical chain. If number is <0, the -// latest known block is returned. -func (ec *EthereumClient) GetBlockByNumber(ctx *Context, number int64) (block *Block, _ error) { - if number < 0 { - rawBlock, err := ec.client.BlockByNumber(ctx.context, nil) - return &Block{rawBlock}, err - } - rawBlock, err := ec.client.BlockByNumber(ctx.context, big.NewInt(number)) - return &Block{rawBlock}, err -} - -// GetHeaderByHash returns the block header with the given hash. -func (ec *EthereumClient) GetHeaderByHash(ctx *Context, hash *Hash) (header *Header, _ error) { - rawHeader, err := ec.client.HeaderByHash(ctx.context, hash.hash) - return &Header{rawHeader}, err -} - -// GetHeaderByNumber returns a block header from the current canonical chain. If number is <0, -// the latest known header is returned. -func (ec *EthereumClient) GetHeaderByNumber(ctx *Context, number int64) (header *Header, _ error) { - if number < 0 { - rawHeader, err := ec.client.HeaderByNumber(ctx.context, nil) - return &Header{rawHeader}, err - } - rawHeader, err := ec.client.HeaderByNumber(ctx.context, big.NewInt(number)) - return &Header{rawHeader}, err -} - -// GetTransactionByHash returns the transaction with the given hash. -func (ec *EthereumClient) GetTransactionByHash(ctx *Context, hash *Hash) (tx *Transaction, _ error) { - // TODO(karalabe): handle isPending - rawTx, _, err := ec.client.TransactionByHash(ctx.context, hash.hash) - return &Transaction{rawTx}, err -} - -// GetTransactionSender returns the sender address of a transaction. The transaction must -// be included in blockchain at the given block and index. -func (ec *EthereumClient) GetTransactionSender(ctx *Context, tx *Transaction, blockhash *Hash, index int) (sender *Address, _ error) { - addr, err := ec.client.TransactionSender(ctx.context, tx.tx, blockhash.hash, uint(index)) - return &Address{addr}, err -} - -// GetTransactionCount returns the total number of transactions in the given block. -func (ec *EthereumClient) GetTransactionCount(ctx *Context, hash *Hash) (count int, _ error) { - rawCount, err := ec.client.TransactionCount(ctx.context, hash.hash) - return int(rawCount), err -} - -// GetTransactionInBlock returns a single transaction at index in the given block. -func (ec *EthereumClient) GetTransactionInBlock(ctx *Context, hash *Hash, index int) (tx *Transaction, _ error) { - rawTx, err := ec.client.TransactionInBlock(ctx.context, hash.hash, uint(index)) - return &Transaction{rawTx}, err - -} - -// GetTransactionReceipt returns the receipt of a transaction by transaction hash. -// Note that the receipt is not available for pending transactions. -func (ec *EthereumClient) GetTransactionReceipt(ctx *Context, hash *Hash) (receipt *Receipt, _ error) { - rawReceipt, err := ec.client.TransactionReceipt(ctx.context, hash.hash) - return &Receipt{rawReceipt}, err -} - -// SyncProgress retrieves the current progress of the sync algorithm. If there's -// no sync currently running, it returns nil. -func (ec *EthereumClient) SyncProgress(ctx *Context) (progress *SyncProgress, _ error) { - rawProgress, err := ec.client.SyncProgress(ctx.context) - if rawProgress == nil { - return nil, err - } - return &SyncProgress{*rawProgress}, err -} - -// NewHeadHandler is a client-side subscription callback to invoke on events and -// subscription failure. -type NewHeadHandler interface { - OnNewHead(header *Header) - OnError(failure string) -} - -// SubscribeNewHead subscribes to notifications about the current blockchain head -// on the given channel. -func (ec *EthereumClient) SubscribeNewHead(ctx *Context, handler NewHeadHandler, buffer int) (sub *Subscription, _ error) { - // Subscribe to the event internally - ch := make(chan *types.Header, buffer) - rawSub, err := ec.client.SubscribeNewHead(ctx.context, ch) - if err != nil { - return nil, err - } - // Start up a dispatcher to feed into the callback - go func() { - for { - select { - case header := <-ch: - handler.OnNewHead(&Header{header}) - - case err := <-rawSub.Err(): - handler.OnError(err.Error()) - return - } - } - }() - return &Subscription{rawSub}, nil -} - -// State Access - -// GetBalanceAt returns the wei balance of the given account. -// The block number can be <0, in which case the balance is taken from the latest known block. -func (ec *EthereumClient) GetBalanceAt(ctx *Context, account *Address, number int64) (balance *BigInt, _ error) { - if number < 0 { - rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, nil) - return &BigInt{rawBalance}, err - } - rawBalance, err := ec.client.BalanceAt(ctx.context, account.address, big.NewInt(number)) - return &BigInt{rawBalance}, err -} - -// GetStorageAt returns the value of key in the contract storage of the given account. -// The block number can be <0, in which case the value is taken from the latest known block. -func (ec *EthereumClient) GetStorageAt(ctx *Context, account *Address, key *Hash, number int64) (storage []byte, _ error) { - if number < 0 { - return ec.client.StorageAt(ctx.context, account.address, key.hash, nil) - } - return ec.client.StorageAt(ctx.context, account.address, key.hash, big.NewInt(number)) -} - -// GetCodeAt returns the contract code of the given account. -// The block number can be <0, in which case the code is taken from the latest known block. -func (ec *EthereumClient) GetCodeAt(ctx *Context, account *Address, number int64) (code []byte, _ error) { - if number < 0 { - return ec.client.CodeAt(ctx.context, account.address, nil) - } - return ec.client.CodeAt(ctx.context, account.address, big.NewInt(number)) -} - -// GetNonceAt returns the account nonce of the given account. -// The block number can be <0, in which case the nonce is taken from the latest known block. -func (ec *EthereumClient) GetNonceAt(ctx *Context, account *Address, number int64) (nonce int64, _ error) { - if number < 0 { - rawNonce, err := ec.client.NonceAt(ctx.context, account.address, nil) - return int64(rawNonce), err - } - rawNonce, err := ec.client.NonceAt(ctx.context, account.address, big.NewInt(number)) - return int64(rawNonce), err -} - -// Filters - -// FilterLogs executes a filter query. -func (ec *EthereumClient) FilterLogs(ctx *Context, query *FilterQuery) (logs *Logs, _ error) { - rawLogs, err := ec.client.FilterLogs(ctx.context, query.query) - if err != nil { - return nil, err - } - // Temp hack due to vm.Logs being []*vm.Log - res := make([]*types.Log, len(rawLogs)) - for i := range rawLogs { - res[i] = &rawLogs[i] - } - return &Logs{res}, nil -} - -// FilterLogsHandler is a client-side subscription callback to invoke on events and -// subscription failure. -type FilterLogsHandler interface { - OnFilterLogs(log *Log) - OnError(failure string) -} - -// SubscribeFilterLogs subscribes to the results of a streaming filter query. -func (ec *EthereumClient) SubscribeFilterLogs(ctx *Context, query *FilterQuery, handler FilterLogsHandler, buffer int) (sub *Subscription, _ error) { - // Subscribe to the event internally - ch := make(chan types.Log, buffer) - rawSub, err := ec.client.SubscribeFilterLogs(ctx.context, query.query, ch) - if err != nil { - return nil, err - } - // Start up a dispatcher to feed into the callback - go func() { - for { - select { - case log := <-ch: - handler.OnFilterLogs(&Log{&log}) - - case err := <-rawSub.Err(): - handler.OnError(err.Error()) - return - } - } - }() - return &Subscription{rawSub}, nil -} - -// Pending State - -// GetPendingBalanceAt returns the wei balance of the given account in the pending state. -func (ec *EthereumClient) GetPendingBalanceAt(ctx *Context, account *Address) (balance *BigInt, _ error) { - rawBalance, err := ec.client.PendingBalanceAt(ctx.context, account.address) - return &BigInt{rawBalance}, err -} - -// GetPendingStorageAt returns the value of key in the contract storage of the given account in the pending state. -func (ec *EthereumClient) GetPendingStorageAt(ctx *Context, account *Address, key *Hash) (storage []byte, _ error) { - return ec.client.PendingStorageAt(ctx.context, account.address, key.hash) -} - -// GetPendingCodeAt returns the contract code of the given account in the pending state. -func (ec *EthereumClient) GetPendingCodeAt(ctx *Context, account *Address) (code []byte, _ error) { - return ec.client.PendingCodeAt(ctx.context, account.address) -} - -// GetPendingNonceAt returns the account nonce of the given account in the pending state. -// This is the nonce that should be used for the next transaction. -func (ec *EthereumClient) GetPendingNonceAt(ctx *Context, account *Address) (nonce int64, _ error) { - rawNonce, err := ec.client.PendingNonceAt(ctx.context, account.address) - return int64(rawNonce), err -} - -// GetPendingTransactionCount returns the total number of transactions in the pending state. -func (ec *EthereumClient) GetPendingTransactionCount(ctx *Context) (count int, _ error) { - rawCount, err := ec.client.PendingTransactionCount(ctx.context) - return int(rawCount), err -} - -// Contract Calling - -// CallContract executes a message call transaction, which is directly executed in the VM -// of the node, but never mined into the blockchain. -// -// blockNumber selects the block height at which the call runs. It can be <0, in which -// case the code is taken from the latest known block. Note that state from very old -// blocks might not be available. -func (ec *EthereumClient) CallContract(ctx *Context, msg *CallMsg, number int64) (output []byte, _ error) { - if number < 0 { - return ec.client.CallContract(ctx.context, msg.msg, nil) - } - return ec.client.CallContract(ctx.context, msg.msg, big.NewInt(number)) -} - -// PendingCallContract executes a message call transaction using the EVM. -// The state seen by the contract call is the pending state. -func (ec *EthereumClient) PendingCallContract(ctx *Context, msg *CallMsg) (output []byte, _ error) { - return ec.client.PendingCallContract(ctx.context, msg.msg) -} - -// SuggestGasPrice retrieves the currently suggested gas price to allow a timely -// execution of a transaction. -func (ec *EthereumClient) SuggestGasPrice(ctx *Context) (price *BigInt, _ error) { - rawPrice, err := ec.client.SuggestGasPrice(ctx.context) - return &BigInt{rawPrice}, err -} - -// EstimateGas tries to estimate the gas needed to execute a specific transaction based on -// the current pending state of the backend blockchain. There is no guarantee that this is -// the true gas limit requirement as other transactions may be added or removed by miners, -// but it should provide a basis for setting a reasonable default. -func (ec *EthereumClient) EstimateGas(ctx *Context, msg *CallMsg) (gas int64, _ error) { - rawGas, err := ec.client.EstimateGas(ctx.context, msg.msg) - return int64(rawGas), err -} - -// SendTransaction injects a signed transaction into the pending pool for execution. -// -// If the transaction was a contract creation use the TransactionReceipt method to get the -// contract address after the transaction has been mined. -func (ec *EthereumClient) SendTransaction(ctx *Context, tx *Transaction) error { - return ec.client.SendTransaction(ctx.context, tx.tx) -} diff --git a/mobile/ethereum.go b/mobile/ethereum.go deleted file mode 100644 index 35a43d274d..0000000000 --- a/mobile/ethereum.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the go-ethereum root package. - -package geth - -import ( - "errors" - - ethereum "github.com/ethereum/go-ethereum" - "github.com/ethereum/go-ethereum/common" -) - -// Subscription represents an event subscription where events are -// delivered on a data channel. -type Subscription struct { - sub ethereum.Subscription -} - -// Unsubscribe cancels the sending of events to the data channel -// and closes the error channel. -func (s *Subscription) Unsubscribe() { - s.sub.Unsubscribe() -} - -// CallMsg contains parameters for contract calls. -type CallMsg struct { - msg ethereum.CallMsg -} - -// NewCallMsg creates an empty contract call parameter list. -func NewCallMsg() *CallMsg { - return new(CallMsg) -} - -func (msg *CallMsg) GetFrom() *Address { return &Address{msg.msg.From} } -func (msg *CallMsg) GetGas() int64 { return int64(msg.msg.Gas) } -func (msg *CallMsg) GetGasPrice() *BigInt { return &BigInt{msg.msg.GasPrice} } -func (msg *CallMsg) GetValue() *BigInt { return &BigInt{msg.msg.Value} } -func (msg *CallMsg) GetData() []byte { return msg.msg.Data } -func (msg *CallMsg) GetTo() *Address { - if to := msg.msg.To; to != nil { - return &Address{*msg.msg.To} - } - return nil -} - -func (msg *CallMsg) SetFrom(address *Address) { msg.msg.From = address.address } -func (msg *CallMsg) SetGas(gas int64) { msg.msg.Gas = uint64(gas) } -func (msg *CallMsg) SetGasPrice(price *BigInt) { msg.msg.GasPrice = price.bigint } -func (msg *CallMsg) SetValue(value *BigInt) { msg.msg.Value = value.bigint } -func (msg *CallMsg) SetData(data []byte) { msg.msg.Data = common.CopyBytes(data) } -func (msg *CallMsg) SetTo(address *Address) { - if address == nil { - msg.msg.To = nil - } - msg.msg.To = &address.address -} - -// SyncProgress gives progress indications when the node is synchronising with -// the Ethereum network. -type SyncProgress struct { - progress ethereum.SyncProgress -} - -func (p *SyncProgress) GetStartingBlock() int64 { return int64(p.progress.StartingBlock) } -func (p *SyncProgress) GetCurrentBlock() int64 { return int64(p.progress.CurrentBlock) } -func (p *SyncProgress) GetHighestBlock() int64 { return int64(p.progress.HighestBlock) } -func (p *SyncProgress) GetPulledStates() int64 { return int64(p.progress.PulledStates) } -func (p *SyncProgress) GetKnownStates() int64 { return int64(p.progress.KnownStates) } - -// Topics is a set of topic lists to filter events with. -type Topics struct{ topics [][]common.Hash } - -// NewTopics creates a slice of uninitialized Topics. -func NewTopics(size int) *Topics { - return &Topics{ - topics: make([][]common.Hash, size), - } -} - -// NewTopicsEmpty creates an empty slice of Topics values. -func NewTopicsEmpty() *Topics { - return NewTopics(0) -} - -// Size returns the number of topic lists inside the set -func (t *Topics) Size() int { - return len(t.topics) -} - -// Get returns the topic list at the given index from the slice. -func (t *Topics) Get(index int) (hashes *Hashes, _ error) { - if index < 0 || index >= len(t.topics) { - return nil, errors.New("index out of bounds") - } - return &Hashes{t.topics[index]}, nil -} - -// Set sets the topic list at the given index in the slice. -func (t *Topics) Set(index int, topics *Hashes) error { - if index < 0 || index >= len(t.topics) { - return errors.New("index out of bounds") - } - t.topics[index] = topics.hashes - return nil -} - -// Append adds a new topic list to the end of the slice. -func (t *Topics) Append(topics *Hashes) { - t.topics = append(t.topics, topics.hashes) -} - -// FilterQuery contains options for contract log filtering. -type FilterQuery struct { - query ethereum.FilterQuery -} - -// NewFilterQuery creates an empty filter query for contract log filtering. -func NewFilterQuery() *FilterQuery { - return new(FilterQuery) -} - -func (fq *FilterQuery) GetFromBlock() *BigInt { return &BigInt{fq.query.FromBlock} } -func (fq *FilterQuery) GetToBlock() *BigInt { return &BigInt{fq.query.ToBlock} } -func (fq *FilterQuery) GetAddresses() *Addresses { return &Addresses{fq.query.Addresses} } -func (fq *FilterQuery) GetTopics() *Topics { return &Topics{fq.query.Topics} } - -func (fq *FilterQuery) SetFromBlock(fromBlock *BigInt) { fq.query.FromBlock = fromBlock.bigint } -func (fq *FilterQuery) SetToBlock(toBlock *BigInt) { fq.query.ToBlock = toBlock.bigint } -func (fq *FilterQuery) SetAddresses(addresses *Addresses) { fq.query.Addresses = addresses.addresses } -func (fq *FilterQuery) SetTopics(topics *Topics) { fq.query.Topics = topics.topics } diff --git a/mobile/geth.go b/mobile/geth.go deleted file mode 100644 index 63ef062345..0000000000 --- a/mobile/geth.go +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the node package to support client side node -// management on mobile platforms. - -package geth - -import ( - "encoding/json" - "fmt" - "path/filepath" - - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/eth" - "github.com/ethereum/go-ethereum/eth/downloader" - "github.com/ethereum/go-ethereum/ethclient" - "github.com/ethereum/go-ethereum/ethstats" - "github.com/ethereum/go-ethereum/internal/debug" - "github.com/ethereum/go-ethereum/les" - "github.com/ethereum/go-ethereum/node" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/nat" - "github.com/ethereum/go-ethereum/params" - whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" -) - -// NodeConfig represents the collection of configuration values to fine tune the Geth -// node embedded into a mobile process. The available values are a subset of the -// entire API provided by go-ethereum to reduce the maintenance surface and dev -// complexity. -type NodeConfig struct { - // Bootstrap nodes used to establish connectivity with the rest of the network. - BootstrapNodes *Enodes - - // MaxPeers is the maximum number of peers that can be connected. If this is - // set to zero, then only the configured static and trusted peers can connect. - MaxPeers int - - // EthereumEnabled specifies whether the node should run the Ethereum protocol. - EthereumEnabled bool - - // EthereumNetworkID is the network identifier used by the Ethereum protocol to - // decide if remote peers should be accepted or not. - EthereumNetworkID int64 // uint64 in truth, but Java can't handle that... - - // EthereumGenesis is the genesis JSON to use to seed the blockchain with. An - // empty genesis state is equivalent to using the mainnet's state. - EthereumGenesis string - - // EthereumDatabaseCache is the system memory in MB to allocate for database caching. - // A minimum of 16MB is always reserved. - EthereumDatabaseCache int - - // EthereumNetStats is a netstats connection string to use to report various - // chain, transaction and node stats to a monitoring server. - // - // It has the form "nodename:secret@host:port" - EthereumNetStats string - - // WhisperEnabled specifies whether the node should run the Whisper protocol. - WhisperEnabled bool - - // Listening address of pprof server. - PprofAddress string -} - -// defaultNodeConfig contains the default node configuration values to use if all -// or some fields are missing from the user's specified list. -var defaultNodeConfig = &NodeConfig{ - BootstrapNodes: FoundationBootnodes(), - MaxPeers: 25, - EthereumEnabled: true, - EthereumNetworkID: 1, - EthereumDatabaseCache: 16, -} - -// NewNodeConfig creates a new node option set, initialized to the default values. -func NewNodeConfig() *NodeConfig { - config := *defaultNodeConfig - return &config -} - -// Node represents a Geth Ethereum node instance. -type Node struct { - node *node.Node -} - -// NewNode creates and configures a new Geth node. -func NewNode(datadir string, config *NodeConfig) (stack *Node, _ error) { - // If no or partial configurations were specified, use defaults - if config == nil { - config = NewNodeConfig() - } - if config.MaxPeers == 0 { - config.MaxPeers = defaultNodeConfig.MaxPeers - } - if config.BootstrapNodes == nil || config.BootstrapNodes.Size() == 0 { - config.BootstrapNodes = defaultNodeConfig.BootstrapNodes - } - - if config.PprofAddress != "" { - debug.StartPProf(config.PprofAddress) - } - - // Create the empty networking stack - nodeConf := &node.Config{ - Name: clientIdentifier, - Version: params.Version, - DataDir: datadir, - KeyStoreDir: filepath.Join(datadir, "keystore"), // Mobile should never use internal keystores! - P2P: p2p.Config{ - NoDiscovery: true, - DiscoveryV5: true, - BootstrapNodesV5: config.BootstrapNodes.nodes, - ListenAddr: ":0", - NAT: nat.Any(), - MaxPeers: config.MaxPeers, - }, - } - rawStack, err := node.New(nodeConf) - if err != nil { - return nil, err - } - - debug.Memsize.Add("node", rawStack) - - var genesis *core.Genesis - if config.EthereumGenesis != "" { - // Parse the user supplied genesis spec if not mainnet - genesis = new(core.Genesis) - if err := json.Unmarshal([]byte(config.EthereumGenesis), genesis); err != nil { - return nil, fmt.Errorf("invalid genesis spec: %v", err) - } - // If we have the testnet, hard code the chain configs too - if config.EthereumGenesis == TestnetGenesis() { - genesis.Config = params.TestnetChainConfig - if config.EthereumNetworkID == 1 { - config.EthereumNetworkID = 3 - } - } - } - // Register the Ethereum protocol if requested - if config.EthereumEnabled { - ethConf := eth.DefaultConfig - ethConf.Genesis = genesis - ethConf.SyncMode = downloader.LightSync - ethConf.NetworkId = uint64(config.EthereumNetworkID) - ethConf.DatabaseCache = config.EthereumDatabaseCache - if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - return les.New(ctx, ðConf) - }); err != nil { - return nil, fmt.Errorf("ethereum init: %v", err) - } - // If netstats reporting is requested, do it - if config.EthereumNetStats != "" { - if err := rawStack.Register(func(ctx *node.ServiceContext) (node.Service, error) { - var lesServ *les.LightEthereum - ctx.Service(&lesServ) - - return ethstats.New(config.EthereumNetStats, nil, lesServ) - }); err != nil { - return nil, fmt.Errorf("netstats init: %v", err) - } - } - } - // Register the Whisper protocol if requested - if config.WhisperEnabled { - 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 &Node{rawStack}, nil -} - -// Start creates a live P2P node and starts running it. -func (n *Node) Start() error { - return n.node.Start() -} - -// Stop terminates a running node along with all it's services. If the node was -// not started, an error is returned. -func (n *Node) Stop() error { - return n.node.Stop() -} - -// GetEthereumClient retrieves a client to access the Ethereum subsystem. -func (n *Node) GetEthereumClient() (client *EthereumClient, _ error) { - rpc, err := n.node.Attach() - if err != nil { - return nil, err - } - return &EthereumClient{ethclient.NewClient(rpc)}, nil -} - -// GetNodeInfo gathers and returns a collection of metadata known about the host. -func (n *Node) GetNodeInfo() *NodeInfo { - return &NodeInfo{n.node.Server().NodeInfo()} -} - -// GetPeersInfo returns an array of metadata objects describing connected peers. -func (n *Node) GetPeersInfo() *PeerInfos { - return &PeerInfos{n.node.Server().PeersInfo()} -} diff --git a/mobile/geth_android.go b/mobile/geth_android.go deleted file mode 100644 index 8e4ebe638f..0000000000 --- a/mobile/geth_android.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build android - -package geth - -// clientIdentifier is a hard coded identifier to report into the network. -var clientIdentifier = "GethDroid" diff --git a/mobile/geth_ios.go b/mobile/geth_ios.go deleted file mode 100644 index 307cd08580..0000000000 --- a/mobile/geth_ios.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build ios - -package geth - -// clientIdentifier is a hard coded identifier to report into the network. -var clientIdentifier = "iGeth" diff --git a/mobile/geth_other.go b/mobile/geth_other.go deleted file mode 100644 index 6f0c5dda68..0000000000 --- a/mobile/geth_other.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// +build !android,!ios - -package geth - -// clientIdentifier is a hard coded identifier to report into the network. -var clientIdentifier = "GethMobile" diff --git a/mobile/init.go b/mobile/init.go deleted file mode 100644 index 2025d85edc..0000000000 --- a/mobile/init.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains initialization code for the mbile library. - -package geth - -import ( - "os" - "runtime" - - "github.com/ethereum/go-ethereum/log" -) - -func init() { - // Initialize the logger - log.Root().SetHandler(log.LvlFilterHandler(log.LvlInfo, log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) - - // Initialize the goroutine count - runtime.GOMAXPROCS(runtime.NumCPU()) -} diff --git a/mobile/interface.go b/mobile/interface.go deleted file mode 100644 index ac0c26088a..0000000000 --- a/mobile/interface.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains perverted wrappers to allow crossing over empty interfaces. - -package geth - -import ( - "errors" - "math/big" - - "github.com/ethereum/go-ethereum/common" -) - -// Interface represents a wrapped version of Go's interface{}, with the capacity -// to store arbitrary data types. -// -// Since it's impossible to get the arbitrary-ness converted between Go and mobile -// platforms, we're using explicit getters and setters for the conversions. There -// is of course no point in enumerating everything, just enough to support the -// contract bindins requiring client side generated code. -type Interface struct { - object interface{} -} - -// NewInterface creates a new empty interface that can be used to pass around -// generic types. -func NewInterface() *Interface { - return new(Interface) -} - -func (i *Interface) SetBool(b bool) { i.object = &b } -func (i *Interface) SetBools(bs []bool) { i.object = &bs } -func (i *Interface) SetString(str string) { i.object = &str } -func (i *Interface) SetStrings(strs *Strings) { i.object = &strs.strs } -func (i *Interface) SetBinary(binary []byte) { b := common.CopyBytes(binary); i.object = &b } -func (i *Interface) SetBinaries(binaries [][]byte) { i.object = &binaries } -func (i *Interface) SetAddress(address *Address) { i.object = &address.address } -func (i *Interface) SetAddresses(addrs *Addresses) { i.object = &addrs.addresses } -func (i *Interface) SetHash(hash *Hash) { i.object = &hash.hash } -func (i *Interface) SetHashes(hashes *Hashes) { i.object = &hashes.hashes } -func (i *Interface) SetInt8(n int8) { i.object = &n } -func (i *Interface) SetInt16(n int16) { i.object = &n } -func (i *Interface) SetInt32(n int32) { i.object = &n } -func (i *Interface) SetInt64(n int64) { i.object = &n } -func (i *Interface) SetUint8(bigint *BigInt) { n := uint8(bigint.bigint.Uint64()); i.object = &n } -func (i *Interface) SetUint16(bigint *BigInt) { n := uint16(bigint.bigint.Uint64()); i.object = &n } -func (i *Interface) SetUint32(bigint *BigInt) { n := uint32(bigint.bigint.Uint64()); i.object = &n } -func (i *Interface) SetUint64(bigint *BigInt) { n := bigint.bigint.Uint64(); i.object = &n } -func (i *Interface) SetBigInt(bigint *BigInt) { i.object = &bigint.bigint } -func (i *Interface) SetBigInts(bigints *BigInts) { i.object = &bigints.bigints } - -func (i *Interface) SetDefaultBool() { i.object = new(bool) } -func (i *Interface) SetDefaultBools() { i.object = new([]bool) } -func (i *Interface) SetDefaultString() { i.object = new(string) } -func (i *Interface) SetDefaultStrings() { i.object = new([]string) } -func (i *Interface) SetDefaultBinary() { i.object = new([]byte) } -func (i *Interface) SetDefaultBinaries() { i.object = new([][]byte) } -func (i *Interface) SetDefaultAddress() { i.object = new(common.Address) } -func (i *Interface) SetDefaultAddresses() { i.object = new([]common.Address) } -func (i *Interface) SetDefaultHash() { i.object = new(common.Hash) } -func (i *Interface) SetDefaultHashes() { i.object = new([]common.Hash) } -func (i *Interface) SetDefaultInt8() { i.object = new(int8) } -func (i *Interface) SetDefaultInt16() { i.object = new(int16) } -func (i *Interface) SetDefaultInt32() { i.object = new(int32) } -func (i *Interface) SetDefaultInt64() { i.object = new(int64) } -func (i *Interface) SetDefaultUint8() { i.object = new(uint8) } -func (i *Interface) SetDefaultUint16() { i.object = new(uint16) } -func (i *Interface) SetDefaultUint32() { i.object = new(uint32) } -func (i *Interface) SetDefaultUint64() { i.object = new(uint64) } -func (i *Interface) SetDefaultBigInt() { i.object = new(*big.Int) } -func (i *Interface) SetDefaultBigInts() { i.object = new([]*big.Int) } - -func (i *Interface) GetBool() bool { return *i.object.(*bool) } -func (i *Interface) GetBools() []bool { return *i.object.(*[]bool) } -func (i *Interface) GetString() string { return *i.object.(*string) } -func (i *Interface) GetStrings() *Strings { return &Strings{*i.object.(*[]string)} } -func (i *Interface) GetBinary() []byte { return *i.object.(*[]byte) } -func (i *Interface) GetBinaries() [][]byte { return *i.object.(*[][]byte) } -func (i *Interface) GetAddress() *Address { return &Address{*i.object.(*common.Address)} } -func (i *Interface) GetAddresses() *Addresses { return &Addresses{*i.object.(*[]common.Address)} } -func (i *Interface) GetHash() *Hash { return &Hash{*i.object.(*common.Hash)} } -func (i *Interface) GetHashes() *Hashes { return &Hashes{*i.object.(*[]common.Hash)} } -func (i *Interface) GetInt8() int8 { return *i.object.(*int8) } -func (i *Interface) GetInt16() int16 { return *i.object.(*int16) } -func (i *Interface) GetInt32() int32 { return *i.object.(*int32) } -func (i *Interface) GetInt64() int64 { return *i.object.(*int64) } -func (i *Interface) GetUint8() *BigInt { - return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint8)))} -} -func (i *Interface) GetUint16() *BigInt { - return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint16)))} -} -func (i *Interface) GetUint32() *BigInt { - return &BigInt{new(big.Int).SetUint64(uint64(*i.object.(*uint32)))} -} -func (i *Interface) GetUint64() *BigInt { - return &BigInt{new(big.Int).SetUint64(*i.object.(*uint64))} -} -func (i *Interface) GetBigInt() *BigInt { return &BigInt{*i.object.(**big.Int)} } -func (i *Interface) GetBigInts() *BigInts { return &BigInts{*i.object.(*[]*big.Int)} } - -// Interfaces is a slices of wrapped generic objects. -type Interfaces struct { - objects []interface{} -} - -// NewInterfaces creates a slice of uninitialized interfaces. -func NewInterfaces(size int) *Interfaces { - return &Interfaces{ - objects: make([]interface{}, size), - } -} - -// Size returns the number of interfaces in the slice. -func (i *Interfaces) Size() int { - return len(i.objects) -} - -// Get returns the bigint at the given index from the slice. -func (i *Interfaces) Get(index int) (iface *Interface, _ error) { - if index < 0 || index >= len(i.objects) { - return nil, errors.New("index out of bounds") - } - return &Interface{i.objects[index]}, nil -} - -// Set sets the big int at the given index in the slice. -func (i *Interfaces) Set(index int, object *Interface) error { - if index < 0 || index >= len(i.objects) { - return errors.New("index out of bounds") - } - i.objects[index] = object.object - return nil -} diff --git a/mobile/logger.go b/mobile/logger.go deleted file mode 100644 index 7078c4fd2c..0000000000 --- a/mobile/logger.go +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package geth - -import ( - "os" - - "github.com/ethereum/go-ethereum/log" -) - -// SetVerbosity sets the global verbosity level (between 0 and 6 - see logger/verbosity.go). -func SetVerbosity(level int) { - log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(level), log.StreamHandler(os.Stderr, log.TerminalFormat(false)))) -} diff --git a/mobile/p2p.go b/mobile/p2p.go deleted file mode 100644 index a80d9fff2e..0000000000 --- a/mobile/p2p.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains wrappers for the p2p package. - -package geth - -import ( - "errors" - - "github.com/ethereum/go-ethereum/p2p" -) - -// NodeInfo represents pi short summary of the information known about the host. -type NodeInfo struct { - info *p2p.NodeInfo -} - -func (ni *NodeInfo) GetID() string { return ni.info.ID } -func (ni *NodeInfo) GetName() string { return ni.info.Name } -func (ni *NodeInfo) GetEnode() string { return ni.info.Enode } -func (ni *NodeInfo) GetIP() string { return ni.info.IP } -func (ni *NodeInfo) GetDiscoveryPort() int { return ni.info.Ports.Discovery } -func (ni *NodeInfo) GetListenerPort() int { return ni.info.Ports.Listener } -func (ni *NodeInfo) GetListenerAddress() string { return ni.info.ListenAddr } -func (ni *NodeInfo) GetProtocols() *Strings { - protos := []string{} - for proto := range ni.info.Protocols { - protos = append(protos, proto) - } - return &Strings{protos} -} - -// PeerInfo represents pi short summary of the information known about pi connected peer. -type PeerInfo struct { - info *p2p.PeerInfo -} - -func (pi *PeerInfo) GetID() string { return pi.info.ID } -func (pi *PeerInfo) GetName() string { return pi.info.Name } -func (pi *PeerInfo) GetCaps() *Strings { return &Strings{pi.info.Caps} } -func (pi *PeerInfo) GetLocalAddress() string { return pi.info.Network.LocalAddress } -func (pi *PeerInfo) GetRemoteAddress() string { return pi.info.Network.RemoteAddress } - -// PeerInfos represents a slice of infos about remote peers. -type PeerInfos struct { - infos []*p2p.PeerInfo -} - -// Size returns the number of peer info entries in the slice. -func (pi *PeerInfos) Size() int { - return len(pi.infos) -} - -// Get returns the peer info at the given index from the slice. -func (pi *PeerInfos) Get(index int) (info *PeerInfo, _ error) { - if index < 0 || index >= len(pi.infos) { - return nil, errors.New("index out of bounds") - } - return &PeerInfo{pi.infos[index]}, nil -} diff --git a/mobile/params.go b/mobile/params.go deleted file mode 100644 index 45fe870ee3..0000000000 --- a/mobile/params.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the params package. - -package geth - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/core" - "github.com/ethereum/go-ethereum/p2p/discv5" - "github.com/ethereum/go-ethereum/params" -) - -// MainnetGenesis returns the JSON spec to use for the main Ethereum network. It -// is actually empty since that defaults to the hard coded binary genesis block. -func MainnetGenesis() string { - return "" -} - -// TestnetGenesis returns the JSON spec to use for the Ethereum test network. -func TestnetGenesis() string { - enc, err := json.Marshal(core.DefaultTestnetGenesisBlock()) - if err != nil { - panic(err) - } - return string(enc) -} - -// RinkebyGenesis returns the JSON spec to use for the Rinkeby test network -func RinkebyGenesis() string { - enc, err := json.Marshal(core.DefaultRinkebyGenesisBlock()) - if err != nil { - panic(err) - } - return string(enc) -} - -// FoundationBootnodes returns the enode URLs of the P2P bootstrap nodes operated -// by the foundation running the V5 discovery protocol. -func FoundationBootnodes() *Enodes { - nodes := &Enodes{nodes: make([]*discv5.Node, len(params.DiscoveryV5Bootnodes))} - for i, url := range params.DiscoveryV5Bootnodes { - nodes.nodes[i] = discv5.MustParseNode(url) - } - return nodes -} diff --git a/mobile/primitives.go b/mobile/primitives.go deleted file mode 100644 index 5c6617fa47..0000000000 --- a/mobile/primitives.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains various wrappers for primitive types. - -package geth - -import ( - "errors" - "fmt" -) - -// Strings represents s slice of strs. -type Strings struct{ strs []string } - -// Size returns the number of strs in the slice. -func (s *Strings) Size() int { - return len(s.strs) -} - -// Get returns the string at the given index from the slice. -func (s *Strings) Get(index int) (str string, _ error) { - if index < 0 || index >= len(s.strs) { - return "", errors.New("index out of bounds") - } - return s.strs[index], nil -} - -// Set sets the string at the given index in the slice. -func (s *Strings) Set(index int, str string) error { - if index < 0 || index >= len(s.strs) { - return errors.New("index out of bounds") - } - s.strs[index] = str - return nil -} - -// String implements the Stringer interface. -func (s *Strings) String() string { - return fmt.Sprintf("%v", s.strs) -} diff --git a/mobile/types.go b/mobile/types.go deleted file mode 100644 index f32b4918f3..0000000000 --- a/mobile/types.go +++ /dev/null @@ -1,339 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the core/types package. - -package geth - -import ( - "encoding/json" - "errors" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/core/types" - "github.com/ethereum/go-ethereum/rlp" -) - -// A Nonce is a 64-bit hash which proves (combined with the mix-hash) that -// a sufficient amount of computation has been carried out on a block. -type Nonce struct { - nonce types.BlockNonce -} - -// GetBytes retrieves the byte representation of the block nonce. -func (n *Nonce) GetBytes() []byte { - return n.nonce[:] -} - -// GetHex retrieves the hex string representation of the block nonce. -func (n *Nonce) GetHex() string { - return fmt.Sprintf("0x%x", n.nonce[:]) -} - -// Bloom represents a 256 bit bloom filter. -type Bloom struct { - bloom types.Bloom -} - -// GetBytes retrieves the byte representation of the bloom filter. -func (b *Bloom) GetBytes() []byte { - return b.bloom[:] -} - -// GetHex retrieves the hex string representation of the bloom filter. -func (b *Bloom) GetHex() string { - return fmt.Sprintf("0x%x", b.bloom[:]) -} - -// Header represents a block header in the Ethereum blockchain. -type Header struct { - header *types.Header -} - -// NewHeaderFromRLP parses a header from an RLP data dump. -func NewHeaderFromRLP(data []byte) (*Header, error) { - h := &Header{ - header: new(types.Header), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), h.header); err != nil { - return nil, err - } - return h, nil -} - -// EncodeRLP encodes a header into an RLP data dump. -func (h *Header) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(h.header) -} - -// NewHeaderFromJSON parses a header from a JSON data dump. -func NewHeaderFromJSON(data string) (*Header, error) { - h := &Header{ - header: new(types.Header), - } - if err := json.Unmarshal([]byte(data), h.header); err != nil { - return nil, err - } - return h, nil -} - -// EncodeJSON encodes a header into a JSON data dump. -func (h *Header) EncodeJSON() (string, error) { - data, err := json.Marshal(h.header) - return string(data), err -} - -func (h *Header) GetParentHash() *Hash { return &Hash{h.header.ParentHash} } -func (h *Header) GetUncleHash() *Hash { return &Hash{h.header.UncleHash} } -func (h *Header) GetCoinbase() *Address { return &Address{h.header.Coinbase} } -func (h *Header) GetRoot() *Hash { return &Hash{h.header.Root} } -func (h *Header) GetTxHash() *Hash { return &Hash{h.header.TxHash} } -func (h *Header) GetReceiptHash() *Hash { return &Hash{h.header.ReceiptHash} } -func (h *Header) GetBloom() *Bloom { return &Bloom{h.header.Bloom} } -func (h *Header) GetDifficulty() *BigInt { return &BigInt{h.header.Difficulty} } -func (h *Header) GetNumber() int64 { return h.header.Number.Int64() } -func (h *Header) GetGasLimit() int64 { return int64(h.header.GasLimit) } -func (h *Header) GetGasUsed() int64 { return int64(h.header.GasUsed) } -func (h *Header) GetTime() int64 { return h.header.Time.Int64() } -func (h *Header) GetExtra() []byte { return h.header.Extra } -func (h *Header) GetMixDigest() *Hash { return &Hash{h.header.MixDigest} } -func (h *Header) GetNonce() *Nonce { return &Nonce{h.header.Nonce} } -func (h *Header) GetHash() *Hash { return &Hash{h.header.Hash()} } - -// Headers represents a slice of headers. -type Headers struct{ headers []*types.Header } - -// Size returns the number of headers in the slice. -func (h *Headers) Size() int { - return len(h.headers) -} - -// Get returns the header at the given index from the slice. -func (h *Headers) Get(index int) (header *Header, _ error) { - if index < 0 || index >= len(h.headers) { - return nil, errors.New("index out of bounds") - } - return &Header{h.headers[index]}, nil -} - -// Block represents an entire block in the Ethereum blockchain. -type Block struct { - block *types.Block -} - -// NewBlockFromRLP parses a block from an RLP data dump. -func NewBlockFromRLP(data []byte) (*Block, error) { - b := &Block{ - block: new(types.Block), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), b.block); err != nil { - return nil, err - } - return b, nil -} - -// EncodeRLP encodes a block into an RLP data dump. -func (b *Block) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(b.block) -} - -// NewBlockFromJSON parses a block from a JSON data dump. -func NewBlockFromJSON(data string) (*Block, error) { - b := &Block{ - block: new(types.Block), - } - if err := json.Unmarshal([]byte(data), b.block); err != nil { - return nil, err - } - return b, nil -} - -// EncodeJSON encodes a block into a JSON data dump. -func (b *Block) EncodeJSON() (string, error) { - data, err := json.Marshal(b.block) - return string(data), err -} - -func (b *Block) GetParentHash() *Hash { return &Hash{b.block.ParentHash()} } -func (b *Block) GetUncleHash() *Hash { return &Hash{b.block.UncleHash()} } -func (b *Block) GetCoinbase() *Address { return &Address{b.block.Coinbase()} } -func (b *Block) GetRoot() *Hash { return &Hash{b.block.Root()} } -func (b *Block) GetTxHash() *Hash { return &Hash{b.block.TxHash()} } -func (b *Block) GetReceiptHash() *Hash { return &Hash{b.block.ReceiptHash()} } -func (b *Block) GetBloom() *Bloom { return &Bloom{b.block.Bloom()} } -func (b *Block) GetDifficulty() *BigInt { return &BigInt{b.block.Difficulty()} } -func (b *Block) GetNumber() int64 { return b.block.Number().Int64() } -func (b *Block) GetGasLimit() int64 { return int64(b.block.GasLimit()) } -func (b *Block) GetGasUsed() int64 { return int64(b.block.GasUsed()) } -func (b *Block) GetTime() int64 { return b.block.Time().Int64() } -func (b *Block) GetExtra() []byte { return b.block.Extra() } -func (b *Block) GetMixDigest() *Hash { return &Hash{b.block.MixDigest()} } -func (b *Block) GetNonce() int64 { return int64(b.block.Nonce()) } - -func (b *Block) GetHash() *Hash { return &Hash{b.block.Hash()} } -func (b *Block) GetHashNoNonce() *Hash { return &Hash{b.block.HashNoNonce()} } - -func (b *Block) GetHeader() *Header { return &Header{b.block.Header()} } -func (b *Block) GetUncles() *Headers { return &Headers{b.block.Uncles()} } -func (b *Block) GetTransactions() *Transactions { return &Transactions{b.block.Transactions()} } -func (b *Block) GetTransaction(hash *Hash) *Transaction { - return &Transaction{b.block.Transaction(hash.hash)} -} - -// Transaction represents a single Ethereum transaction. -type Transaction struct { - tx *types.Transaction -} - -// NewTransaction creates a new transaction with the given properties. -func NewTransaction(nonce int64, to *Address, amount *BigInt, gasLimit int64, gasPrice *BigInt, data []byte) *Transaction { - return &Transaction{types.NewTransaction(uint64(nonce), to.address, amount.bigint, uint64(gasLimit), gasPrice.bigint, common.CopyBytes(data))} -} - -// NewTransactionFromRLP parses a transaction from an RLP data dump. -func NewTransactionFromRLP(data []byte) (*Transaction, error) { - tx := &Transaction{ - tx: new(types.Transaction), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), tx.tx); err != nil { - return nil, err - } - return tx, nil -} - -// EncodeRLP encodes a transaction into an RLP data dump. -func (tx *Transaction) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(tx.tx) -} - -// NewTransactionFromJSON parses a transaction from a JSON data dump. -func NewTransactionFromJSON(data string) (*Transaction, error) { - tx := &Transaction{ - tx: new(types.Transaction), - } - if err := json.Unmarshal([]byte(data), tx.tx); err != nil { - return nil, err - } - return tx, nil -} - -// EncodeJSON encodes a transaction into a JSON data dump. -func (tx *Transaction) EncodeJSON() (string, error) { - data, err := json.Marshal(tx.tx) - return string(data), err -} - -func (tx *Transaction) GetData() []byte { return tx.tx.Data() } -func (tx *Transaction) GetGas() int64 { return int64(tx.tx.Gas()) } -func (tx *Transaction) GetGasPrice() *BigInt { return &BigInt{tx.tx.GasPrice()} } -func (tx *Transaction) GetValue() *BigInt { return &BigInt{tx.tx.Value()} } -func (tx *Transaction) GetNonce() int64 { return int64(tx.tx.Nonce()) } - -func (tx *Transaction) GetHash() *Hash { return &Hash{tx.tx.Hash()} } -func (tx *Transaction) GetCost() *BigInt { return &BigInt{tx.tx.Cost()} } - -// Deprecated: GetSigHash cannot know which signer to use. -func (tx *Transaction) GetSigHash() *Hash { return &Hash{types.HomesteadSigner{}.Hash(tx.tx)} } - -// Deprecated: use EthereumClient.TransactionSender -func (tx *Transaction) GetFrom(chainID *BigInt) (address *Address, _ error) { - var signer types.Signer = types.HomesteadSigner{} - if chainID != nil { - signer = types.NewEIP155Signer(chainID.bigint) - } - from, err := types.Sender(signer, tx.tx) - return &Address{from}, err -} - -func (tx *Transaction) GetTo() *Address { - if to := tx.tx.To(); to != nil { - return &Address{*to} - } - return nil -} - -func (tx *Transaction) WithSignature(sig []byte, chainID *BigInt) (signedTx *Transaction, _ error) { - var signer types.Signer = types.HomesteadSigner{} - if chainID != nil { - signer = types.NewEIP155Signer(chainID.bigint) - } - rawTx, err := tx.tx.WithSignature(signer, common.CopyBytes(sig)) - return &Transaction{rawTx}, err -} - -// Transactions represents a slice of transactions. -type Transactions struct{ txs types.Transactions } - -// Size returns the number of transactions in the slice. -func (txs *Transactions) Size() int { - return len(txs.txs) -} - -// Get returns the transaction at the given index from the slice. -func (txs *Transactions) Get(index int) (tx *Transaction, _ error) { - if index < 0 || index >= len(txs.txs) { - return nil, errors.New("index out of bounds") - } - return &Transaction{txs.txs[index]}, nil -} - -// Receipt represents the results of a transaction. -type Receipt struct { - receipt *types.Receipt -} - -// NewReceiptFromRLP parses a transaction receipt from an RLP data dump. -func NewReceiptFromRLP(data []byte) (*Receipt, error) { - r := &Receipt{ - receipt: new(types.Receipt), - } - if err := rlp.DecodeBytes(common.CopyBytes(data), r.receipt); err != nil { - return nil, err - } - return r, nil -} - -// EncodeRLP encodes a transaction receipt into an RLP data dump. -func (r *Receipt) EncodeRLP() ([]byte, error) { - return rlp.EncodeToBytes(r.receipt) -} - -// NewReceiptFromJSON parses a transaction receipt from a JSON data dump. -func NewReceiptFromJSON(data string) (*Receipt, error) { - r := &Receipt{ - receipt: new(types.Receipt), - } - if err := json.Unmarshal([]byte(data), r.receipt); err != nil { - return nil, err - } - return r, nil -} - -// EncodeJSON encodes a transaction receipt into a JSON data dump. -func (r *Receipt) EncodeJSON() (string, error) { - data, err := rlp.EncodeToBytes(r.receipt) - return string(data), err -} - -func (r *Receipt) GetStatus() int { return int(r.receipt.Status) } -func (r *Receipt) GetPostState() []byte { return r.receipt.PostState } -func (r *Receipt) GetCumulativeGasUsed() int64 { return int64(r.receipt.CumulativeGasUsed) } -func (r *Receipt) GetBloom() *Bloom { return &Bloom{r.receipt.Bloom} } -func (r *Receipt) GetLogs() *Logs { return &Logs{r.receipt.Logs} } -func (r *Receipt) GetTxHash() *Hash { return &Hash{r.receipt.TxHash} } -func (r *Receipt) GetContractAddress() *Address { return &Address{r.receipt.ContractAddress} } -func (r *Receipt) GetGasUsed() int64 { return int64(r.receipt.GasUsed) } diff --git a/mobile/vm.go b/mobile/vm.go deleted file mode 100644 index 72093e3d5b..0000000000 --- a/mobile/vm.go +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains all the wrappers from the core/types package. - -package geth - -import ( - "errors" - - "github.com/ethereum/go-ethereum/core/types" -) - -// Log represents a contract log event. These events are generated by the LOG -// opcode and stored/indexed by the node. -type Log struct { - log *types.Log -} - -func (l *Log) GetAddress() *Address { return &Address{l.log.Address} } -func (l *Log) GetTopics() *Hashes { return &Hashes{l.log.Topics} } -func (l *Log) GetData() []byte { return l.log.Data } -func (l *Log) GetBlockNumber() int64 { return int64(l.log.BlockNumber) } -func (l *Log) GetTxHash() *Hash { return &Hash{l.log.TxHash} } -func (l *Log) GetTxIndex() int { return int(l.log.TxIndex) } -func (l *Log) GetBlockHash() *Hash { return &Hash{l.log.BlockHash} } -func (l *Log) GetIndex() int { return int(l.log.Index) } - -// Logs represents a slice of VM logs. -type Logs struct{ logs []*types.Log } - -// Size returns the number of logs in the slice. -func (l *Logs) Size() int { - return len(l.logs) -} - -// Get returns the log at the given index from the slice. -func (l *Logs) Get(index int) (log *Log, _ error) { - if index < 0 || index >= len(l.logs) { - return nil, errors.New("index out of bounds") - } - return &Log{l.logs[index]}, nil -} diff --git a/whisper/mailserver/mailserver.go b/whisper/mailserver/mailserver.go deleted file mode 100644 index d32eaddec3..0000000000 --- a/whisper/mailserver/mailserver.go +++ /dev/null @@ -1,195 +0,0 @@ -// 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 . - -package mailserver - -import ( - "encoding/binary" - "fmt" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/rlp" - whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/util" -) - -type WMailServer struct { - db *leveldb.DB - w *whisper.Whisper - pow float64 - key []byte -} - -type DBKey struct { - timestamp uint32 - hash common.Hash - raw []byte -} - -func NewDbKey(t uint32, h common.Hash) *DBKey { - const sz = common.HashLength + 4 - var k DBKey - k.timestamp = t - k.hash = h - k.raw = make([]byte, sz) - binary.BigEndian.PutUint32(k.raw, k.timestamp) - copy(k.raw[4:], k.hash[:]) - return &k -} - -func (s *WMailServer) Init(shh *whisper.Whisper, path string, password string, pow float64) error { - var err error - if len(path) == 0 { - return fmt.Errorf("DB file is not specified") - } - - if len(password) == 0 { - return fmt.Errorf("password is not specified") - } - - s.db, err = leveldb.OpenFile(path, nil) - if err != nil { - return fmt.Errorf("open DB file: %s", err) - } - - s.w = shh - s.pow = pow - - MailServerKeyID, err := s.w.AddSymKeyFromPassword(password) - if err != nil { - return fmt.Errorf("create symmetric key: %s", err) - } - s.key, err = s.w.GetSymKey(MailServerKeyID) - if err != nil { - return fmt.Errorf("save symmetric key: %s", err) - } - return nil -} - -func (s *WMailServer) Close() { - if s.db != nil { - s.db.Close() - } -} - -func (s *WMailServer) Archive(env *whisper.Envelope) { - key := NewDbKey(env.Expiry-env.TTL, env.Hash()) - rawEnvelope, err := rlp.EncodeToBytes(env) - if err != nil { - log.Error(fmt.Sprintf("rlp.EncodeToBytes failed: %s", err)) - } else { - err = s.db.Put(key.raw, rawEnvelope, nil) - if err != nil { - log.Error(fmt.Sprintf("Writing to DB failed: %s", err)) - } - } -} - -func (s *WMailServer) DeliverMail(peer *whisper.Peer, request *whisper.Envelope) { - if peer == nil { - log.Error("Whisper peer is nil") - return - } - - ok, lower, upper, bloom := s.validateRequest(peer.ID(), request) - if ok { - s.processRequest(peer, lower, upper, bloom) - } -} - -func (s *WMailServer) processRequest(peer *whisper.Peer, lower, upper uint32, bloom []byte) []*whisper.Envelope { - ret := make([]*whisper.Envelope, 0) - var err error - var zero common.Hash - kl := NewDbKey(lower, zero) - ku := NewDbKey(upper, zero) - i := s.db.NewIterator(&util.Range{Start: kl.raw, Limit: ku.raw}, nil) - defer i.Release() - - for i.Next() { - var envelope whisper.Envelope - err = rlp.DecodeBytes(i.Value(), &envelope) - if err != nil { - log.Error(fmt.Sprintf("RLP decoding failed: %s", err)) - } - - if whisper.BloomFilterMatch(bloom, envelope.Bloom()) { - if peer == nil { - // used for test purposes - ret = append(ret, &envelope) - } else { - err = s.w.SendP2PDirect(peer, &envelope) - if err != nil { - log.Error(fmt.Sprintf("Failed to send direct message to peer: %s", err)) - return nil - } - } - } - } - - err = i.Error() - if err != nil { - log.Error(fmt.Sprintf("Level DB iterator error: %s", err)) - } - - return ret -} - -func (s *WMailServer) validateRequest(peerID []byte, request *whisper.Envelope) (bool, uint32, uint32, []byte) { - if s.pow > 0.0 && request.PoW() < s.pow { - return false, 0, 0, nil - } - - f := whisper.Filter{KeySym: s.key} - decrypted := request.Open(&f) - if decrypted == nil { - log.Warn(fmt.Sprintf("Failed to decrypt p2p request")) - return false, 0, 0, nil - } - - src := crypto.FromECDSAPub(decrypted.Src) - if len(src)-len(peerID) == 1 { - src = src[1:] - } - - // if you want to check the signature, you can do it here. e.g.: - // if !bytes.Equal(peerID, src) { - if src == nil { - log.Warn(fmt.Sprintf("Wrong signature of p2p request")) - return false, 0, 0, nil - } - - var bloom []byte - payloadSize := len(decrypted.Payload) - if payloadSize < 8 { - log.Warn(fmt.Sprintf("Undersized p2p request")) - return false, 0, 0, nil - } else if payloadSize == 8 { - bloom = whisper.MakeFullNodeBloom() - } else if payloadSize < 8+whisper.BloomFilterSize { - log.Warn(fmt.Sprintf("Undersized bloom filter in p2p request")) - return false, 0, 0, nil - } else { - bloom = decrypted.Payload[8 : 8+whisper.BloomFilterSize] - } - - lower := binary.BigEndian.Uint32(decrypted.Payload[:4]) - upper := binary.BigEndian.Uint32(decrypted.Payload[4:8]) - return true, lower, upper, bloom -} diff --git a/whisper/mailserver/server_test.go b/whisper/mailserver/server_test.go deleted file mode 100644 index edb817cc75..0000000000 --- a/whisper/mailserver/server_test.go +++ /dev/null @@ -1,212 +0,0 @@ -// 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 . - -package mailserver - -import ( - "bytes" - "crypto/ecdsa" - "encoding/binary" - "io/ioutil" - "math/rand" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - whisper "github.com/ethereum/go-ethereum/whisper/whisperv6" -) - -const powRequirement = 0.00001 - -var keyID string -var shh *whisper.Whisper -var seed = time.Now().Unix() - -type ServerTestParams struct { - topic whisper.TopicType - low uint32 - upp uint32 - key *ecdsa.PrivateKey -} - -func assert(statement bool, text string, t *testing.T) { - if !statement { - t.Fatal(text) - } -} - -func TestDBKey(t *testing.T) { - var h common.Hash - i := uint32(time.Now().Unix()) - k := NewDbKey(i, h) - assert(len(k.raw) == common.HashLength+4, "wrong DB key length", t) - assert(byte(i%0x100) == k.raw[3], "raw representation should be big endian", t) - assert(byte(i/0x1000000) == k.raw[0], "big endian expected", t) -} - -func generateEnvelope(t *testing.T) *whisper.Envelope { - h := crypto.Keccak256Hash([]byte("test sample data")) - params := &whisper.MessageParams{ - KeySym: h[:], - Topic: whisper.TopicType{0x1F, 0x7E, 0xA1, 0x7F}, - Payload: []byte("test payload"), - PoW: powRequirement, - WorkTime: 2, - } - - msg, err := whisper.NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed to wrap with seed %d: %s.", seed, err) - } - return env -} - -func TestMailServer(t *testing.T) { - const password = "password_for_this_test" - const dbPath = "whisper-server-test" - - dir, err := ioutil.TempDir("", dbPath) - if err != nil { - t.Fatal(err) - } - - var server WMailServer - shh = whisper.New(&whisper.DefaultConfig) - shh.RegisterServer(&server) - - err = server.Init(shh, dir, password, powRequirement) - if err != nil { - t.Fatal(err) - } - defer server.Close() - - keyID, err = shh.AddSymKeyFromPassword(password) - if err != nil { - t.Fatalf("Failed to create symmetric key for mail request: %s", err) - } - - rand.Seed(seed) - env := generateEnvelope(t) - server.Archive(env) - deliverTest(t, &server, env) -} - -func deliverTest(t *testing.T, server *WMailServer, env *whisper.Envelope) { - id, err := shh.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair with seed %d: %s.", seed, err) - } - testPeerID, err := shh.GetPrivateKey(id) - if err != nil { - t.Fatalf("failed to retrieve new key pair with seed %d: %s.", seed, err) - } - birth := env.Expiry - env.TTL - p := &ServerTestParams{ - topic: env.Topic, - low: birth - 1, - upp: birth + 1, - key: testPeerID, - } - - singleRequest(t, server, env, p, true) - - p.low, p.upp = birth+1, 0xffffffff - singleRequest(t, server, env, p, false) - - p.low, p.upp = 0, birth-1 - singleRequest(t, server, env, p, false) - - p.low = birth - 1 - p.upp = birth + 1 - p.topic[0] = 0xFF - singleRequest(t, server, env, p, false) -} - -func singleRequest(t *testing.T, server *WMailServer, env *whisper.Envelope, p *ServerTestParams, expect bool) { - request := createRequest(t, p) - src := crypto.FromECDSAPub(&p.key.PublicKey) - ok, lower, upper, bloom := server.validateRequest(src, request) - if !ok { - t.Fatalf("request validation failed, seed: %d.", seed) - } - if lower != p.low { - t.Fatalf("request validation failed (lower bound), seed: %d.", seed) - } - if upper != p.upp { - t.Fatalf("request validation failed (upper bound), seed: %d.", seed) - } - expectedBloom := whisper.TopicToBloom(p.topic) - if !bytes.Equal(bloom, expectedBloom) { - t.Fatalf("request validation failed (topic), seed: %d.", seed) - } - - var exist bool - mail := server.processRequest(nil, p.low, p.upp, bloom) - for _, msg := range mail { - if msg.Hash() == env.Hash() { - exist = true - break - } - } - - if exist != expect { - t.Fatalf("error: exist = %v, seed: %d.", exist, seed) - } - - src[0]++ - ok, lower, upper, bloom = server.validateRequest(src, request) - if !ok { - // request should be valid regardless of signature - t.Fatalf("request validation false negative, seed: %d (lower: %d, upper: %d).", seed, lower, upper) - } -} - -func createRequest(t *testing.T, p *ServerTestParams) *whisper.Envelope { - bloom := whisper.TopicToBloom(p.topic) - data := make([]byte, 8) - binary.BigEndian.PutUint32(data, p.low) - binary.BigEndian.PutUint32(data[4:], p.upp) - data = append(data, bloom...) - - key, err := shh.GetSymKey(keyID) - if err != nil { - t.Fatalf("failed to retrieve sym key with seed %d: %s.", seed, err) - } - - params := &whisper.MessageParams{ - KeySym: key, - Topic: p.topic, - Payload: data, - PoW: powRequirement * 2, - WorkTime: 2, - Src: p.key, - } - - msg, err := whisper.NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed to wrap with seed %d: %s.", seed, err) - } - return env -} diff --git a/whisper/shhclient/client.go b/whisper/shhclient/client.go deleted file mode 100644 index a814154e47..0000000000 --- a/whisper/shhclient/client.go +++ /dev/null @@ -1,193 +0,0 @@ -// 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 . - -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/whisperv6" -) - -// Client defines typed wrappers for the Whisper v6 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) (string, error) { - var result string - 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) -} - -// MarkTrustedPeer 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 string) (string, error) { - var id string - return id, sc.c.CallContext(ctx, &id, "shh_generateSymKeyFromPassword", 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) (string, error) { - var hash string - return hash, sc.c.CallContext(ctx, &hash, "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 (sc *Client) SubscribeMessages(ctx context.Context, criteria whisper.Criteria, ch chan<- *whisper.Message) (ethereum.Subscription, error) { - return sc.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 (sc *Client) NewMessageFilter(ctx context.Context, criteria whisper.Criteria) (string, error) { - var id string - return id, sc.c.CallContext(ctx, &id, "shh_newMessageFilter", criteria) -} - -// DeleteMessageFilter removes the filter associated with the given id. -func (sc *Client) DeleteMessageFilter(ctx context.Context, id string) error { - var ignored bool - return sc.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 (sc *Client) FilterMessages(ctx context.Context, id string) ([]*whisper.Message, error) { - var messages []*whisper.Message - return messages, sc.c.CallContext(ctx, &messages, "shh_getFilterMessages", id) -} diff --git a/whisper/whisperv5/api.go b/whisper/whisperv5/api.go deleted file mode 100644 index 2ce4642202..0000000000 --- a/whisper/whisperv5/api.go +++ /dev/null @@ -1,561 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "context" - "crypto/ecdsa" - "errors" - "fmt" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/rpc" -) - -var ( - ErrSymAsym = errors.New("specify either a symmetric or an asymmetric key") - ErrInvalidSymmetricKey = errors.New("invalid symmetric key") - ErrInvalidPublicKey = errors.New("invalid public key") - ErrInvalidSigningPubKey = errors.New("invalid signing public key") - ErrTooLowPoW = errors.New("message rejected, PoW too low") - ErrNoTopics = errors.New("missing topic(s)") -) - -// PublicWhisperAPI provides the whisper RPC service that can be -// use publicly without security implications. -type PublicWhisperAPI struct { - w *Whisper - - mu sync.Mutex - lastUsed map[string]time.Time // keeps track when a filter was polled for the last time. -} - -// NewPublicWhisperAPI create a new RPC whisper service. -func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { - api := &PublicWhisperAPI{ - w: w, - lastUsed: make(map[string]time.Time), - } - return api -} - -// Version returns the Whisper sub-protocol version. -func (api *PublicWhisperAPI) Version(ctx context.Context) string { - return ProtocolVersionStr -} - -// Info contains diagnostic information. -type Info struct { - Memory int `json:"memory"` // Memory size of the floating messages in bytes. - Messages int `json:"messages"` // Number of floating messages. - MinPow float64 `json:"minPow"` // Minimal accepted PoW - MaxMessageSize uint32 `json:"maxMessageSize"` // Maximum accepted message size -} - -// Info returns diagnostic information about the whisper node. -func (api *PublicWhisperAPI) Info(ctx context.Context) Info { - stats := api.w.Stats() - return Info{ - Memory: stats.memoryUsed, - Messages: len(api.w.messageQueue) + len(api.w.p2pMsgQueue), - MinPow: api.w.MinPow(), - MaxMessageSize: api.w.MaxMessageSize(), - } -} - -// SetMaxMessageSize sets the maximum message size that is accepted. -// Upper limit is defined in whisperv5.MaxMessageSize. -func (api *PublicWhisperAPI) SetMaxMessageSize(ctx context.Context, size uint32) (bool, error) { - return true, api.w.SetMaxMessageSize(size) -} - -// SetMinPoW sets the minimum PoW for a message before it is accepted. -func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) { - return true, api.w.SetMinimumPoW(pow) -} - -// MarkTrustedPeer marks a 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 (api *PublicWhisperAPI) MarkTrustedPeer(ctx context.Context, enode string) (bool, error) { - n, err := discover.ParseNode(enode) - if err != nil { - return false, err - } - return true, api.w.AllowP2PMessagesFromPeer(n.ID[:]) -} - -// NewKeyPair generates a new public and private key pair for message decryption and encryption. -// It returns an ID that can be used to refer to the keypair. -func (api *PublicWhisperAPI) NewKeyPair(ctx context.Context) (string, error) { - return api.w.NewKeyPair() -} - -// AddPrivateKey imports the given private key. -func (api *PublicWhisperAPI) AddPrivateKey(ctx context.Context, privateKey hexutil.Bytes) (string, error) { - key, err := crypto.ToECDSA(privateKey) - if err != nil { - return "", err - } - return api.w.AddKeyPair(key) -} - -// DeleteKeyPair removes the key with the given key if it exists. -func (api *PublicWhisperAPI) DeleteKeyPair(ctx context.Context, key string) (bool, error) { - if ok := api.w.DeleteKeyPair(key); ok { - return true, nil - } - return false, fmt.Errorf("key pair %s not found", key) -} - -// HasKeyPair returns an indication if the node has a key pair that is associated with the given id. -func (api *PublicWhisperAPI) HasKeyPair(ctx context.Context, id string) bool { - return api.w.HasKeyPair(id) -} - -// GetPublicKey returns the public key associated with the given key. The key is the hex -// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62. -func (api *PublicWhisperAPI) GetPublicKey(ctx context.Context, id string) (hexutil.Bytes, error) { - key, err := api.w.GetPrivateKey(id) - if err != nil { - return hexutil.Bytes{}, err - } - return crypto.FromECDSAPub(&key.PublicKey), nil -} - -// GetPrivateKey returns the private key associated with the given key. The key is the hex -// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62. -func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) { - key, err := api.w.GetPrivateKey(id) - if err != nil { - return hexutil.Bytes{}, err - } - return crypto.FromECDSA(key), nil -} - -// NewSymKey generate a random symmetric key. -// It returns an ID that can be used to refer to the key. -// Can be used encrypting and decrypting messages where the key is known to both parties. -func (api *PublicWhisperAPI) NewSymKey(ctx context.Context) (string, error) { - return api.w.GenerateSymKey() -} - -// AddSymKey import a symmetric key. -// It returns an ID that can be used to refer to the key. -// Can be used encrypting and decrypting messages where the key is known to both parties. -func (api *PublicWhisperAPI) AddSymKey(ctx context.Context, key hexutil.Bytes) (string, error) { - return api.w.AddSymKeyDirect([]byte(key)) -} - -// GenerateSymKeyFromPassword derive a key from the given password, stores it, and returns its ID. -func (api *PublicWhisperAPI) GenerateSymKeyFromPassword(ctx context.Context, passwd string) (string, error) { - return api.w.AddSymKeyFromPassword(passwd) -} - -// HasSymKey returns an indication if the node has a symmetric key associated with the given key. -func (api *PublicWhisperAPI) HasSymKey(ctx context.Context, id string) bool { - return api.w.HasSymKey(id) -} - -// GetSymKey returns the symmetric key associated with the given id. -func (api *PublicWhisperAPI) GetSymKey(ctx context.Context, id string) (hexutil.Bytes, error) { - return api.w.GetSymKey(id) -} - -// DeleteSymKey deletes the symmetric key that is associated with the given id. -func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool { - return api.w.DeleteSymKey(id) -} - -//go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go - -// NewMessage represents a new whisper message that is posted through the RPC. -type NewMessage struct { - SymKeyID string `json:"symKeyID"` - PublicKey []byte `json:"pubKey"` - Sig string `json:"sig"` - TTL uint32 `json:"ttl"` - Topic TopicType `json:"topic"` - Payload []byte `json:"payload"` - Padding []byte `json:"padding"` - PowTime uint32 `json:"powTime"` - PowTarget float64 `json:"powTarget"` - TargetPeer string `json:"targetPeer"` -} - -type newMessageOverride struct { - PublicKey hexutil.Bytes - Payload hexutil.Bytes - Padding hexutil.Bytes -} - -// Post a message on the Whisper network. -func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (bool, error) { - var ( - symKeyGiven = len(req.SymKeyID) > 0 - pubKeyGiven = len(req.PublicKey) > 0 - err error - ) - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { - return false, ErrSymAsym - } - - params := &MessageParams{ - TTL: req.TTL, - Payload: req.Payload, - Padding: req.Padding, - WorkTime: req.PowTime, - PoW: req.PowTarget, - Topic: req.Topic, - } - - // Set key that is used to sign the message - if len(req.Sig) > 0 { - if params.Src, err = api.w.GetPrivateKey(req.Sig); err != nil { - return false, err - } - } - - // Set symmetric key that is used to encrypt the message - if symKeyGiven { - if params.Topic == (TopicType{}) { // topics are mandatory with symmetric encryption - return false, ErrNoTopics - } - if params.KeySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { - return false, err - } - if !validateSymmetricKey(params.KeySym) { - return false, ErrInvalidSymmetricKey - } - } - - // Set asymmetric key that is used to encrypt the message - if pubKeyGiven { - if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil { - return false, ErrInvalidPublicKey - } - } - - // encrypt and sent message - whisperMsg, err := NewSentMessage(params) - if err != nil { - return false, err - } - - env, err := whisperMsg.Wrap(params) - if err != nil { - return false, err - } - - // send to specific node (skip PoW check) - if len(req.TargetPeer) > 0 { - n, err := discover.ParseNode(req.TargetPeer) - if err != nil { - return false, fmt.Errorf("failed to parse target peer: %s", err) - } - return true, api.w.SendP2PMessage(n.ID[:], env) - } - - // ensure that the message PoW meets the node's minimum accepted PoW - if req.PowTarget < api.w.MinPow() { - return false, ErrTooLowPoW - } - - return true, api.w.Send(env) -} - -//go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go - -// Criteria holds various filter options for inbound messages. -type Criteria struct { - SymKeyID string `json:"symKeyID"` - PrivateKeyID string `json:"privateKeyID"` - Sig []byte `json:"sig"` - MinPow float64 `json:"minPow"` - Topics []TopicType `json:"topics"` - AllowP2P bool `json:"allowP2P"` -} - -type criteriaOverride struct { - Sig hexutil.Bytes -} - -// Messages set up a subscription that fires events when messages arrive that match -// the given set of criteria. -func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.Subscription, error) { - var ( - symKeyGiven = len(crit.SymKeyID) > 0 - pubKeyGiven = len(crit.PrivateKeyID) > 0 - err error - ) - - // ensure that the RPC connection supports subscriptions - notifier, supported := rpc.NotifierFromContext(ctx) - if !supported { - return nil, rpc.ErrNotificationsUnsupported - } - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { - return nil, ErrSymAsym - } - - filter := Filter{ - PoW: crit.MinPow, - Messages: make(map[common.Hash]*ReceivedMessage), - AllowP2P: crit.AllowP2P, - } - - if len(crit.Sig) > 0 { - if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil { - return nil, ErrInvalidSigningPubKey - } - } - - for i, bt := range crit.Topics { - if len(bt) == 0 || len(bt) > 4 { - return nil, fmt.Errorf("subscribe: topic %d has wrong size: %d", i, len(bt)) - } - filter.Topics = append(filter.Topics, bt[:]) - } - - // listen for message that are encrypted with the given symmetric key - if symKeyGiven { - if len(filter.Topics) == 0 { - return nil, ErrNoTopics - } - key, err := api.w.GetSymKey(crit.SymKeyID) - if err != nil { - return nil, err - } - if !validateSymmetricKey(key) { - return nil, ErrInvalidSymmetricKey - } - filter.KeySym = key - filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) - } - - // listen for messages that are encrypted with the given public key - if pubKeyGiven { - filter.KeyAsym, err = api.w.GetPrivateKey(crit.PrivateKeyID) - if err != nil || filter.KeyAsym == nil { - return nil, ErrInvalidPublicKey - } - } - - id, err := api.w.Subscribe(&filter) - if err != nil { - return nil, err - } - - // create subscription and start waiting for message events - rpcSub := notifier.CreateSubscription() - go func() { - // for now poll internally, refactor whisper internal for channel support - ticker := time.NewTicker(250 * time.Millisecond) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - if filter := api.w.GetFilter(id); filter != nil { - for _, rpcMessage := range toMessage(filter.Retrieve()) { - if err := notifier.Notify(rpcSub.ID, rpcMessage); err != nil { - log.Error("Failed to send notification", "err", err) - } - } - } - case <-rpcSub.Err(): - api.w.Unsubscribe(id) - return - case <-notifier.Closed(): - api.w.Unsubscribe(id) - return - } - } - }() - - return rpcSub, nil -} - -//go:generate gencodec -type Message -field-override messageOverride -out gen_message_json.go - -// Message is the RPC representation of a whisper message. -type Message struct { - Sig []byte `json:"sig,omitempty"` - TTL uint32 `json:"ttl"` - Timestamp uint32 `json:"timestamp"` - Topic TopicType `json:"topic"` - Payload []byte `json:"payload"` - Padding []byte `json:"padding"` - PoW float64 `json:"pow"` - Hash []byte `json:"hash"` - Dst []byte `json:"recipientPublicKey,omitempty"` -} - -type messageOverride struct { - Sig hexutil.Bytes - Payload hexutil.Bytes - Padding hexutil.Bytes - Hash hexutil.Bytes - Dst hexutil.Bytes -} - -// ToWhisperMessage converts an internal message into an API version. -func ToWhisperMessage(message *ReceivedMessage) *Message { - msg := Message{ - Payload: message.Payload, - Padding: message.Padding, - Timestamp: message.Sent, - TTL: message.TTL, - PoW: message.PoW, - Hash: message.EnvelopeHash.Bytes(), - Topic: message.Topic, - } - - if message.Dst != nil { - b := crypto.FromECDSAPub(message.Dst) - if b != nil { - msg.Dst = b - } - } - - if isMessageSigned(message.Raw[0]) { - b := crypto.FromECDSAPub(message.SigToPubKey()) - if b != nil { - msg.Sig = b - } - } - - return &msg -} - -// toMessage converts a set of messages to its RPC representation. -func toMessage(messages []*ReceivedMessage) []*Message { - msgs := make([]*Message, len(messages)) - for i, msg := range messages { - msgs[i] = ToWhisperMessage(msg) - } - return msgs -} - -// GetFilterMessages returns the messages that match the filter criteria and -// are received between the last poll and now. -func (api *PublicWhisperAPI) GetFilterMessages(id string) ([]*Message, error) { - api.mu.Lock() - f := api.w.GetFilter(id) - if f == nil { - api.mu.Unlock() - return nil, fmt.Errorf("filter not found") - } - api.lastUsed[id] = time.Now() - api.mu.Unlock() - - receivedMessages := f.Retrieve() - messages := make([]*Message, 0, len(receivedMessages)) - for _, msg := range receivedMessages { - messages = append(messages, ToWhisperMessage(msg)) - } - - return messages, nil -} - -// DeleteMessageFilter deletes a filter. -func (api *PublicWhisperAPI) DeleteMessageFilter(id string) (bool, error) { - api.mu.Lock() - defer api.mu.Unlock() - - delete(api.lastUsed, id) - return true, api.w.Unsubscribe(id) -} - -// NewMessageFilter creates a new filter that can be used to poll for -// (new) messages that satisfy the given criteria. -func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) { - var ( - src *ecdsa.PublicKey - keySym []byte - keyAsym *ecdsa.PrivateKey - topics [][]byte - - symKeyGiven = len(req.SymKeyID) > 0 - asymKeyGiven = len(req.PrivateKeyID) > 0 - - err error - ) - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && asymKeyGiven) || (!symKeyGiven && !asymKeyGiven) { - return "", ErrSymAsym - } - - if len(req.Sig) > 0 { - if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil { - return "", ErrInvalidSigningPubKey - } - } - - if symKeyGiven { - if keySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { - return "", err - } - if !validateSymmetricKey(keySym) { - return "", ErrInvalidSymmetricKey - } - } - - if asymKeyGiven { - if keyAsym, err = api.w.GetPrivateKey(req.PrivateKeyID); err != nil { - return "", err - } - } - - if len(req.Topics) > 0 { - topics = make([][]byte, 0, len(req.Topics)) - for _, topic := range req.Topics { - topics = append(topics, topic[:]) - } - } - - f := &Filter{ - Src: src, - KeySym: keySym, - KeyAsym: keyAsym, - PoW: req.MinPow, - AllowP2P: req.AllowP2P, - Topics: topics, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - id, err := api.w.Subscribe(f) - if err != nil { - return "", err - } - - api.mu.Lock() - api.lastUsed[id] = time.Now() - api.mu.Unlock() - - return id, nil -} diff --git a/whisper/whisperv5/benchmarks_test.go b/whisper/whisperv5/benchmarks_test.go deleted file mode 100644 index dcfbcb56d8..0000000000 --- a/whisper/whisperv5/benchmarks_test.go +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "testing" - - "github.com/ethereum/go-ethereum/crypto" -) - -func BenchmarkDeriveKeyMaterial(b *testing.B) { - for i := 0; i < b.N; i++ { - deriveKeyMaterial([]byte("test"), 0) - } -} - -func BenchmarkEncryptionSym(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - for i := 0; i < b.N; i++ { - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Errorf("failed Wrap with seed %d: %s.", seed, err) - b.Errorf("i = %d, len(msg.Raw) = %d, params.Payload = %d.", i, len(msg.Raw), len(params.Payload)) - return - } - } -} - -func BenchmarkEncryptionAsym(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - - for i := 0; i < b.N; i++ { - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - } -} - -func BenchmarkDecryptionSymValid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - f := Filter{KeySym: params.KeySym} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg == nil { - b.Fatalf("failed to open with seed %d.", seed) - } - } -} - -func BenchmarkDecryptionSymInvalid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - f := Filter{KeySym: []byte("arbitrary stuff here")} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg != nil { - b.Fatalf("opened envelope with invalid key, seed: %d.", seed) - } - } -} - -func BenchmarkDecryptionAsymValid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - f := Filter{KeyAsym: key} - params.KeySym = nil - params.Dst = &key.PublicKey - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg == nil { - b.Fatalf("fail to open, seed: %d.", seed) - } - } -} - -func BenchmarkDecryptionAsymInvalid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - key, err = crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - f := Filter{KeyAsym: key} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg != nil { - b.Fatalf("opened envelope with invalid key, seed: %d.", seed) - } - } -} - -func increment(x []byte) { - for i := 0; i < len(x); i++ { - x[i]++ - if x[i] != 0 { - break - } - } -} - -func BenchmarkPoW(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - params.Payload = make([]byte, 32) - params.PoW = 10.0 - params.TTL = 1 - - for i := 0; i < b.N; i++ { - increment(params.Payload) - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - } -} diff --git a/whisper/whisperv5/config.go b/whisper/whisperv5/config.go deleted file mode 100644 index fcc2307046..0000000000 --- a/whisper/whisperv5/config.go +++ /dev/null @@ -1,27 +0,0 @@ -// 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 . - -package whisperv5 - -type Config struct { - MaxMessageSize uint32 `toml:",omitempty"` - MinimumAcceptedPOW float64 `toml:",omitempty"` -} - -var DefaultConfig = Config{ - MaxMessageSize: DefaultMaxMessageSize, - MinimumAcceptedPOW: DefaultMinimumPoW, -} diff --git a/whisper/whisperv5/doc.go b/whisper/whisperv5/doc.go deleted file mode 100644 index 8161db8ed6..0000000000 --- a/whisper/whisperv5/doc.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -/* -Package whisperv5 implements the Whisper protocol (version 5). - -Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP). -As such it may be likened and compared to both, not dissimilar to the -matter/energy duality (apologies to physicists for the blatant abuse of a -fundamental and beautiful natural principle). - -Whisper is a pure identity-based messaging system. Whisper provides a low-level -(non-application-specific) but easily-accessible API without being based upon -or prejudiced by the low-level hardware attributes and characteristics, -particularly the notion of singular endpoints. -*/ -package whisperv5 - -import ( - "fmt" - "time" -) - -const ( - EnvelopeVersion = uint64(0) - ProtocolVersion = uint64(5) - ProtocolVersionStr = "5.0" - ProtocolName = "shh" - - statusCode = 0 // used by whisper protocol - messagesCode = 1 // normal whisper message - p2pCode = 2 // peer-to-peer message (to be consumed by the peer, but not forwarded any further) - p2pRequestCode = 3 // peer-to-peer message, used by Dapp protocol - NumberOfMessageCodes = 64 - - paddingMask = byte(3) - signatureFlag = byte(4) - - TopicLength = 4 - signatureLength = 65 - aesKeyLength = 32 - AESNonceLength = 12 - keyIdSize = 32 - - MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message. - 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) - messageQueueLimit = 1024 - - expirationCycle = time.Second - transmissionCycle = 300 * time.Millisecond - - DefaultTTL = 50 // seconds - SynchAllowance = 10 // seconds -) - -type unknownVersionError uint64 - -func (e unknownVersionError) Error() string { - return fmt.Sprintf("invalid envelope version %d", uint64(e)) -} - -// MailServer represents a mail server, capable of -// archiving the old messages for subsequent delivery -// to the peers. Any implementation must ensure that both -// functions are thread-safe. Also, they must return ASAP. -// DeliverMail should use directMessagesCode for delivery, -// in order to bypass the expiry checks. -type MailServer interface { - Archive(env *Envelope) - DeliverMail(whisperPeer *Peer, request *Envelope) -} diff --git a/whisper/whisperv5/envelope.go b/whisper/whisperv5/envelope.go deleted file mode 100644 index 169cbba9dd..0000000000 --- a/whisper/whisperv5/envelope.go +++ /dev/null @@ -1,246 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Envelope element. - -package whisperv5 - -import ( - "crypto/ecdsa" - "encoding/binary" - "fmt" - gmath "math" - "math/big" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/ecies" - "github.com/ethereum/go-ethereum/rlp" -) - -// Envelope represents a clear-text data packet to transmit through the Whisper -// network. Its contents may or may not be encrypted and signed. -type Envelope struct { - Version []byte - Expiry uint32 - TTL uint32 - Topic TopicType - AESNonce []byte - Data []byte - EnvNonce uint64 - - pow float64 // Message-specific PoW as described in the Whisper specification. - hash common.Hash // Cached hash of the envelope to avoid rehashing every time. - // 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, 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, - AESNonce: aesNonce, - Data: msg.Raw, - EnvNonce: 0, - } - - if EnvelopeVersion < 256 { - env.Version[0] = byte(EnvelopeVersion) - } else { - panic("please increase the size of Envelope.Version before releasing this version") - } - - return &env -} - -func (e *Envelope) IsSymmetric() bool { - return len(e.AESNonce) > 0 -} - -func (e *Envelope) isAsymmetric() bool { - return !e.IsSymmetric() -} - -func (e *Envelope) Ver() uint64 { - return bytesToUintLittleEndian(e.Version) -} - -// Seal closes the envelope by spending the requested amount of time as a proof -// of work on hashing the data. -func (e *Envelope) Seal(options *MessageParams) error { - var target, bestBit int - if options.PoW == 0 { - // adjust for the duration of Seal() execution only if execution time is predefined unconditionally - e.Expiry += options.WorkTime - } else { - target = e.powToFirstBit(options.PoW) - if target < 1 { - target = 1 - } - } - - buf := make([]byte, 64) - h := crypto.Keccak256(e.rlpWithoutNonce()) - copy(buf[:32], h) - - finish := time.Now().Add(time.Duration(options.WorkTime) * time.Second).UnixNano() - for nonce := uint64(0); time.Now().UnixNano() < finish; { - for i := 0; i < 1024; i++ { - binary.BigEndian.PutUint64(buf[56:], nonce) - d := new(big.Int).SetBytes(crypto.Keccak256(buf)) - firstBit := math.FirstBitSet(d) - if firstBit > bestBit { - e.EnvNonce, bestBit = nonce, firstBit - if target > 0 && bestBit >= target { - return nil - } - } - nonce++ - } - } - - if target > 0 && bestBit < target { - return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime) - } - - return nil -} - -func (e *Envelope) PoW() float64 { - if e.pow == 0 { - e.calculatePoW(0) - } - return e.pow -} - -func (e *Envelope) calculatePoW(diff uint32) { - buf := make([]byte, 64) - h := crypto.Keccak256(e.rlpWithoutNonce()) - copy(buf[:32], h) - binary.BigEndian.PutUint64(buf[56:], e.EnvNonce) - d := new(big.Int).SetBytes(crypto.Keccak256(buf)) - firstBit := math.FirstBitSet(d) - x := gmath.Pow(2, float64(firstBit)) - x /= float64(e.size()) - x /= float64(e.TTL + diff) - e.pow = x -} - -func (e *Envelope) powToFirstBit(pow float64) int { - x := pow - x *= float64(e.size()) - x *= float64(e.TTL) - bits := gmath.Log2(x) - bits = gmath.Ceil(bits) - return int(bits) -} - -// 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{}) { - encoded, _ := rlp.EncodeToBytes(e) - e.hash = crypto.Keccak256Hash(encoded) - } - return e.hash -} - -// DecodeRLP decodes an Envelope from an RLP data stream. -func (e *Envelope) DecodeRLP(s *rlp.Stream) error { - raw, err := s.Raw() - if err != nil { - return err - } - // The decoding of Envelope uses the struct fields but also needs - // to compute the hash of the whole RLP-encoded envelope. This - // type has the same structure as Envelope but is not an - // rlp.Decoder (does not implement DecodeRLP function). - // Only public members will be encoded. - type rlpenv Envelope - if err := rlp.DecodeBytes(raw, (*rlpenv)(e)); err != nil { - return err - } - e.hash = crypto.Keccak256Hash(raw) - return nil -} - -// OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key. -func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, error) { - message := &ReceivedMessage{Raw: e.Data} - err := message.decryptAsymmetric(key) - switch err { - case nil: - return message, nil - case ecies.ErrInvalidPublicKey: // addressed to somebody else - return nil, err - default: - return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err) - } -} - -// 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.AESNonce) - if err != nil { - msg = nil - } - return msg, err -} - -// Open tries to decrypt an envelope, and populates the message fields in case of success. -func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) { - if e.isAsymmetric() { - msg, _ = e.OpenAsymmetric(watcher.KeyAsym) - if msg != nil { - msg.Dst = &watcher.KeyAsym.PublicKey - } - } else if e.IsSymmetric() { - msg, _ = e.OpenSymmetric(watcher.KeySym) - if msg != nil { - msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) - } - } - - if msg != nil { - ok := msg.Validate() - if !ok { - return nil - } - msg.Topic = e.Topic - msg.PoW = e.PoW() - msg.TTL = e.TTL - msg.Sent = e.Expiry - e.TTL - msg.EnvelopeHash = e.Hash() - msg.EnvelopeVersion = e.Ver() - } - return msg -} diff --git a/whisper/whisperv5/filter.go b/whisper/whisperv5/filter.go deleted file mode 100644 index 3190334ebb..0000000000 --- a/whisper/whisperv5/filter.go +++ /dev/null @@ -1,243 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "crypto/ecdsa" - "fmt" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" -) - -type Filter struct { - Src *ecdsa.PublicKey // Sender of the message - KeyAsym *ecdsa.PrivateKey // Private Key of recipient - KeySym []byte // Key associated with the Topic - Topics [][]byte // Topics to filter messages with - PoW float64 // Proof of work as described in the Whisper spec - AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages - SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization - - Messages map[common.Hash]*ReceivedMessage - mutex sync.RWMutex -} - -type Filters struct { - watchers map[string]*Filter - whisper *Whisper - mutex sync.RWMutex -} - -func NewFilters(w *Whisper) *Filters { - return &Filters{ - watchers: make(map[string]*Filter), - whisper: w, - } -} - -func (fs *Filters) Install(watcher *Filter) (string, error) { - if watcher.Messages == nil { - watcher.Messages = make(map[common.Hash]*ReceivedMessage) - } - - id, err := GenerateRandomID() - if err != nil { - return "", err - } - - fs.mutex.Lock() - defer fs.mutex.Unlock() - - if fs.watchers[id] != nil { - return "", fmt.Errorf("failed to generate unique ID") - } - - if watcher.expectsSymmetricEncryption() { - watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) - } - - fs.watchers[id] = watcher - return id, err -} - -func (fs *Filters) Uninstall(id string) bool { - fs.mutex.Lock() - defer fs.mutex.Unlock() - if fs.watchers[id] != nil { - delete(fs.watchers, id) - return true - } - return false -} - -func (fs *Filters) Get(id string) *Filter { - fs.mutex.RLock() - defer fs.mutex.RUnlock() - return fs.watchers[id] -} - -func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { - var msg *ReceivedMessage - - fs.mutex.RLock() - defer fs.mutex.RUnlock() - - i := -1 // only used for logging info - for _, watcher := range fs.watchers { - i++ - if p2pMessage && !watcher.AllowP2P { - log.Trace(fmt.Sprintf("msg [%x], filter [%d]: p2p messages are not allowed", env.Hash(), i)) - continue - } - - var match bool - if msg != nil { - match = watcher.MatchMessage(msg) - } else { - match = watcher.MatchEnvelope(env) - if match { - msg = env.Open(watcher) - if msg == nil { - log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", i) - } - } else { - log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", i) - } - } - - if match && msg != nil { - log.Trace("processing message: decrypted", "hash", env.Hash().Hex()) - if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) { - watcher.Trigger(msg) - } - } - } -} - -func (f *Filter) processEnvelope(env *Envelope) *ReceivedMessage { - if f.MatchEnvelope(env) { - msg := env.Open(f) - if msg != nil { - return msg - } else { - log.Trace("processing envelope: failed to open", "hash", env.Hash().Hex()) - } - } else { - log.Trace("processing envelope: does not match", "hash", env.Hash().Hex()) - } - return nil -} - -func (f *Filter) expectsAsymmetricEncryption() bool { - return f.KeyAsym != nil -} - -func (f *Filter) expectsSymmetricEncryption() bool { - return f.KeySym != nil -} - -func (f *Filter) Trigger(msg *ReceivedMessage) { - f.mutex.Lock() - defer f.mutex.Unlock() - - if _, exist := f.Messages[msg.EnvelopeHash]; !exist { - f.Messages[msg.EnvelopeHash] = msg - } -} - -func (f *Filter) Retrieve() (all []*ReceivedMessage) { - f.mutex.Lock() - defer f.mutex.Unlock() - - all = make([]*ReceivedMessage, 0, len(f.Messages)) - for _, msg := range f.Messages { - all = append(all, msg) - } - - f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages - return all -} - -func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { - if f.PoW > 0 && msg.PoW < f.PoW { - return false - } - - if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() { - return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) && f.MatchTopic(msg.Topic) - } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() { - return f.SymKeyHash == msg.SymKeyHash && f.MatchTopic(msg.Topic) - } - return false -} - -func (f *Filter) MatchEnvelope(envelope *Envelope) bool { - if f.PoW > 0 && envelope.pow < f.PoW { - return false - } - - if f.expectsAsymmetricEncryption() && envelope.isAsymmetric() { - return f.MatchTopic(envelope.Topic) - } else if f.expectsSymmetricEncryption() && envelope.IsSymmetric() { - return f.MatchTopic(envelope.Topic) - } - return false -} - -func (f *Filter) MatchTopic(topic TopicType) bool { - if len(f.Topics) == 0 { - // any topic matches - return true - } - - for _, bt := range f.Topics { - if matchSingleTopic(topic, bt) { - return true - } - } - return false -} - -func matchSingleTopic(topic TopicType, bt []byte) bool { - if len(bt) > TopicLength { - bt = bt[:TopicLength] - } - - if len(bt) < TopicLength { - return false - } - - for j, b := range bt { - if topic[j] != b { - return false - } - } - return true -} - -func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool { - if !ValidatePublicKey(a) { - return false - } else if !ValidatePublicKey(b) { - return false - } - // the curve is always the same, just compare the points - return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 -} diff --git a/whisper/whisperv5/filter_test.go b/whisper/whisperv5/filter_test.go deleted file mode 100644 index 01034a3513..0000000000 --- a/whisper/whisperv5/filter_test.go +++ /dev/null @@ -1,848 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "math/big" - mrand "math/rand" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -var seed int64 - -// InitSingleTest should be called in the beginning of every -// test, which uses RNG, in order to make the tests -// reproduciblity independent of their sequence. -func InitSingleTest() { - seed = time.Now().Unix() - mrand.Seed(seed) -} - -func InitDebugTest(i int64) { - seed = i - mrand.Seed(seed) -} - -type FilterTestCase struct { - f *Filter - id string - alive bool - msgCnt int -} - -func generateFilter(t *testing.T, symmetric bool) (*Filter, error) { - var f Filter - f.Messages = make(map[common.Hash]*ReceivedMessage) - - const topicNum = 8 - f.Topics = make([][]byte, topicNum) - for i := 0; i < topicNum; i++ { - f.Topics[i] = make([]byte, 4) - mrand.Read(f.Topics[i][:]) - f.Topics[i][0] = 0x01 - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("generateFilter 1 failed with seed %d.", seed) - return nil, err - } - f.Src = &key.PublicKey - - if symmetric { - f.KeySym = make([]byte, aesKeyLength) - mrand.Read(f.KeySym) - f.SymKeyHash = crypto.Keccak256Hash(f.KeySym) - } else { - f.KeyAsym, err = crypto.GenerateKey() - if err != nil { - t.Fatalf("generateFilter 2 failed with seed %d.", seed) - return nil, err - } - } - - // AcceptP2P & PoW are not set - return &f, nil -} - -func generateTestCases(t *testing.T, SizeTestFilters int) []FilterTestCase { - cases := make([]FilterTestCase, SizeTestFilters) - for i := 0; i < SizeTestFilters; i++ { - f, _ := generateFilter(t, true) - cases[i].f = f - cases[i].alive = mrand.Int()&int(1) == 0 - } - return cases -} - -func TestInstallFilters(t *testing.T) { - InitSingleTest() - - const SizeTestFilters = 256 - w := New(&Config{}) - filters := NewFilters(w) - tst := generateTestCases(t, SizeTestFilters) - - var err error - var j string - for i := 0; i < SizeTestFilters; i++ { - j, err = filters.Install(tst[i].f) - if err != nil { - t.Fatalf("seed %d: failed to install filter: %s", seed, err) - } - tst[i].id = j - if len(j) != keyIdSize*2 { - t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j)) - } - } - - for _, testCase := range tst { - if !testCase.alive { - filters.Uninstall(testCase.id) - } - } - - for i, testCase := range tst { - fil := filters.Get(testCase.id) - exist := fil != nil - if exist != testCase.alive { - t.Fatalf("seed %d: failed alive: %d, %v, %v", seed, i, exist, testCase.alive) - } - if exist && fil.PoW != testCase.f.PoW { - t.Fatalf("seed %d: failed Get: %d, %v, %v", seed, i, exist, testCase.alive) - } - } -} - -func TestInstallSymKeyGeneratesHash(t *testing.T) { - InitSingleTest() - - w := New(&Config{}) - filters := NewFilters(w) - filter, _ := generateFilter(t, true) - - // save the current SymKeyHash for comparison - initialSymKeyHash := filter.SymKeyHash - - // ensure the SymKeyHash is invalid, for Install to recreate it - var invalid common.Hash - filter.SymKeyHash = invalid - - _, err := filters.Install(filter) - - if err != nil { - t.Fatalf("Error installing the filter: %s", err) - } - - for i, b := range filter.SymKeyHash { - if b != initialSymKeyHash[i] { - t.Fatalf("The filter's symmetric key hash was not properly generated by Install") - } - } -} - -func TestInstallIdenticalFilters(t *testing.T) { - InitSingleTest() - - w := New(&Config{}) - filters := NewFilters(w) - filter1, _ := generateFilter(t, true) - - // Copy the first filter since some of its fields - // are randomly gnerated. - filter2 := &Filter{ - KeySym: filter1.KeySym, - Topics: filter1.Topics, - PoW: filter1.PoW, - AllowP2P: filter1.AllowP2P, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - _, err := filters.Install(filter1) - - if err != nil { - t.Fatalf("Error installing the first filter with seed %d: %s", seed, err) - } - - _, err = filters.Install(filter2) - - if err != nil { - t.Fatalf("Error installing the second filter with seed %d: %s", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("Error generating message parameters with seed %d: %s", seed, err) - } - - params.KeySym = filter1.KeySym - params.Topic = BytesToTopic(filter1.Topics[0]) - - filter1.Src = ¶ms.Src.PublicKey - filter2.Src = ¶ms.Src.PublicKey - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(filter1) - if msg == nil { - t.Fatalf("failed to Open with filter1") - } - - if !filter1.MatchEnvelope(env) { - t.Fatalf("failed matching with the first filter") - } - - if !filter2.MatchEnvelope(env) { - t.Fatalf("failed matching with the first filter") - } - - if !filter1.MatchMessage(msg) { - t.Fatalf("failed matching with the second filter") - } - - if !filter2.MatchMessage(msg) { - t.Fatalf("failed matching with the second filter") - } -} - -func TestComparePubKey(t *testing.T) { - InitSingleTest() - - key1, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate first key with seed %d: %s.", seed, err) - } - key2, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate second key with seed %d: %s.", seed, err) - } - if IsPubKeyEqual(&key1.PublicKey, &key2.PublicKey) { - t.Fatalf("public keys are equal, seed %d.", seed) - } - - // generate key3 == key1 - mrand.Seed(seed) - key3, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate third key with seed %d: %s.", seed, err) - } - if IsPubKeyEqual(&key1.PublicKey, &key3.PublicKey) { - t.Fatalf("key1 == key3, seed %d.", seed) - } -} - -func TestMatchEnvelope(t *testing.T) { - InitSingleTest() - - fsym, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - fasym, err := generateFilter(t, false) - if err != nil { - t.Fatalf("failed generateFilter() with seed %d: %s.", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.Topic[0] = 0xFF // ensure mismatch - - // mismatch with pseudo-random data - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - match := fsym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope symmetric with seed %d.", seed) - } - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope asymmetric with seed %d.", seed) - } - - // encrypt symmetrically - i := mrand.Int() % 4 - fsym.Topics[i] = params.Topic[:] - fasym.Topics[i] = params.Topic[:] - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap() with seed %d: %s.", seed, err) - } - - // symmetric + matching topic: match - match = fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed) - } - - // asymmetric + matching topic: mismatch - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope() asymmetric with seed %d.", seed) - } - - // symmetric + matching topic + insufficient PoW: mismatch - fsym.PoW = env.PoW() + 1.0 - match = fsym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(symmetric + matching topic + insufficient PoW) asymmetric with seed %d.", seed) - } - - // symmetric + matching topic + sufficient PoW: match - fsym.PoW = env.PoW() / 2 - match = fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(symmetric + matching topic + sufficient PoW) with seed %d.", seed) - } - - // symmetric + topics are nil (wildcard): match - prevTopics := fsym.Topics - fsym.Topics = nil - match = fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(symmetric + topics are nil) with seed %d.", seed) - } - fsym.Topics = prevTopics - - // encrypt asymmetrically - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap() with seed %d: %s.", seed, err) - } - - // encryption method mismatch - match = fsym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } - - // asymmetric + mismatching topic: mismatch - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + mismatching topic) with seed %d.", seed) - } - - // asymmetric + matching topic: match - fasym.Topics[i] = fasym.Topics[i+1] - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(asymmetric + matching topic) with seed %d.", seed) - } - - // asymmetric + filter without topic (wildcard): match - fasym.Topics = nil - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + filter without topic) with seed %d.", seed) - } - - // asymmetric + insufficient PoW: mismatch - fasym.PoW = env.PoW() + 1.0 - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(asymmetric + insufficient PoW) with seed %d.", seed) - } - - // asymmetric + sufficient PoW: match - fasym.PoW = env.PoW() / 2 - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + sufficient PoW) with seed %d.", seed) - } - - // filter without topic + envelope without topic: match - env.Topic = TopicType{} - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed) - } - - // filter with topic + envelope without topic: mismatch - fasym.Topics = fsym.Topics - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed) - } -} - -func TestMatchMessageSym(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - const index = 1 - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[index]) - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(f) - if msg == nil { - t.Fatalf("failed Open with seed %d.", seed) - } - - // Src: match - *f.Src.X = *params.Src.PublicKey.X - *f.Src.Y = *params.Src.PublicKey.Y - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src match) with seed %d.", seed) - } - - // insufficient PoW: mismatch - f.PoW = msg.PoW + 1.0 - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed) - } - - // sufficient PoW: match - f.PoW = msg.PoW / 2 - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed) - } - - // topic mismatch - f.Topics[index][0]++ - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) - } - f.Topics[index][0]-- - - // key mismatch - f.SymKeyHash[0]++ - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed) - } - f.SymKeyHash[0]-- - - // Src absent: match - f.Src = nil - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed) - } - - // key hash mismatch - h := f.SymKeyHash - f.SymKeyHash = common.Hash{} - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key hash mismatch) with seed %d.", seed) - } - f.SymKeyHash = h - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key hash match) with seed %d.", seed) - } - - // encryption method mismatch - f.KeySym = nil - f.KeyAsym, err = crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } -} - -func TestMatchMessageAsym(t *testing.T) { - InitSingleTest() - - f, err := generateFilter(t, false) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - const index = 1 - params.Topic = BytesToTopic(f.Topics[index]) - params.Dst = &f.KeyAsym.PublicKey - keySymOrig := params.KeySym - params.KeySym = nil - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(f) - if msg == nil { - t.Fatalf("failed to open with seed %d.", seed) - } - - // Src: match - *f.Src.X = *params.Src.PublicKey.X - *f.Src.Y = *params.Src.PublicKey.Y - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchMessage(src match) with seed %d.", seed) - } - - // insufficient PoW: mismatch - f.PoW = msg.PoW + 1.0 - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed) - } - - // sufficient PoW: match - f.PoW = msg.PoW / 2 - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed) - } - - // topic mismatch - f.Topics[index][0]++ - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) - } - f.Topics[index][0]-- - - // key mismatch - prev := *f.KeyAsym.PublicKey.X - zero := *big.NewInt(0) - *f.KeyAsym.PublicKey.X = zero - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed) - } - *f.KeyAsym.PublicKey.X = prev - - // Src absent: match - f.Src = nil - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed) - } - - // encryption method mismatch - f.KeySym = keySymOrig - f.KeyAsym = nil - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } -} - -func cloneFilter(orig *Filter) *Filter { - var clone Filter - clone.Messages = make(map[common.Hash]*ReceivedMessage) - clone.Src = orig.Src - clone.KeyAsym = orig.KeyAsym - clone.KeySym = orig.KeySym - clone.Topics = orig.Topics - clone.PoW = orig.PoW - clone.AllowP2P = orig.AllowP2P - clone.SymKeyHash = orig.SymKeyHash - return &clone -} - -func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - return nil - } - - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[2]) - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - return nil - } - return env -} - -func TestWatchers(t *testing.T) { - InitSingleTest() - - const NumFilters = 16 - const NumMessages = 256 - var i int - var j uint32 - var e *Envelope - var x, firstID string - var err error - - w := New(&Config{}) - filters := NewFilters(w) - tst := generateTestCases(t, NumFilters) - for i = 0; i < NumFilters; i++ { - tst[i].f.Src = nil - x, err = filters.Install(tst[i].f) - if err != nil { - t.Fatalf("failed to install filter with seed %d: %s.", seed, err) - } - tst[i].id = x - if len(firstID) == 0 { - firstID = x - } - } - - lastID := x - - var envelopes [NumMessages]*Envelope - for i = 0; i < NumMessages; i++ { - j = mrand.Uint32() % NumFilters - e = generateCompatibeEnvelope(t, tst[j].f) - envelopes[i] = e - tst[j].msgCnt++ - } - - for i = 0; i < NumMessages; i++ { - filters.NotifyWatchers(envelopes[i], false) - } - - var total int - var mail []*ReceivedMessage - var count [NumFilters]int - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - count[i] = len(mail) - total += len(mail) - } - - if total != NumMessages { - t.Fatalf("failed with seed %d: total = %d, want: %d.", seed, total, NumMessages) - } - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - if len(mail) != 0 { - t.Fatalf("failed with seed %d: i = %d.", seed, i) - } - - if tst[i].msgCnt != count[i] { - t.Fatalf("failed with seed %d: count[%d]: get %d, want %d.", seed, i, tst[i].msgCnt, count[i]) - } - } - - // another round with a cloned filter - - clone := cloneFilter(tst[0].f) - filters.Uninstall(lastID) - total = 0 - last := NumFilters - 1 - tst[last].f = clone - filters.Install(clone) - for i = 0; i < NumFilters; i++ { - tst[i].msgCnt = 0 - count[i] = 0 - } - - // make sure that the first watcher receives at least one message - e = generateCompatibeEnvelope(t, tst[0].f) - envelopes[0] = e - tst[0].msgCnt++ - for i = 1; i < NumMessages; i++ { - j = mrand.Uint32() % NumFilters - e = generateCompatibeEnvelope(t, tst[j].f) - envelopes[i] = e - tst[j].msgCnt++ - } - - for i = 0; i < NumMessages; i++ { - filters.NotifyWatchers(envelopes[i], false) - } - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - count[i] = len(mail) - total += len(mail) - } - - combined := tst[0].msgCnt + tst[last].msgCnt - if total != NumMessages+count[0] { - t.Fatalf("failed with seed %d: total = %d, count[0] = %d.", seed, total, count[0]) - } - - if combined != count[0] { - t.Fatalf("failed with seed %d: combined = %d, count[0] = %d.", seed, combined, count[0]) - } - - if combined != count[last] { - t.Fatalf("failed with seed %d: combined = %d, count[last] = %d.", seed, combined, count[last]) - } - - for i = 1; i < NumFilters-1; i++ { - mail = tst[i].f.Retrieve() - if len(mail) != 0 { - t.Fatalf("failed with seed %d: i = %d.", seed, i) - } - - if tst[i].msgCnt != count[i] { - t.Fatalf("failed with seed %d: i = %d, get %d, want %d.", seed, i, tst[i].msgCnt, count[i]) - } - } - - // test AcceptP2P - - total = 0 - filters.NotifyWatchers(envelopes[0], true) - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - total += len(mail) - } - - if total != 0 { - t.Fatalf("failed with seed %d: total: got %d, want 0.", seed, total) - } - - f := filters.Get(firstID) - if f == nil { - t.Fatalf("failed to get the filter with seed %d.", seed) - } - f.AllowP2P = true - total = 0 - filters.NotifyWatchers(envelopes[0], true) - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - total += len(mail) - } - - if total != 1 { - t.Fatalf("failed with seed %d: total: got %d, want 1.", seed, total) - } -} - -func TestVariableTopics(t *testing.T) { - InitSingleTest() - - const lastTopicByte = 3 - var match bool - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - for i := 0; i < 4; i++ { - env.Topic = BytesToTopic(f.Topics[i]) - match = f.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i) - } - - f.Topics[i][lastTopicByte]++ - match = f.MatchEnvelope(env) - if match { - t.Fatalf("MatchEnvelope symmetric with seed %d, step %d: false positive.", seed, i) - } - } -} - -func TestMatchSingleTopic_ReturnTrue(t *testing.T) { - bt := []byte("test") - topic := BytesToTopic(bt) - - if !matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_WithTail_ReturnTrue(t *testing.T) { - bt := []byte("test with tail") - topic := BytesToTopic([]byte("test")) - - if !matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_NotEquals_ReturnFalse(t *testing.T) { - bt := []byte("tes") - topic := BytesToTopic(bt) - - if matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_InsufficientLength_ReturnFalse(t *testing.T) { - bt := []byte("test") - topic := BytesToTopic([]byte("not_equal")) - - if matchSingleTopic(topic, bt) { - t.FailNow() - } -} diff --git a/whisper/whisperv5/gen_criteria_json.go b/whisper/whisperv5/gen_criteria_json.go deleted file mode 100644 index 1c0e389ad9..0000000000 --- a/whisper/whisperv5/gen_criteria_json.go +++ /dev/null @@ -1,64 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv5 - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*criteriaOverride)(nil) - -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 -} diff --git a/whisper/whisperv5/gen_message_json.go b/whisper/whisperv5/gen_message_json.go deleted file mode 100644 index b4c4274d07..0000000000 --- a/whisper/whisperv5/gen_message_json.go +++ /dev/null @@ -1,82 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv5 - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*messageOverride)(nil) - -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 -} diff --git a/whisper/whisperv5/gen_newmessage_json.go b/whisper/whisperv5/gen_newmessage_json.go deleted file mode 100644 index 97ffb64ad3..0000000000 --- a/whisper/whisperv5/gen_newmessage_json.go +++ /dev/null @@ -1,88 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv5 - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*newMessageOverride)(nil) - -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 -} diff --git a/whisper/whisperv5/message.go b/whisper/whisperv5/message.go deleted file mode 100644 index 35711d724b..0000000000 --- a/whisper/whisperv5/message.go +++ /dev/null @@ -1,352 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Message element. - -package whisperv5 - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/ecdsa" - crand "crypto/rand" - "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" -) - -// MessageParams specifies the exact way a message should be wrapped into an Envelope. -type MessageParams struct { - TTL uint32 - Src *ecdsa.PrivateKey - Dst *ecdsa.PublicKey - KeySym []byte - Topic TopicType - WorkTime uint32 - PoW float64 - Payload []byte - Padding []byte -} - -// SentMessage represents an end-user data packet to transmit through the -// Whisper protocol. These are wrapped into Envelopes that need not be -// understood by intermediate nodes, just forwarded. -type sentMessage struct { - Raw []byte -} - -// ReceivedMessage represents a data packet to be received through the -// Whisper protocol. -type ReceivedMessage struct { - Raw []byte - - Payload []byte - Padding []byte - Signature []byte - - PoW float64 // Proof of work as described in the Whisper spec - Sent uint32 // Time when the message was posted into the network - TTL uint32 // Maximum time to live allowed for the message - Src *ecdsa.PublicKey // Message recipient (identity used to decode the message) - Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message) - Topic TopicType - - SymKeyHash common.Hash // The Keccak256Hash of the key, associated with the Topic - EnvelopeHash common.Hash // Message envelope hash to act as a unique id - EnvelopeVersion uint64 -} - -func isMessageSigned(flags byte) bool { - return (flags & signatureFlag) != 0 -} - -func (msg *ReceivedMessage) isSymmetricEncryption() bool { - return msg.SymKeyHash != common.Hash{} -} - -func (msg *ReceivedMessage) isAsymmetricEncryption() bool { - return msg.Dst != nil -} - -// NewSentMessage creates and initializes a non-signed, non-encrypted Whisper message. -func NewSentMessage(params *MessageParams) (*sentMessage, error) { - msg := sentMessage{} - 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 { - return nil, err - } - msg.Raw = append(msg.Raw, params.Payload...) - 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 { - rawSize := len(params.Payload) + 1 - if params.Src != nil { - rawSize += signatureLength - } - 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, totalPadSize) - _, err := crand.Read(buf[1:]) - if err != nil { - return err - } - 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 - } - return nil -} - -// sign calculates and sets the cryptographic signature for the message, -// also setting the sign flag. -func (msg *sentMessage) sign(key *ecdsa.PrivateKey) error { - if isMessageSigned(msg.Raw[0]) { - // this should not happen, but no reason to panic - log.Error("failed to sign the message: already signed") - return nil - } - - msg.Raw[0] |= signatureFlag - hash := crypto.Keccak256(msg.Raw) - signature, err := crypto.Sign(hash, key) - if err != nil { - msg.Raw[0] &= ^signatureFlag // clear the flag - return err - } - msg.Raw = append(msg.Raw, signature...) - return nil -} - -// encryptAsymmetric encrypts a message with a public key. -func (msg *sentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { - if !ValidatePublicKey(key) { - return errors.New("invalid public key provided for asymmetric encryption") - } - encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil) - if err == nil { - msg.Raw = encrypted - } - return err -} - -// 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) (nonce []byte, err error) { - if !validateSymmetricKey(key) { - return nil, errors.New("invalid key provided for symmetric encryption") - } - - block, err := aes.NewCipher(key) - if err != nil { - return nil, err - } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - 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, err - } else if !validateSymmetricKey(nonce) { - return nil, errors.New("crypto/rand failed to generate nonce") - } - - msg.Raw = aesgcm.Seal(nil, nonce, msg.Raw, nil) - return nonce, nil -} - -// Wrap bundles the message into an Envelope to transmit over the network. -func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) { - if options.TTL == 0 { - options.TTL = DefaultTTL - } - if options.Src != nil { - if err = msg.sign(options.Src); err != nil { - return nil, err - } - } - var nonce []byte - if options.Dst != nil { - err = msg.encryptAsymmetric(options.Dst) - } else if options.KeySym != nil { - nonce, err = msg.encryptSymmetric(options.KeySym) - } else { - err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided") - } - if err != nil { - return nil, err - } - - envelope = NewEnvelope(options.TTL, options.Topic, nonce, msg) - if err = envelope.Seal(options); err != nil { - return nil, err - } - return envelope, nil -} - -// 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, nonce []byte) error { - block, err := aes.NewCipher(key) - if err != nil { - return err - } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return err - } - if len(nonce) != aesgcm.NonceSize() { - log.Error("decrypting the message", "AES nonce size", len(nonce)) - return errors.New("wrong AES nonce size") - } - decrypted, err := aesgcm.Open(nil, nonce, msg.Raw, nil) - if err != nil { - return err - } - msg.Raw = decrypted - return nil -} - -// decryptAsymmetric decrypts an encrypted payload with a private key. -func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error { - decrypted, err := ecies.ImportECDSA(key).Decrypt(msg.Raw, nil, nil) - if err == nil { - msg.Raw = decrypted - } - return err -} - -// Validate checks the validity and extracts the fields in case of success -func (msg *ReceivedMessage) Validate() bool { - end := len(msg.Raw) - if end < 1 { - return false - } - - if isMessageSigned(msg.Raw[0]) { - end -= signatureLength - if end <= 1 { - return false - } - msg.Signature = msg.Raw[end:] - msg.Src = msg.SigToPubKey() - if msg.Src == nil { - return false - } - } - - padSize, ok := msg.extractPadding(end) - if !ok { - return false - } - - msg.Payload = msg.Raw[1+padSize : end] - return true -} - -// extractPadding extracts the padding from raw message. -// although we don't support sending messages with padding size -// exceeding 255 bytes, such messages are perfectly valid, and -// can be successfully decrypted. -func (msg *ReceivedMessage) extractPadding(end int) (int, bool) { - paddingSize := 0 - 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 { - return 0, false - } - msg.Padding = msg.Raw[1+sz : 1+paddingSize] - } - return paddingSize, true -} - -// SigToPubKey retrieves the public key of the message signer. -func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey { - defer func() { recover() }() // in case of invalid signature - - pub, err := crypto.SigToPub(msg.hash(), msg.Signature) - if err != nil { - log.Error("failed to recover public key from signature", "err", err) - return nil - } - return pub -} - -// hash calculates the SHA3 checksum of the message flags, payload and padding. -func (msg *ReceivedMessage) hash() []byte { - if isMessageSigned(msg.Raw[0]) { - sz := len(msg.Raw) - signatureLength - return crypto.Keccak256(msg.Raw[:sz]) - } - return crypto.Keccak256(msg.Raw) -} diff --git a/whisper/whisperv5/message_test.go b/whisper/whisperv5/message_test.go deleted file mode 100644 index 2edf945df0..0000000000 --- a/whisper/whisperv5/message_test.go +++ /dev/null @@ -1,415 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "bytes" - mrand "math/rand" - "testing" - - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" -) - -func generateMessageParams() (*MessageParams, error) { - // set all the parameters except p.Dst and p.Padding - - buf := make([]byte, 4) - mrand.Read(buf) - sz := mrand.Intn(400) - - var p MessageParams - p.PoW = 0.01 - p.WorkTime = 1 - p.TTL = uint32(mrand.Intn(1024)) - p.Payload = make([]byte, sz) - p.KeySym = make([]byte, aesKeyLength) - mrand.Read(p.Payload) - mrand.Read(p.KeySym) - p.Topic = BytesToTopic(buf) - - var err error - p.Src, err = crypto.GenerateKey() - if err != nil { - return nil, err - } - - return &p, nil -} - -func singleMessageTest(t *testing.T, symmetric bool) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - - if !symmetric { - params.KeySym = nil - params.Dst = &key.PublicKey - } - - text := make([]byte, 0, 512) - text = append(text, params.Payload...) - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - var decrypted *ReceivedMessage - if symmetric { - decrypted, err = env.OpenSymmetric(params.KeySym) - } else { - decrypted, err = env.OpenAsymmetric(key) - } - - if err != nil { - t.Fatalf("failed to encrypt with seed %d: %s.", seed, err) - } - - if !decrypted.Validate() { - t.Fatalf("failed to validate with seed %d.", seed) - } - - if !bytes.Equal(text, decrypted.Payload) { - t.Fatalf("failed with seed %d: compare payload.", seed) - } - if !isMessageSigned(decrypted.Raw[0]) { - t.Fatalf("failed with seed %d: unsigned.", seed) - } - if len(decrypted.Signature) != signatureLength { - t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature)) - } - if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) { - t.Fatalf("failed with seed %d: signature mismatch.", seed) - } -} - -func TestMessageEncryption(t *testing.T) { - InitSingleTest() - - var symmetric bool - for i := 0; i < 256; i++ { - singleMessageTest(t, symmetric) - symmetric = !symmetric - } -} - -func TestMessageWrap(t *testing.T) { - seed = int64(1777444222) - mrand.Seed(seed) - target := 128.0 - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1 - params.WorkTime = 12 - params.PoW = target - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - pow := env.PoW() - if pow < target { - t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target) - } - - // set PoW target too high, expect error - msg2, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1000000 - params.WorkTime = 1 - params.PoW = 10000000.0 - _, err = msg2.Wrap(params) - if err == nil { - t.Fatalf("unexpectedly reached the PoW target with seed %d.", seed) - } -} - -func TestMessageSeal(t *testing.T) { - // this test depends on deterministic choice of seed (1976726903) - seed = int64(1976726903) - mrand.Seed(seed) - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1 - aesnonce := make([]byte, 12) - mrand.Read(aesnonce) - - env := NewEnvelope(params.TTL, params.Topic, aesnonce, msg) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - env.Expiry = uint32(seed) // make it deterministic - target := 32.0 - params.WorkTime = 4 - params.PoW = target - env.Seal(params) - - env.calculatePoW(0) - pow := env.PoW() - if pow < target { - t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target) - } - - params.WorkTime = 1 - params.PoW = 1000000000.0 - env.Seal(params) - env.calculatePoW(0) - pow = env.PoW() - if pow < 2*target { - t.Fatalf("failed Wrap with seed %d: pow too small %f.", seed, pow) - } -} - -func TestEnvelopeOpen(t *testing.T) { - InitSingleTest() - - var symmetric bool - for i := 0; i < 256; i++ { - singleEnvelopeOpenTest(t, symmetric) - symmetric = !symmetric - } -} - -func singleEnvelopeOpenTest(t *testing.T, symmetric bool) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - - if !symmetric { - params.KeySym = nil - params.Dst = &key.PublicKey - } - - text := make([]byte, 0, 512) - text = append(text, params.Payload...) - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - f := Filter{KeyAsym: key, KeySym: params.KeySym} - decrypted := env.Open(&f) - if decrypted == nil { - t.Fatalf("failed to open with seed %d.", seed) - } - - if !bytes.Equal(text, decrypted.Payload) { - t.Fatalf("failed with seed %d: compare payload.", seed) - } - if !isMessageSigned(decrypted.Raw[0]) { - t.Fatalf("failed with seed %d: unsigned.", seed) - } - if len(decrypted.Signature) != signatureLength { - t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature)) - } - if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) { - t.Fatalf("failed with seed %d: signature mismatch.", seed) - } - if decrypted.isAsymmetricEncryption() == symmetric { - t.Fatalf("failed with seed %d: asymmetric %v vs. %v.", seed, decrypted.isAsymmetricEncryption(), symmetric) - } - if decrypted.isSymmetricEncryption() != symmetric { - t.Fatalf("failed with seed %d: symmetric %v vs. %v.", seed, decrypted.isSymmetricEncryption(), symmetric) - } - if !symmetric { - if decrypted.Dst == nil { - t.Fatalf("failed with seed %d: dst is nil.", seed) - } - if !IsPubKeyEqual(decrypted.Dst, &key.PublicKey) { - t.Fatalf("failed with seed %d: Dst.", seed) - } - } -} - -func TestEncryptWithZeroKey(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = make([]byte, aesKeyLength) - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with zero key, seed: %d.", seed) - } - - params, err = generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = make([]byte, 0) - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with empty key, seed: %d.", seed) - } - - params, err = generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = nil - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with nil key, seed: %d.", seed) - } -} - -func TestRlpEncode(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("wrapped with zero key, seed: %d.", seed) - } - - raw, err := rlp.EncodeToBytes(env) - if err != nil { - t.Fatalf("RLP encode failed: %s.", err) - } - - var decoded Envelope - rlp.DecodeBytes(raw, &decoded) - if err != nil { - t.Fatalf("RLP decode failed: %s.", err) - } - - he := env.Hash() - hd := decoded.Hash() - - if he != hd { - t.Fatalf("Hashes are not equal: %x vs. %x", he, hd) - } -} - -func singlePaddingTest(t *testing.T, padSize int) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d and sz=%d: %s.", seed, padSize, err) - } - params.Padding = make([]byte, padSize) - params.PoW = 0.0000000001 - pad := make([]byte, padSize) - _, err = mrand.Read(pad) - if err != nil { - t.Fatalf("padding is not generated (seed %d): %s", seed, err) - } - n := copy(params.Padding, pad) - if n != padSize { - t.Fatalf("padding is not copied (seed %d): %s", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed to wrap, seed: %d and sz=%d.", seed, padSize) - } - f := Filter{KeySym: params.KeySym} - decrypted := env.Open(&f) - if decrypted == nil { - t.Fatalf("failed to open, seed and sz=%d: %d.", seed, padSize) - } - if !bytes.Equal(pad, decrypted.Padding) { - t.Fatalf("padding is not retireved as expected with seed %d and sz=%d:\n[%x]\n[%x].", seed, padSize, pad, decrypted.Padding) - } -} - -func TestPadding(t *testing.T) { - InitSingleTest() - - for i := 1; i < 260; i++ { - singlePaddingTest(t, i) - } - - lim := 256 * 256 - for i := lim - 5; i < lim+2; i++ { - singlePaddingTest(t, i) - } - - for i := 0; i < 256; i++ { - n := mrand.Intn(256*254) + 256 - singlePaddingTest(t, n) - } - - for i := 0; i < 256; i++ { - n := mrand.Intn(256*1024) + 256*256 - singlePaddingTest(t, n) - } -} diff --git a/whisper/whisperv5/peer.go b/whisper/whisperv5/peer.go deleted file mode 100644 index da07631992..0000000000 --- a/whisper/whisperv5/peer.go +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "fmt" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rlp" - set "gopkg.in/fatih/set.v0" -) - -// Peer represents a whisper protocol peer connection. -type Peer struct { - host *Whisper - peer *p2p.Peer - ws p2p.MsgReadWriter - trusted bool - - known *set.Set // Messages already known by the peer to avoid wasting bandwidth - - quit chan struct{} -} - -// newPeer creates a new whisper peer object, but does not run the handshake itself. -func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer { - return &Peer{ - host: host, - peer: remote, - ws: rw, - trusted: false, - known: set.New(), - quit: make(chan struct{}), - } -} - -// start initiates the peer updater, periodically broadcasting the whisper packets -// into the network. -func (peer *Peer) start() { - go peer.update() - log.Trace("start", "peer", peer.ID()) -} - -// stop terminates the peer updater, stopping message forwarding to it. -func (peer *Peer) stop() { - close(peer.quit) - log.Trace("stop", "peer", peer.ID()) -} - -// handshake sends the protocol initiation status message to the remote peer and -// verifies the remote status too. -func (peer *Peer) handshake() error { - // Send the handshake status message asynchronously - errc := make(chan error, 1) - go func() { - errc <- p2p.Send(peer.ws, statusCode, ProtocolVersion) - }() - // Fetch the remote status packet and verify protocol match - packet, err := peer.ws.ReadMsg() - if err != nil { - return err - } - if packet.Code != statusCode { - return fmt.Errorf("peer [%x] sent packet %x before status packet", peer.ID(), packet.Code) - } - s := rlp.NewStream(packet.Payload, uint64(packet.Size)) - peerVersion, err := s.Uint() - if err != nil { - return fmt.Errorf("peer [%x] sent bad status message: %v", peer.ID(), err) - } - if peerVersion != ProtocolVersion { - return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", peer.ID(), peerVersion, ProtocolVersion) - } - // Wait until out own status is consumed too - if err := <-errc; err != nil { - return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err) - } - return nil -} - -// update executes periodic operations on the peer, including message transmission -// and expiration. -func (peer *Peer) update() { - // Start the tickers for the updates - expire := time.NewTicker(expirationCycle) - transmit := time.NewTicker(transmissionCycle) - - // Loop and transmit until termination is requested - for { - select { - case <-expire.C: - peer.expire() - - case <-transmit.C: - if err := peer.broadcast(); err != nil { - log.Trace("broadcast failed", "reason", err, "peer", peer.ID()) - return - } - - case <-peer.quit: - return - } - } -} - -// mark marks an envelope known to the peer so that it won't be sent back. -func (peer *Peer) mark(envelope *Envelope) { - peer.known.Add(envelope.Hash()) -} - -// marked checks if an envelope is already known to the remote peer. -func (peer *Peer) marked(envelope *Envelope) bool { - return peer.known.Has(envelope.Hash()) -} - -// expire iterates over all the known envelopes in the host and removes all -// expired (unknown) ones from the known list. -func (peer *Peer) expire() { - unmark := make(map[common.Hash]struct{}) - peer.known.Each(func(v interface{}) bool { - if !peer.host.isEnvelopeCached(v.(common.Hash)) { - unmark[v.(common.Hash)] = struct{}{} - } - return true - }) - // Dump all known but no longer cached - for hash := range unmark { - peer.known.Remove(hash) - } -} - -// broadcast iterates over the collection of envelopes and transmits yet unknown -// ones over the network. -func (peer *Peer) broadcast() error { - var cnt int - envelopes := peer.host.Envelopes() - for _, envelope := range envelopes { - if !peer.marked(envelope) { - err := p2p.Send(peer.ws, messagesCode, envelope) - if err != nil { - return err - } else { - peer.mark(envelope) - cnt++ - } - } - } - if cnt > 0 { - log.Trace("broadcast", "num. messages", cnt) - } - return nil -} - -func (peer *Peer) ID() []byte { - id := peer.peer.ID() - return id[:] -} diff --git a/whisper/whisperv5/peer_test.go b/whisper/whisperv5/peer_test.go deleted file mode 100644 index 256a670aa4..0000000000 --- a/whisper/whisperv5/peer_test.go +++ /dev/null @@ -1,298 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "bytes" - "crypto/ecdsa" - "net" - "sync" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/p2p/nat" -) - -var keys = []string{ - "d49dcf37238dc8a7aac57dc61b9fee68f0a97f062968978b9fafa7d1033d03a9", - "73fd6143c48e80ed3c56ea159fe7494a0b6b393a392227b422f4c3e8f1b54f98", - "119dd32adb1daa7a4c7bf77f847fb28730785aa92947edf42fdd997b54de40dc", - "deeda8709dea935bb772248a3144dea449ffcc13e8e5a1fd4ef20ce4e9c87837", - "5bd208a079633befa349441bdfdc4d85ba9bd56081525008380a63ac38a407cf", - "1d27fb4912002d58a2a42a50c97edb05c1b3dffc665dbaa42df1fe8d3d95c9b5", - "15def52800c9d6b8ca6f3066b7767a76afc7b611786c1276165fbc61636afb68", - "51be6ab4b2dc89f251ff2ace10f3c1cc65d6855f3e083f91f6ff8efdfd28b48c", - "ef1ef7441bf3c6419b162f05da6037474664f198b58db7315a6f4de52414b4a0", - "09bdf6985aabc696dc1fbeb5381aebd7a6421727343872eb2fadfc6d82486fd9", - "15d811bf2e01f99a224cdc91d0cf76cea08e8c67905c16fee9725c9be71185c4", - "2f83e45cf1baaea779789f755b7da72d8857aeebff19362dd9af31d3c9d14620", - "73f04e34ac6532b19c2aae8f8e52f38df1ac8f5cd10369f92325b9b0494b0590", - "1e2e07b69e5025537fb73770f483dc8d64f84ae3403775ef61cd36e3faf162c1", - "8963d9bbb3911aac6d30388c786756b1c423c4fbbc95d1f96ddbddf39809e43a", - "0422da85abc48249270b45d8de38a4cc3c02032ede1fcf0864a51092d58a2f1f", - "8ae5c15b0e8c7cade201fdc149831aa9b11ff626a7ffd27188886cc108ad0fa8", - "acd8f5a71d4aecfcb9ad00d32aa4bcf2a602939b6a9dd071bab443154184f805", - "a285a922125a7481600782ad69debfbcdb0316c1e97c267aff29ef50001ec045", - "28fd4eee78c6cd4bf78f39f8ab30c32c67c24a6223baa40e6f9c9a0e1de7cef5", - "c5cca0c9e6f043b288c6f1aef448ab59132dab3e453671af5d0752961f013fc7", - "46df99b051838cb6f8d1b73f232af516886bd8c4d0ee07af9a0a033c391380fd", - "c6a06a53cbaadbb432884f36155c8f3244e244881b5ee3e92e974cfa166d793f", - "783b90c75c63dc72e2f8d11b6f1b4de54d63825330ec76ee8db34f06b38ea211", - "9450038f10ca2c097a8013e5121b36b422b95b04892232f930a29292d9935611", - "e215e6246ed1cfdcf7310d4d8cdbe370f0d6a8371e4eb1089e2ae05c0e1bc10f", - "487110939ed9d64ebbc1f300adeab358bc58875faf4ca64990fbd7fe03b78f2b", - "824a70ea76ac81366da1d4f4ac39de851c8ac49dca456bb3f0a186ceefa269a5", - "ba8f34fa40945560d1006a328fe70c42e35cc3d1017e72d26864cd0d1b150f15", - "30a5dfcfd144997f428901ea88a43c8d176b19c79dde54cc58eea001aa3d246c", - "de59f7183aca39aa245ce66a05245fecfc7e2c75884184b52b27734a4a58efa2", - "92629e2ff5f0cb4f5f08fffe0f64492024d36f045b901efb271674b801095c5a", - "7184c1701569e3a4c4d2ddce691edd983b81e42e09196d332e1ae2f1e062cff4", -} - -const NumNodes = 16 // must not exceed the number of keys (32) - -type TestData struct { - counter [NumNodes]int - mutex sync.RWMutex -} - -type TestNode struct { - shh *Whisper - id *ecdsa.PrivateKey - server *p2p.Server - filerId string -} - -var result TestData -var nodes [NumNodes]*TestNode -var sharedKey = []byte("some arbitrary data here") -var sharedTopic TopicType = TopicType{0xF, 0x1, 0x2, 0} -var expectedMessage = []byte("per rectum ad astra") - -// This test does the following: -// 1. creates a chain of whisper nodes, -// 2. installs the filters with shared (predefined) parameters, -// 3. each node sends a number of random (undecryptable) messages, -// 4. first node sends one expected (decryptable) message, -// 5. checks if each node have received and decrypted exactly one message. -func TestSimulation(t *testing.T) { - initialize(t) - - for i := 0; i < NumNodes; i++ { - sendMsg(t, false, i) - } - - sendMsg(t, true, 0) - checkPropagation(t) - stopServers() -} - -func initialize(t *testing.T) { - var err error - - for i := 0; i < NumNodes; i++ { - var node TestNode - node.shh = New(&DefaultConfig) - node.shh.SetMinimumPoW(0.00000001) - node.shh.Start(nil) - topics := make([]TopicType, 0) - topics = append(topics, sharedTopic) - f := Filter{KeySym: sharedKey} - f.Topics = [][]byte{topics[0][:]} - node.filerId, err = node.shh.Subscribe(&f) - if err != nil { - t.Fatalf("failed to install the filter: %s.", err) - } - node.id, err = crypto.HexToECDSA(keys[i]) - if err != nil { - t.Fatalf("failed convert the key: %s.", keys[i]) - } - name := common.MakeName("whisper-go", "2.0") - node.server = &p2p.Server{ - Config: p2p.Config{ - PrivateKey: node.id, - MaxPeers: NumNodes/2 + 1, - Name: name, - Protocols: node.shh.Protocols(), - ListenAddr: "127.0.0.1:0", - NAT: nat.Any(), - }, - } - - err = node.server.Start() - if err != nil { - t.Fatalf("failed to start server %d.", i) - } - - for j := 0; j < i; j++ { - peerNodeId := nodes[j].id - address, _ := net.ResolveTCPAddr("tcp", nodes[j].server.ListenAddr) - peerPort := uint16(address.Port) - peerNode := discover.PubkeyID(&peerNodeId.PublicKey) - peer := discover.NewNode(peerNode, address.IP, peerPort, peerPort) - node.server.AddPeer(peer) - } - - nodes[i] = &node - } -} - -func stopServers() { - for i := 0; i < NumNodes; i++ { - n := nodes[i] - if n != nil { - n.shh.Unsubscribe(n.filerId) - n.shh.Stop() - n.server.Stop() - } - } -} - -func checkPropagation(t *testing.T) { - if t.Failed() { - return - } - - const cycle = 100 - const iterations = 100 - - for j := 0; j < iterations; j++ { - time.Sleep(cycle * time.Millisecond) - - for i := 0; i < NumNodes; i++ { - f := nodes[i].shh.GetFilter(nodes[i].filerId) - if f == nil { - t.Fatalf("failed to get filterId %s from node %d.", nodes[i].filerId, i) - } - - mail := f.Retrieve() - if !validateMail(t, i, mail) { - return - } - - if isTestComplete() { - return - } - } - } - - t.Fatalf("Test was not complete: timeout %d seconds.", iterations*cycle/1000) -} - -func validateMail(t *testing.T, index int, mail []*ReceivedMessage) bool { - var cnt int - for _, m := range mail { - if bytes.Equal(m.Payload, expectedMessage) { - cnt++ - } - } - - if cnt == 0 { - // no messages received yet: nothing is wrong - return true - } - if cnt > 1 { - t.Fatalf("node %d received %d.", index, cnt) - return false - } - - if cnt > 0 { - result.mutex.Lock() - defer result.mutex.Unlock() - result.counter[index] += cnt - if result.counter[index] > 1 { - t.Fatalf("node %d accumulated %d.", index, result.counter[index]) - } - } - return true -} - -func isTestComplete() bool { - result.mutex.RLock() - defer result.mutex.RUnlock() - - for i := 0; i < NumNodes; i++ { - if result.counter[i] < 1 { - return false - } - } - - for i := 0; i < NumNodes; i++ { - envelopes := nodes[i].shh.Envelopes() - if len(envelopes) < 2 { - return false - } - } - - return true -} - -func sendMsg(t *testing.T, expected bool, id int) { - if t.Failed() { - return - } - - opt := MessageParams{KeySym: sharedKey, Topic: sharedTopic, Payload: expectedMessage, PoW: 0.00000001, WorkTime: 1} - if !expected { - opt.KeySym[0]++ - opt.Topic[0]++ - opt.Payload = opt.Payload[1:] - } - - msg, err := NewSentMessage(&opt) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - envelope, err := msg.Wrap(&opt) - if err != nil { - t.Fatalf("failed to seal message: %s", err) - } - - err = nodes[id].shh.Send(envelope) - if err != nil { - t.Fatalf("failed to send message: %s", err) - } -} - -func TestPeerBasic(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d.", seed) - } - - params.PoW = 0.001 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d.", seed) - } - - p := newPeer(nil, nil, nil) - p.mark(env) - if !p.marked(env) { - t.Fatalf("failed mark with seed %d.", seed) - } -} diff --git a/whisper/whisperv5/topic.go b/whisper/whisperv5/topic.go deleted file mode 100644 index c4eda1db42..0000000000 --- a/whisper/whisperv5/topic.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Topic element. - -package whisperv5 - -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -// TopicType represents a cryptographically secure, probabilistic partial -// classifications of a message, determined as the first (left) 4 bytes of the -// SHA3 hash of some arbitrary data given by the original author of the message. -type TopicType [TopicLength]byte - -func BytesToTopic(b []byte) (t TopicType) { - sz := TopicLength - if x := len(b); x < TopicLength { - sz = x - } - for i := 0; i < sz; i++ { - t[i] = b[i] - } - return t -} - -// String converts a topic byte array to a string representation. -func (t *TopicType) String() string { - return common.ToHex(t[:]) -} - -// MarshalText returns the hex representation of t. -func (t TopicType) MarshalText() ([]byte, error) { - return hexutil.Bytes(t[:]).MarshalText() -} - -// UnmarshalText parses a hex representation to a topic. -func (t *TopicType) UnmarshalText(input []byte) error { - return hexutil.UnmarshalFixedText("Topic", input, t[:]) -} diff --git a/whisper/whisperv5/topic_test.go b/whisper/whisperv5/topic_test.go deleted file mode 100644 index 54bbeaf85e..0000000000 --- a/whisper/whisperv5/topic_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "encoding/json" - "testing" -) - -var topicStringTests = []struct { - topic TopicType - str string -}{ - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, str: "0x00000000"}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, str: "0x007f80ff"}, - {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, str: "0xff807f00"}, - {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, str: "0xf26e7779"}, -} - -func TestTopicString(t *testing.T) { - for i, tst := range topicStringTests { - s := tst.topic.String() - if s != tst.str { - t.Fatalf("failed test %d: have %s, want %s.", i, s, tst.str) - } - } -} - -var bytesToTopicTests = []struct { - data []byte - topic TopicType -}{ - {topic: TopicType{0x8f, 0x9a, 0x2b, 0x7d}, data: []byte{0x8f, 0x9a, 0x2b, 0x7d}}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte{0x00, 0x7f, 0x80, 0xff}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00, 0x00}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00}}, - {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte{0x01}}, - {topic: TopicType{0x00, 0xfe, 0x00, 0x00}, data: []byte{0x00, 0xfe}}, - {topic: TopicType{0xea, 0x1d, 0x43, 0x00}, data: []byte{0xea, 0x1d, 0x43}}, - {topic: TopicType{0x6f, 0x3c, 0xb0, 0xdd}, data: []byte{0x6f, 0x3c, 0xb0, 0xdd, 0x0f, 0x00, 0x90}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: nil}, -} - -var unmarshalTestsGood = []struct { - topic TopicType - data []byte -}{ - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x00000000"`)}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte(`"0x007f80ff"`)}, - {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, data: []byte(`"0xff807f00"`)}, - {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, data: []byte(`"0xf26e7779"`)}, -} - -var unmarshalTestsBad = []struct { - topic TopicType - data []byte -}{ - {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(`"0x000000000"`)}, - {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(`"0000000"`)}, - {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(`"abcdefg0"`)}, -} - -var unmarshalTestsUgly = []struct { - topic TopicType - data []byte -}{ - {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) { - for i, tst := range unmarshalTestsGood { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err != nil { - t.Errorf("failed test %d. input: %v. err: %v", i, tst.data, err) - } else if top != tst.topic { - t.Errorf("failed test %d: have %v, want %v.", i, t, tst.topic) - } - } -} - -func TestUnmarshalTestsBad(t *testing.T) { - // in this test UnmarshalJSON() is supposed to fail - for i, tst := range unmarshalTestsBad { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err == nil { - t.Fatalf("failed test %d. input: %v.", i, tst.data) - } - } -} - -func TestUnmarshalTestsUgly(t *testing.T) { - // in this test UnmarshalJSON() is NOT supposed to fail, but result should be wrong - for i, tst := range unmarshalTestsUgly { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err != nil { - t.Errorf("failed test %d. input: %v.", i, tst.data) - } else if top == tst.topic { - t.Errorf("failed test %d: have %v, want %v.", i, top, tst.topic) - } - } -} diff --git a/whisper/whisperv5/whisper.go b/whisper/whisperv5/whisper.go deleted file mode 100644 index cec233cb04..0000000000 --- a/whisper/whisperv5/whisper.go +++ /dev/null @@ -1,855 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "bytes" - "crypto/ecdsa" - crand "crypto/rand" - "crypto/sha256" - "fmt" - "runtime" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rpc" - "github.com/syndtr/goleveldb/leveldb/errors" - "golang.org/x/crypto/pbkdf2" - "golang.org/x/sync/syncmap" - set "gopkg.in/fatih/set.v0" -) - -type Statistics struct { - messagesCleared int - memoryCleared int - memoryUsed int - cycles 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 -// network, using its very own P2P communication layer. -type Whisper struct { - protocol p2p.Protocol // Protocol description and parameters - filters *Filters // Message filters installed with Subscribe function - - privateKeys map[string]*ecdsa.PrivateKey // Private key storage - symKeys map[string][]byte // Symmetric key storage - 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 - expirations map[uint32]*set.SetNonTS // Message expiration pool - - 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 - p2pMsgQueue chan *Envelope // Message queue for peer-to-peer messages (not to be forwarded any further) - quit chan struct{} // Channel used for graceful exit - - settings syncmap.Map // holds configuration settings that can be dynamically changed - - statsMu sync.Mutex // guard stats - stats Statistics // Statistics of whisper node - - mailServer MailServer // MailServer interface -} - -// New creates a Whisper client ready to communicate through the Ethereum P2P network. -func New(cfg *Config) *Whisper { - if cfg == nil { - cfg = &DefaultConfig - } - - whisper := &Whisper{ - privateKeys: make(map[string]*ecdsa.PrivateKey), - symKeys: make(map[string][]byte), - envelopes: make(map[common.Hash]*Envelope), - expirations: make(map[uint32]*set.SetNonTS), - peers: make(map[*Peer]struct{}), - messageQueue: make(chan *Envelope, messageQueueLimit), - p2pMsgQueue: make(chan *Envelope, messageQueueLimit), - quit: make(chan struct{}), - } - - 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 - whisper.protocol = p2p.Protocol{ - Name: ProtocolName, - Version: uint(ProtocolVersion), - Length: NumberOfMessageCodes, - Run: whisper.HandlePeer, - NodeInfo: func() interface{} { - return map[string]interface{}{ - "version": ProtocolVersionStr, - "maxMessageSize": whisper.MaxMessageSize(), - "minimumPoW": whisper.MinPow(), - } - }, - } - - 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 -func (w *Whisper) APIs() []rpc.API { - return []rpc.API{ - { - Namespace: ProtocolName, - Version: ProtocolVersionStr, - Service: NewPublicWhisperAPI(w), - Public: true, - }, - } -} - -// RegisterServer registers MailServer interface. -// MailServer will process all the incoming messages with p2pRequestCode. -func (w *Whisper) RegisterServer(server MailServer) { - w.mailServer = server -} - -// Protocols returns the whisper sub-protocols ran by this particular client. -func (w *Whisper) Protocols() []p2p.Protocol { - return []p2p.Protocol{w.protocol} -} - -// Version returns the whisper sub-protocols version number. -func (w *Whisper) Version() uint { - return w.protocol.Version -} - -// SetMaxMessageSize sets the maximal message size allowed by this node -func (w *Whisper) SetMaxMessageSize(size uint32) error { - if size > MaxMessageSize { - return fmt.Errorf("message size too large [%d>%d]", size, MaxMessageSize) - } - w.settings.Store(maxMsgSizeIdx, size) - return nil -} - -// SetMinimumPoW sets the minimal PoW required by this node -func (w *Whisper) SetMinimumPoW(val float64) error { - if val <= 0.0 { - return fmt.Errorf("invalid PoW: %f", val) - } - w.settings.Store(minPowIdx, val) - return nil -} - -// getPeer retrieves peer by ID -func (w *Whisper) getPeer(peerID []byte) (*Peer, error) { - w.peerMu.Lock() - defer w.peerMu.Unlock() - for p := range w.peers { - id := p.peer.ID() - if bytes.Equal(peerID, id[:]) { - return p, nil - } - } - return nil, fmt.Errorf("Could not find peer with ID: %x", peerID) -} - -// AllowP2PMessagesFromPeer marks specific peer trusted, -// which will allow it to send historic (expired) messages. -func (w *Whisper) AllowP2PMessagesFromPeer(peerID []byte) error { - p, err := w.getPeer(peerID) - if err != nil { - return err - } - p.trusted = true - return nil -} - -// RequestHistoricMessages sends a message with p2pRequestCode to a specific peer, -// which is known to implement MailServer interface, and is supposed to process this -// request and respond with a number of peer-to-peer messages (possibly expired), -// which are not supposed to be forwarded any further. -// The whisper protocol is agnostic of the format and contents of envelope. -func (w *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error { - p, err := w.getPeer(peerID) - if err != nil { - return err - } - p.trusted = true - return p2p.Send(p.ws, p2pRequestCode, envelope) -} - -// SendP2PMessage sends a peer-to-peer message to a specific peer. -func (w *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { - p, err := w.getPeer(peerID) - if err != nil { - return err - } - return w.SendP2PDirect(p, envelope) -} - -// SendP2PDirect sends a peer-to-peer message to a specific peer. -func (w *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error { - return p2p.Send(peer.ws, p2pCode, envelope) -} - -// NewKeyPair generates a new cryptographic identity for the client, and injects -// it into the known identities for message decryption. Returns ID of the new key pair. -func (w *Whisper) NewKeyPair() (string, error) { - key, err := crypto.GenerateKey() - if err != nil || !validatePrivateKey(key) { - key, err = crypto.GenerateKey() // retry once - } - if err != nil { - return "", err - } - if !validatePrivateKey(key) { - return "", fmt.Errorf("failed to generate valid key") - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - w.keyMu.Lock() - defer w.keyMu.Unlock() - - if w.privateKeys[id] != nil { - return "", fmt.Errorf("failed to generate unique ID") - } - w.privateKeys[id] = key - return id, nil -} - -// DeleteKeyPair deletes the specified key if it exists. -func (w *Whisper) DeleteKeyPair(key string) bool { - w.keyMu.Lock() - defer w.keyMu.Unlock() - - if w.privateKeys[key] != nil { - delete(w.privateKeys, key) - return true - } - 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 -// of the specified public pair. -func (w *Whisper) HasKeyPair(id string) bool { - w.keyMu.RLock() - defer w.keyMu.RUnlock() - return w.privateKeys[id] != nil -} - -// GetPrivateKey retrieves the private key of the specified identity. -func (w *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) { - w.keyMu.RLock() - defer w.keyMu.RUnlock() - key := w.privateKeys[id] - if key == nil { - return nil, fmt.Errorf("invalid id") - } - return key, nil -} - -// 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) { - key := make([]byte, aesKeyLength) - _, err := crand.Read(key) - if err != nil { - return "", err - } else if !validateSymmetricKey(key) { - return "", fmt.Errorf("error in GenerateSymKey: crypto/rand failed to generate random data") - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - w.keyMu.Lock() - defer w.keyMu.Unlock() - - if w.symKeys[id] != nil { - return "", fmt.Errorf("failed to generate unique ID") - } - w.symKeys[id] = key - return id, nil -} - -// AddSymKeyDirect stores the key, and returns its id. -func (w *Whisper) AddSymKeyDirect(key []byte) (string, error) { - if len(key) != aesKeyLength { - return "", fmt.Errorf("wrong key size: %d", len(key)) - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - w.keyMu.Lock() - defer w.keyMu.Unlock() - - if w.symKeys[id] != nil { - return "", fmt.Errorf("failed to generate unique ID") - } - w.symKeys[id] = key - return id, nil -} - -// AddSymKeyFromPassword generates the key from password, stores it, and returns its id. -func (w *Whisper) AddSymKeyFromPassword(password string) (string, error) { - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - if w.HasSymKey(id) { - return "", fmt.Errorf("failed to generate unique ID") - } - - derived, err := deriveKeyMaterial([]byte(password), EnvelopeVersion) - if err != nil { - return "", err - } - - w.keyMu.Lock() - defer w.keyMu.Unlock() - - // double check is necessary, because deriveKeyMaterial() is very slow - if w.symKeys[id] != nil { - return "", fmt.Errorf("critical error: failed to generate unique ID") - } - w.symKeys[id] = derived - return id, nil -} - -// HasSymKey returns true if there is a key associated with the given id. -// Otherwise returns false. -func (w *Whisper) HasSymKey(id string) bool { - w.keyMu.RLock() - defer w.keyMu.RUnlock() - return w.symKeys[id] != nil -} - -// DeleteSymKey deletes the key associated with the name string if it exists. -func (w *Whisper) DeleteSymKey(id string) bool { - w.keyMu.Lock() - defer w.keyMu.Unlock() - if w.symKeys[id] != nil { - delete(w.symKeys, id) - return true - } - return false -} - -// GetSymKey returns the symmetric key associated with the given id. -func (w *Whisper) GetSymKey(id string) ([]byte, error) { - w.keyMu.RLock() - defer w.keyMu.RUnlock() - if w.symKeys[id] != nil { - return w.symKeys[id], nil - } - return nil, fmt.Errorf("non-existent key ID") -} - -// Subscribe installs a new message handler used for filtering, decrypting -// and subsequent storing of incoming messages. -func (w *Whisper) Subscribe(f *Filter) (string, error) { - return w.filters.Install(f) -} - -// GetFilter returns the filter by id. -func (w *Whisper) GetFilter(id string) *Filter { - return w.filters.Get(id) -} - -// Unsubscribe removes an installed message handler. -func (w *Whisper) Unsubscribe(id string) error { - ok := w.filters.Uninstall(id) - if !ok { - return fmt.Errorf("Unsubscribe: Invalid ID") - } - return nil -} - -// Send injects a message into the whisper send queue, to be distributed in the -// 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") - } - return err -} - -// Start implements node.Service, starting the background data propagation thread -// of the Whisper protocol. -func (w *Whisper) Start(*p2p.Server) error { - log.Info("started whisper v." + ProtocolVersionStr) - go w.update() - - numCPU := runtime.NumCPU() - for i := 0; i < numCPU; i++ { - go w.processQueue() - } - - return nil -} - -// Stop implements node.Service, stopping the background data propagation thread -// of the Whisper protocol. -func (w *Whisper) Stop() error { - close(w.quit) - log.Info("whisper stopped") - return nil -} - -// HandlePeer is called by the underlying P2P layer when the whisper sub-protocol -// connection is negotiated. -func (w *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error { - // Create the new peer and start tracking it - whisperPeer := newPeer(w, peer, rw) - - w.peerMu.Lock() - w.peers[whisperPeer] = struct{}{} - w.peerMu.Unlock() - - defer func() { - w.peerMu.Lock() - delete(w.peers, whisperPeer) - w.peerMu.Unlock() - }() - - // Run the peer handshake and state updates - if err := whisperPeer.handshake(); err != nil { - return err - } - whisperPeer.start() - defer whisperPeer.stop() - - return w.runMessageLoop(whisperPeer, rw) -} - -// runMessageLoop reads and processes inbound messages directly to merge into client-global state. -func (w *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { - for { - // fetch the next packet - packet, err := rw.ReadMsg() - if err != nil { - log.Info("message loop", "peer", p.peer.ID(), "err", err) - return err - } - if packet.Size > w.MaxMessageSize() { - log.Warn("oversized message received", "peer", p.peer.ID()) - return errors.New("oversized message received") - } - - switch packet.Code { - case statusCode: - // this should not happen, but no need to panic; just ignore this message. - log.Warn("unxepected status message received", "peer", p.peer.ID()) - case messagesCode: - // decode the contained envelopes - 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") - } - cached, err := w.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. - // this message is not supposed to be forwarded to other peers, and - // therefore might not satisfy the PoW, expiry and other requirements. - // these messages are only accepted from the trusted peer. - if p.trusted { - var envelope Envelope - if err := packet.Decode(&envelope); err != nil { - log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid direct message") - } - w.postEvent(&envelope, true) - } - case p2pRequestCode: - // Must be processed if mail server is implemented. Otherwise ignore. - if w.mailServer != nil { - var request Envelope - if err := packet.Decode(&request); err != nil { - log.Warn("failed to decode p2p request message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid p2p request") - } - w.mailServer.DeliverMail(p, &request) - } - default: - // New message types might be implemented in the future versions of Whisper. - // For forward compatibility, just ignore. - } - - packet.Discard() - } -} - -// add inserts a new envelope into the message pool to be distributed within the -// whisper network. It also inserts the envelope into the expiration pool at the -// appropriate time-stamp. In case of error, connection should be dropped. -func (w *Whisper) add(envelope *Envelope) (bool, error) { - now := uint32(time.Now().Unix()) - sent := envelope.Expiry - envelope.TTL - - if sent > now { - if sent-SynchAllowance > now { - return false, fmt.Errorf("envelope created in the future [%x]", envelope.Hash()) - } - // recalculate PoW, adjusted for the time difference, plus one second for latency - envelope.calculatePoW(sent - now + 1) - } - - if envelope.Expiry < now { - if envelope.Expiry+SynchAllowance*2 < now { - return false, fmt.Errorf("very old message") - } - log.Debug("expired envelope dropped", "hash", envelope.Hash().Hex()) - return false, nil // drop envelope without error - } - - if uint32(envelope.size()) > w.MaxMessageSize() { - return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash()) - } - - if len(envelope.Version) > 4 { - return false, fmt.Errorf("oversized version [%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() < w.MinPow() { - log.Debug("envelope with low PoW dropped", "PoW", envelope.PoW(), "hash", envelope.Hash().Hex()) - return false, nil // drop envelope without error - } - - hash := envelope.Hash() - - w.poolMu.Lock() - _, alreadyCached := w.envelopes[hash] - if !alreadyCached { - w.envelopes[hash] = envelope - if w.expirations[envelope.Expiry] == nil { - w.expirations[envelope.Expiry] = set.NewNonTS() - } - if !w.expirations[envelope.Expiry].Has(hash) { - w.expirations[envelope.Expiry].Add(hash) - } - } - w.poolMu.Unlock() - - if alreadyCached { - log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex()) - } else { - log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex()) - w.statsMu.Lock() - w.stats.memoryUsed += envelope.size() - w.statsMu.Unlock() - w.postEvent(envelope, false) // notify the local node about the new message - if w.mailServer != nil { - w.mailServer.Archive(envelope) - } - } - return true, nil -} - -// postEvent queues the message for further processing. -func (w *Whisper) postEvent(envelope *Envelope, isP2P bool) { - // if the version of incoming message is higher than - // currently supported version, we can not decrypt it, - // and therefore just ignore this message - if envelope.Ver() <= EnvelopeVersion { - if isP2P { - w.p2pMsgQueue <- envelope - } else { - w.checkOverflow() - w.messageQueue <- envelope - } - } -} - -// checkOverflow checks if message queue overflow occurs and reports it if necessary. -func (w *Whisper) checkOverflow() { - queueSize := len(w.messageQueue) - - if queueSize == messageQueueLimit { - if !w.Overflow() { - w.settings.Store(overflowIdx, true) - log.Warn("message queue overflow") - } - } else if queueSize <= messageQueueLimit/2 { - if w.Overflow() { - w.settings.Store(overflowIdx, false) - log.Warn("message queue overflow fixed (back to normal)") - } - } -} - -// processQueue delivers the messages to the watchers during the lifetime of the whisper node. -func (w *Whisper) processQueue() { - var e *Envelope - for { - select { - case <-w.quit: - return - - case e = <-w.messageQueue: - w.filters.NotifyWatchers(e, false) - - case e = <-w.p2pMsgQueue: - w.filters.NotifyWatchers(e, true) - } - } -} - -// update loops until the lifetime of the whisper node, updating its internal -// state by expiring stale messages from the pool. -func (w *Whisper) update() { - // Start a ticker to check for expirations - expire := time.NewTicker(expirationCycle) - - // Repeat updates until termination is requested - for { - select { - case <-expire.C: - w.expire() - - case <-w.quit: - return - } - } -} - -// expire iterates over all the expiration timestamps, removing all stale -// messages from the pools. -func (w *Whisper) expire() { - w.poolMu.Lock() - defer w.poolMu.Unlock() - - w.statsMu.Lock() - defer w.statsMu.Unlock() - w.stats.reset() - now := uint32(time.Now().Unix()) - for expiry, hashSet := range w.expirations { - if expiry < now { - // Dump all expired messages and remove timestamp - hashSet.Each(func(v interface{}) bool { - sz := w.envelopes[v.(common.Hash)].size() - delete(w.envelopes, v.(common.Hash)) - w.stats.messagesCleared++ - w.stats.memoryCleared += sz - w.stats.memoryUsed -= sz - return true - }) - w.expirations[expiry].Clear() - delete(w.expirations, expiry) - } - } -} - -// Stats returns the whisper node statistics. -func (w *Whisper) Stats() Statistics { - w.statsMu.Lock() - defer w.statsMu.Unlock() - - return w.stats -} - -// Envelopes retrieves all the messages currently pooled by the node. -func (w *Whisper) Envelopes() []*Envelope { - w.poolMu.RLock() - defer w.poolMu.RUnlock() - - all := make([]*Envelope, 0, len(w.envelopes)) - for _, envelope := range w.envelopes { - all = append(all, envelope) - } - return all -} - -// Messages iterates through all currently floating envelopes -// and retrieves all the messages, that this filter could decrypt. -func (w *Whisper) Messages(id string) []*ReceivedMessage { - result := make([]*ReceivedMessage, 0) - w.poolMu.RLock() - defer w.poolMu.RUnlock() - - if filter := w.filters.Get(id); filter != nil { - for _, env := range w.envelopes { - msg := filter.processEnvelope(env) - if msg != nil { - result = append(result, msg) - } - } - } - return result -} - -// isEnvelopeCached checks if envelope with specific hash has already been received and cached. -func (w *Whisper) isEnvelopeCached(hash common.Hash) bool { - w.poolMu.Lock() - defer w.poolMu.Unlock() - - _, exist := w.envelopes[hash] - return exist -} - -// reset resets the node's statistics after each expiry cycle. -func (s *Statistics) reset() { - s.cycles++ - s.totalMessagesCleared += s.messagesCleared - - s.memoryCleared = 0 - s.messagesCleared = 0 -} - -// ValidatePublicKey checks the format of the given public key. -func ValidatePublicKey(k *ecdsa.PublicKey) bool { - return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 -} - -// validatePrivateKey checks the format of the given private key. -func validatePrivateKey(k *ecdsa.PrivateKey) bool { - if k == nil || k.D == nil || k.D.Sign() == 0 { - return false - } - return ValidatePublicKey(&k.PublicKey) -} - -// validateSymmetricKey returns false if the key contains all zeros -func validateSymmetricKey(k []byte) bool { - return len(k) > 0 && !containsOnlyZeros(k) -} - -// containsOnlyZeros checks if the data contain only zeros. -func containsOnlyZeros(data []byte) bool { - for _, b := range data { - if b != 0 { - return false - } - } - return true -} - -// bytesToUintLittleEndian converts the slice to 64-bit unsigned integer. -func bytesToUintLittleEndian(b []byte) (res uint64) { - mul := uint64(1) - for i := 0; i < len(b); i++ { - res += uint64(b[i]) * mul - mul *= 256 - } - return res -} - -// BytesToUintBigEndian converts the slice to 64-bit unsigned integer. -func BytesToUintBigEndian(b []byte) (res uint64) { - for i := 0; i < len(b); i++ { - res *= 256 - res += uint64(b[i]) - } - return res -} - -// 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, aesKeyLength, sha256.New) - return derivedKey, nil - } - return nil, unknownVersionError(version) -} - -// GenerateRandomID generates a random string, which is then returned to be used as a key id -func GenerateRandomID() (id string, err error) { - buf := make([]byte, keyIdSize) - _, err = crand.Read(buf) - if err != nil { - return "", err - } - if !validateSymmetricKey(buf) { - return "", fmt.Errorf("error in generateRandomID: crypto/rand failed to generate random data") - } - id = common.Bytes2Hex(buf) - return id, err -} diff --git a/whisper/whisperv5/whisper_test.go b/whisper/whisperv5/whisper_test.go deleted file mode 100644 index 8af085292a..0000000000 --- a/whisper/whisperv5/whisper_test.go +++ /dev/null @@ -1,851 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv5 - -import ( - "bytes" - "crypto/ecdsa" - mrand "math/rand" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" -) - -func TestWhisperBasic(t *testing.T) { - w := New(&DefaultConfig) - p := w.Protocols() - shh := p[0] - if shh.Name != ProtocolName { - t.Fatalf("failed Protocol Name: %v.", shh.Name) - } - if uint64(shh.Version) != ProtocolVersion { - t.Fatalf("failed Protocol Version: %v.", shh.Version) - } - if shh.Length != NumberOfMessageCodes { - t.Fatalf("failed Protocol Length: %v.", shh.Length) - } - if shh.Run == nil { - t.Fatalf("failed shh.Run.") - } - if uint64(w.Version()) != ProtocolVersion { - t.Fatalf("failed whisper Version: %v.", shh.Version) - } - if w.GetFilter("non-existent") != nil { - t.Fatalf("failed GetFilter.") - } - - peerID := make([]byte, 64) - mrand.Read(peerID) - peer, _ := w.getPeer(peerID) - if peer != nil { - t.Fatal("found peer for random key.") - } - if err := w.AllowP2PMessagesFromPeer(peerID); err == nil { - t.Fatalf("failed MarkPeerTrusted.") - } - exist := w.HasSymKey("non-existing") - if exist { - t.Fatalf("failed HasSymKey.") - } - key, err := w.GetSymKey("non-existing") - if err == nil { - t.Fatalf("failed GetSymKey(non-existing): false positive.") - } - if key != nil { - t.Fatalf("failed GetSymKey: false positive.") - } - mail := w.Envelopes() - if len(mail) != 0 { - t.Fatalf("failed w.Envelopes().") - } - m := w.Messages("non-existent") - if len(m) != 0 { - t.Fatalf("failed w.Messages.") - } - - var derived []byte - ver := uint64(0xDEADBEEF) - if _, err := deriveKeyMaterial(peerID, ver); err != unknownVersionError(ver) { - t.Fatalf("failed deriveKeyMaterial with param = %v: %s.", peerID, err) - } - derived, err = deriveKeyMaterial(peerID, 0) - if err != nil { - t.Fatalf("failed second deriveKeyMaterial with param = %v: %s.", peerID, err) - } - if !validateSymmetricKey(derived) { - t.Fatalf("failed validateSymmetricKey with param = %v.", derived) - } - if containsOnlyZeros(derived) { - t.Fatalf("failed containsOnlyZeros with param = %v.", derived) - } - - buf := []byte{0xFF, 0xE5, 0x80, 0x2, 0} - le := bytesToUintLittleEndian(buf) - be := BytesToUintBigEndian(buf) - if le != uint64(0x280e5ff) { - t.Fatalf("failed bytesToIntLittleEndian: %d.", le) - } - if be != uint64(0xffe5800200) { - t.Fatalf("failed BytesToIntBigEndian: %d.", be) - } - - id, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - pk, err := w.GetPrivateKey(id) - if err != nil { - t.Fatalf("failed to retrieve new key pair: %s.", err) - } - if !validatePrivateKey(pk) { - t.Fatalf("failed validatePrivateKey: %v.", pk) - } - if !ValidatePublicKey(&pk.PublicKey) { - t.Fatalf("failed ValidatePublicKey: %v.", pk) - } -} - -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) { - w := New(&DefaultConfig) - id1, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - id2, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - pk1, err := w.GetPrivateKey(id1) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - pk2, err := w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - - if !w.HasKeyPair(id1) { - t.Fatalf("failed HasIdentity(pk1).") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed HasIdentity(pk2).") - } - if pk1 == nil { - t.Fatalf("failed GetIdentity(pk1).") - } - if pk2 == nil { - t.Fatalf("failed GetIdentity(pk2).") - } - - if !validatePrivateKey(pk1) { - t.Fatalf("pk1 is invalid.") - } - if !validatePrivateKey(pk2) { - t.Fatalf("pk2 is invalid.") - } - - // Delete one identity - done := w.DeleteKeyPair(id1) - if !done { - t.Fatalf("failed to delete id1.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - if w.HasKeyPair(id1) { - t.Fatalf("failed DeleteIdentity(pub1): still exist.") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed DeleteIdentity(pub1): pub2 does not exist.") - } - if pk1 != nil { - t.Fatalf("failed DeleteIdentity(pub1): first key still exist.") - } - if pk2 == nil { - t.Fatalf("failed DeleteIdentity(pub1): second key does not exist.") - } - - // Delete again non-existing identity - done = w.DeleteKeyPair(id1) - if done { - t.Fatalf("delete id1: false positive.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - if w.HasKeyPair(id1) { - t.Fatalf("failed delete non-existing identity: exist.") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed delete non-existing identity: pub2 does not exist.") - } - if pk1 != nil { - t.Fatalf("failed delete non-existing identity: first key exist.") - } - if pk2 == nil { - t.Fatalf("failed delete non-existing identity: second key does not exist.") - } - - // Delete second identity - done = w.DeleteKeyPair(id2) - if !done { - t.Fatalf("failed to delete id2.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - if w.HasKeyPair(id1) { - t.Fatalf("failed delete second identity: first identity exist.") - } - if w.HasKeyPair(id2) { - t.Fatalf("failed delete second identity: still exist.") - } - if pk1 != nil { - t.Fatalf("failed delete second identity: first key exist.") - } - if pk2 != nil { - t.Fatalf("failed delete second identity: second key exist.") - } -} - -func TestWhisperSymKeyManagement(t *testing.T) { - InitSingleTest() - - var err error - var k1, k2 []byte - w := New(&DefaultConfig) - id1 := string("arbitrary-string-1") - id2 := string("arbitrary-string-2") - - id1, err = w.GenerateSymKey() - if err != nil { - t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err) - } - - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed GetSymKey(id2): false positive.") - } - if !w.HasSymKey(id1) { - t.Fatalf("failed HasSymKey(id1).") - } - if w.HasSymKey(id2) { - t.Fatalf("failed HasSymKey(id2): false positive.") - } - if k1 == nil { - t.Fatalf("first key does not exist.") - } - if k2 != nil { - t.Fatalf("second key still exist.") - } - - // add existing id, nothing should change - randomKey := make([]byte, aesKeyLength) - mrand.Read(randomKey) - id1, err = w.AddSymKeyDirect(randomKey) - if err != nil { - t.Fatalf("failed AddSymKey with seed %d: %s.", seed, err) - } - - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed w.GetSymKey(id2): false positive.") - } - if !w.HasSymKey(id1) { - t.Fatalf("failed w.HasSymKey(id1).") - } - if w.HasSymKey(id2) { - t.Fatalf("failed w.HasSymKey(id2): false positive.") - } - if k1 == nil { - t.Fatalf("first key does not exist.") - } - if !bytes.Equal(k1, randomKey) { - t.Fatalf("k1 != randomKey.") - } - if k2 != nil { - t.Fatalf("second key already exist.") - } - - id2, err = w.AddSymKeyDirect(randomKey) - if err != nil { - t.Fatalf("failed AddSymKey(id2) with seed %d: %s.", seed, err) - } - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if !w.HasSymKey(id1) { - t.Fatalf("HasSymKey(id1) failed.") - } - if !w.HasSymKey(id2) { - t.Fatalf("HasSymKey(id2) failed.") - } - if k1 == nil { - t.Fatalf("k1 does not exist.") - } - if k2 == nil { - t.Fatalf("k2 does not exist.") - } - if !bytes.Equal(k1, k2) { - t.Fatalf("k1 != k2.") - } - if !bytes.Equal(k1, randomKey) { - t.Fatalf("k1 != randomKey.") - } - if len(k1) != aesKeyLength { - t.Fatalf("wrong length of k1.") - } - if len(k2) != aesKeyLength { - t.Fatalf("wrong length of k2.") - } - - w.DeleteSymKey(id1) - k1, err = w.GetSymKey(id1) - if err == nil { - t.Fatalf("failed w.GetSymKey(id1): false positive.") - } - if k1 != nil { - t.Fatalf("failed GetSymKey(id1): false positive.") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if w.HasSymKey(id1) { - t.Fatalf("failed to delete first key: still exist.") - } - if !w.HasSymKey(id2) { - t.Fatalf("failed to delete first key: second key does not exist.") - } - if k1 != nil { - t.Fatalf("failed to delete first key.") - } - if k2 == nil { - t.Fatalf("failed to delete first key: second key is nil.") - } - - w.DeleteSymKey(id1) - w.DeleteSymKey(id2) - k1, err = w.GetSymKey(id1) - if err == nil { - t.Fatalf("failed w.GetSymKey(id1): false positive.") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed w.GetSymKey(id2): false positive.") - } - if k1 != nil || k2 != nil { - t.Fatalf("k1 or k2 is not nil") - } - if w.HasSymKey(id1) { - t.Fatalf("failed to delete second key: first key exist.") - } - if w.HasSymKey(id2) { - t.Fatalf("failed to delete second key: still exist.") - } - if k1 != nil { - t.Fatalf("failed to delete second key: first key is not nil.") - } - if k2 != nil { - t.Fatalf("failed to delete second key: second key is not nil.") - } - - randomKey = make([]byte, aesKeyLength+1) - mrand.Read(randomKey) - _, err = w.AddSymKeyDirect(randomKey) - if err == nil { - t.Fatalf("added the key with wrong size, seed %d.", seed) - } - - const password = "arbitrary data here" - id1, err = w.AddSymKeyFromPassword(password) - if err != nil { - t.Fatalf("failed AddSymKeyFromPassword(id1) with seed %d: %s.", seed, err) - } - id2, err = w.AddSymKeyFromPassword(password) - if err != nil { - t.Fatalf("failed AddSymKeyFromPassword(id2) with seed %d: %s.", seed, err) - } - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if !w.HasSymKey(id1) { - t.Fatalf("HasSymKey(id1) failed.") - } - if !w.HasSymKey(id2) { - t.Fatalf("HasSymKey(id2) failed.") - } - if k1 == nil { - t.Fatalf("k1 does not exist.") - } - if k2 == nil { - t.Fatalf("k2 does not exist.") - } - if !bytes.Equal(k1, k2) { - t.Fatalf("k1 != k2.") - } - if len(k1) != aesKeyLength { - t.Fatalf("wrong length of k1.") - } - if len(k2) != aesKeyLength { - t.Fatalf("wrong length of k2.") - } - if !validateSymmetricKey(k2) { - t.Fatalf("key validation failed.") - } -} - -func TestExpiry(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - w.SetMinimumPoW(0.0000001) - defer w.SetMinimumPoW(DefaultMinimumPoW) - w.Start(nil) - defer w.Stop() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.TTL = 1 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send envelope with seed %d: %s.", seed, err) - } - - // wait till received or timeout - var received, expired bool - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // wait till expired or timeout - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) == 0 { - expired = true - break - } - } - - if !expired { - t.Fatalf("expire failed, seed: %d.", seed) - } -} - -func TestCustomization(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPoW(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - const smallPoW = 0.00001 - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[2]) - params.PoW = smallPoW - params.TTL = 3600 * 24 // one day - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err == nil { - t.Fatalf("successfully sent envelope with PoW %.06f, false positive (seed %d).", env.PoW(), seed) - } - - w.SetMinimumPoW(smallPoW / 2) - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send envelope with seed %d: %s.", seed, err) - } - - params.TTL++ - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - w.SetMaxMessageSize(uint32(env.size() - 1)) - err = w.Send(env) - if err == nil { - t.Fatalf("successfully sent oversized envelope (seed %d): false positive.", seed) - } - - w.SetMaxMessageSize(DefaultMaxMessageSize) - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send second envelope with seed %d: %s.", seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) > 1 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - id, err := w.Subscribe(f) - if err != nil { - t.Fatalf("failed subscribe with seed %d: %s.", seed, err) - } - time.Sleep(5 * time.Millisecond) - mail := f.Retrieve() - if len(mail) > 0 { - t.Fatalf("received premature mail") - } - - mail = w.Messages(id) - if len(mail) != 2 { - t.Fatalf("failed to get whisper messages") - } -} - -func TestSymmetricSendCycle(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPoW(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter1, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter1.PoW = DefaultMinimumPoW - - // Copy the first filter since some of its fields - // are randomly gnerated. - filter2 := &Filter{ - KeySym: filter1.KeySym, - Topics: filter1.Topics, - PoW: filter1.PoW, - AllowP2P: filter1.AllowP2P, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - filter1.Src = ¶ms.Src.PublicKey - filter2.Src = ¶ms.Src.PublicKey - - params.KeySym = filter1.KeySym - params.Topic = BytesToTopic(filter1.Topics[2]) - params.PoW = filter1.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter1) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter2) - if err != nil { - t.Fatalf("failed subscribe 2 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail1 := filter1.Retrieve() - mail2 := filter2.Retrieve() - if len(mail2) == 0 { - t.Fatalf("did not receive any email for filter 2") - } - if len(mail1) == 0 { - t.Fatalf("did not receive any email for filter 1") - } - -} - -func TestSymmetricSendWithoutAKey(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPoW(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter.PoW = DefaultMinimumPoW - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - filter.Src = nil - - params.KeySym = filter.KeySym - params.Topic = BytesToTopic(filter.Topics[2]) - params.PoW = filter.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail := filter.Retrieve() - if len(mail) == 0 { - t.Fatalf("did not receive message in spite of not setting a public key") - } -} - -func TestSymmetricSendKeyMismatch(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPoW(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter.PoW = DefaultMinimumPoW - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.KeySym = filter.KeySym - params.Topic = BytesToTopic(filter.Topics[2]) - params.PoW = filter.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail := filter.Retrieve() - if len(mail) > 0 { - t.Fatalf("received a message when keys weren't matching") - } -} diff --git a/whisper/whisperv6/api.go b/whisper/whisperv6/api.go deleted file mode 100644 index d729e79c35..0000000000 --- a/whisper/whisperv6/api.go +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "context" - "crypto/ecdsa" - "errors" - "fmt" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/rpc" -) - -// List of errors -var ( - ErrSymAsym = errors.New("specify either a symmetric or an asymmetric key") - ErrInvalidSymmetricKey = errors.New("invalid symmetric key") - ErrInvalidPublicKey = errors.New("invalid public key") - ErrInvalidSigningPubKey = errors.New("invalid signing public key") - ErrTooLowPoW = errors.New("message rejected, PoW too low") - ErrNoTopics = errors.New("missing topic(s)") -) - -// PublicWhisperAPI provides the whisper RPC service that can be -// use publicly without security implications. -type PublicWhisperAPI struct { - w *Whisper - - mu sync.Mutex - lastUsed map[string]time.Time // keeps track when a filter was polled for the last time. -} - -// NewPublicWhisperAPI create a new RPC whisper service. -func NewPublicWhisperAPI(w *Whisper) *PublicWhisperAPI { - api := &PublicWhisperAPI{ - w: w, - lastUsed: make(map[string]time.Time), - } - return api -} - -// Version returns the Whisper sub-protocol version. -func (api *PublicWhisperAPI) Version(ctx context.Context) string { - return ProtocolVersionStr -} - -// Info contains diagnostic information. -type Info struct { - Memory int `json:"memory"` // Memory size of the floating messages in bytes. - Messages int `json:"messages"` // Number of floating messages. - MinPow float64 `json:"minPow"` // Minimal accepted PoW - MaxMessageSize uint32 `json:"maxMessageSize"` // Maximum accepted message size -} - -// Info returns diagnostic information about the whisper node. -func (api *PublicWhisperAPI) Info(ctx context.Context) Info { - stats := api.w.Stats() - return Info{ - Memory: stats.memoryUsed, - Messages: len(api.w.messageQueue) + len(api.w.p2pMsgQueue), - MinPow: api.w.MinPow(), - MaxMessageSize: api.w.MaxMessageSize(), - } -} - -// SetMaxMessageSize sets the maximum message size that is accepted. -// Upper limit is defined by MaxMessageSize. -func (api *PublicWhisperAPI) SetMaxMessageSize(ctx context.Context, size uint32) (bool, error) { - return true, api.w.SetMaxMessageSize(size) -} - -// SetMinPoW sets the minimum PoW, and notifies the peers. -func (api *PublicWhisperAPI) SetMinPoW(ctx context.Context, pow float64) (bool, error) { - return true, api.w.SetMinimumPoW(pow) -} - -// SetBloomFilter sets the new value of bloom filter, and notifies the peers. -func (api *PublicWhisperAPI) SetBloomFilter(ctx context.Context, bloom hexutil.Bytes) (bool, error) { - return true, api.w.SetBloomFilter(bloom) -} - -// MarkTrustedPeer marks a 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 (api *PublicWhisperAPI) MarkTrustedPeer(ctx context.Context, enode string) (bool, error) { - n, err := discover.ParseNode(enode) - if err != nil { - return false, err - } - return true, api.w.AllowP2PMessagesFromPeer(n.ID[:]) -} - -// NewKeyPair generates a new public and private key pair for message decryption and encryption. -// It returns an ID that can be used to refer to the keypair. -func (api *PublicWhisperAPI) NewKeyPair(ctx context.Context) (string, error) { - return api.w.NewKeyPair() -} - -// AddPrivateKey imports the given private key. -func (api *PublicWhisperAPI) AddPrivateKey(ctx context.Context, privateKey hexutil.Bytes) (string, error) { - key, err := crypto.ToECDSA(privateKey) - if err != nil { - return "", err - } - return api.w.AddKeyPair(key) -} - -// DeleteKeyPair removes the key with the given key if it exists. -func (api *PublicWhisperAPI) DeleteKeyPair(ctx context.Context, key string) (bool, error) { - if ok := api.w.DeleteKeyPair(key); ok { - return true, nil - } - return false, fmt.Errorf("key pair %s not found", key) -} - -// HasKeyPair returns an indication if the node has a key pair that is associated with the given id. -func (api *PublicWhisperAPI) HasKeyPair(ctx context.Context, id string) bool { - return api.w.HasKeyPair(id) -} - -// GetPublicKey returns the public key associated with the given key. The key is the hex -// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62. -func (api *PublicWhisperAPI) GetPublicKey(ctx context.Context, id string) (hexutil.Bytes, error) { - key, err := api.w.GetPrivateKey(id) - if err != nil { - return hexutil.Bytes{}, err - } - return crypto.FromECDSAPub(&key.PublicKey), nil -} - -// GetPrivateKey returns the private key associated with the given key. The key is the hex -// encoded representation of a key in the form specified in section 4.3.6 of ANSI X9.62. -func (api *PublicWhisperAPI) GetPrivateKey(ctx context.Context, id string) (hexutil.Bytes, error) { - key, err := api.w.GetPrivateKey(id) - if err != nil { - return hexutil.Bytes{}, err - } - return crypto.FromECDSA(key), nil -} - -// NewSymKey generate a random symmetric key. -// It returns an ID that can be used to refer to the key. -// Can be used encrypting and decrypting messages where the key is known to both parties. -func (api *PublicWhisperAPI) NewSymKey(ctx context.Context) (string, error) { - return api.w.GenerateSymKey() -} - -// AddSymKey import a symmetric key. -// It returns an ID that can be used to refer to the key. -// Can be used encrypting and decrypting messages where the key is known to both parties. -func (api *PublicWhisperAPI) AddSymKey(ctx context.Context, key hexutil.Bytes) (string, error) { - return api.w.AddSymKeyDirect([]byte(key)) -} - -// GenerateSymKeyFromPassword derive a key from the given password, stores it, and returns its ID. -func (api *PublicWhisperAPI) GenerateSymKeyFromPassword(ctx context.Context, passwd string) (string, error) { - return api.w.AddSymKeyFromPassword(passwd) -} - -// HasSymKey returns an indication if the node has a symmetric key associated with the given key. -func (api *PublicWhisperAPI) HasSymKey(ctx context.Context, id string) bool { - return api.w.HasSymKey(id) -} - -// GetSymKey returns the symmetric key associated with the given id. -func (api *PublicWhisperAPI) GetSymKey(ctx context.Context, id string) (hexutil.Bytes, error) { - return api.w.GetSymKey(id) -} - -// DeleteSymKey deletes the symmetric key that is associated with the given id. -func (api *PublicWhisperAPI) DeleteSymKey(ctx context.Context, id string) bool { - return api.w.DeleteSymKey(id) -} - -// MakeLightClient turns the node into light client, which does not forward -// any incoming messages, and sends only messages originated in this node. -func (api *PublicWhisperAPI) MakeLightClient(ctx context.Context) bool { - api.w.lightClient = true - return api.w.lightClient -} - -// CancelLightClient cancels light client mode. -func (api *PublicWhisperAPI) CancelLightClient(ctx context.Context) bool { - api.w.lightClient = false - return !api.w.lightClient -} - -//go:generate gencodec -type NewMessage -field-override newMessageOverride -out gen_newmessage_json.go - -// NewMessage represents a new whisper message that is posted through the RPC. -type NewMessage struct { - SymKeyID string `json:"symKeyID"` - PublicKey []byte `json:"pubKey"` - Sig string `json:"sig"` - TTL uint32 `json:"ttl"` - Topic TopicType `json:"topic"` - Payload []byte `json:"payload"` - Padding []byte `json:"padding"` - PowTime uint32 `json:"powTime"` - PowTarget float64 `json:"powTarget"` - TargetPeer string `json:"targetPeer"` -} - -type newMessageOverride struct { - PublicKey hexutil.Bytes - Payload hexutil.Bytes - Padding hexutil.Bytes -} - -// Post posts a message on the Whisper network. -// returns the hash of the message in case of success. -func (api *PublicWhisperAPI) Post(ctx context.Context, req NewMessage) (hexutil.Bytes, error) { - var ( - symKeyGiven = len(req.SymKeyID) > 0 - pubKeyGiven = len(req.PublicKey) > 0 - err error - ) - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { - return nil, ErrSymAsym - } - - params := &MessageParams{ - TTL: req.TTL, - Payload: req.Payload, - Padding: req.Padding, - WorkTime: req.PowTime, - PoW: req.PowTarget, - Topic: req.Topic, - } - - // Set key that is used to sign the message - if len(req.Sig) > 0 { - if params.Src, err = api.w.GetPrivateKey(req.Sig); err != nil { - return nil, err - } - } - - // Set symmetric key that is used to encrypt the message - if symKeyGiven { - if params.Topic == (TopicType{}) { // topics are mandatory with symmetric encryption - return nil, ErrNoTopics - } - if params.KeySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { - return nil, err - } - if !validateDataIntegrity(params.KeySym, aesKeyLength) { - return nil, ErrInvalidSymmetricKey - } - } - - // Set asymmetric key that is used to encrypt the message - if pubKeyGiven { - if params.Dst, err = crypto.UnmarshalPubkey(req.PublicKey); err != nil { - return nil, ErrInvalidPublicKey - } - } - - // encrypt and sent message - whisperMsg, err := NewSentMessage(params) - if err != nil { - return nil, err - } - - var result []byte - env, err := whisperMsg.Wrap(params) - if err != nil { - return nil, err - } - - // send to specific node (skip PoW check) - if len(req.TargetPeer) > 0 { - n, err := discover.ParseNode(req.TargetPeer) - if err != nil { - return nil, fmt.Errorf("failed to parse target peer: %s", err) - } - err = api.w.SendP2PMessage(n.ID[:], env) - if err == nil { - hash := env.Hash() - result = hash[:] - } - return result, err - } - - // ensure that the message PoW meets the node's minimum accepted PoW - if req.PowTarget < api.w.MinPow() { - return nil, ErrTooLowPoW - } - - err = api.w.Send(env) - if err == nil { - hash := env.Hash() - result = hash[:] - } - return result, err -} - -//go:generate gencodec -type Criteria -field-override criteriaOverride -out gen_criteria_json.go - -// Criteria holds various filter options for inbound messages. -type Criteria struct { - SymKeyID string `json:"symKeyID"` - PrivateKeyID string `json:"privateKeyID"` - Sig []byte `json:"sig"` - MinPow float64 `json:"minPow"` - Topics []TopicType `json:"topics"` - AllowP2P bool `json:"allowP2P"` -} - -type criteriaOverride struct { - Sig hexutil.Bytes -} - -// Messages set up a subscription that fires events when messages arrive that match -// the given set of criteria. -func (api *PublicWhisperAPI) Messages(ctx context.Context, crit Criteria) (*rpc.Subscription, error) { - var ( - symKeyGiven = len(crit.SymKeyID) > 0 - pubKeyGiven = len(crit.PrivateKeyID) > 0 - err error - ) - - // ensure that the RPC connection supports subscriptions - notifier, supported := rpc.NotifierFromContext(ctx) - if !supported { - return nil, rpc.ErrNotificationsUnsupported - } - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && pubKeyGiven) || (!symKeyGiven && !pubKeyGiven) { - return nil, ErrSymAsym - } - - filter := Filter{ - PoW: crit.MinPow, - Messages: make(map[common.Hash]*ReceivedMessage), - AllowP2P: crit.AllowP2P, - } - - if len(crit.Sig) > 0 { - if filter.Src, err = crypto.UnmarshalPubkey(crit.Sig); err != nil { - return nil, ErrInvalidSigningPubKey - } - } - - for i, bt := range crit.Topics { - if len(bt) == 0 || len(bt) > 4 { - return nil, fmt.Errorf("subscribe: topic %d has wrong size: %d", i, len(bt)) - } - filter.Topics = append(filter.Topics, bt[:]) - } - - // listen for message that are encrypted with the given symmetric key - if symKeyGiven { - if len(filter.Topics) == 0 { - return nil, ErrNoTopics - } - key, err := api.w.GetSymKey(crit.SymKeyID) - if err != nil { - return nil, err - } - if !validateDataIntegrity(key, aesKeyLength) { - return nil, ErrInvalidSymmetricKey - } - filter.KeySym = key - filter.SymKeyHash = crypto.Keccak256Hash(filter.KeySym) - } - - // listen for messages that are encrypted with the given public key - if pubKeyGiven { - filter.KeyAsym, err = api.w.GetPrivateKey(crit.PrivateKeyID) - if err != nil || filter.KeyAsym == nil { - return nil, ErrInvalidPublicKey - } - } - - id, err := api.w.Subscribe(&filter) - if err != nil { - return nil, err - } - - // create subscription and start waiting for message events - rpcSub := notifier.CreateSubscription() - go func() { - // for now poll internally, refactor whisper internal for channel support - ticker := time.NewTicker(250 * time.Millisecond) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - if filter := api.w.GetFilter(id); filter != nil { - for _, rpcMessage := range toMessage(filter.Retrieve()) { - if err := notifier.Notify(rpcSub.ID, rpcMessage); err != nil { - log.Error("Failed to send notification", "err", err) - } - } - } - case <-rpcSub.Err(): - api.w.Unsubscribe(id) - return - case <-notifier.Closed(): - api.w.Unsubscribe(id) - return - } - } - }() - - return rpcSub, nil -} - -//go:generate gencodec -type Message -field-override messageOverride -out gen_message_json.go - -// Message is the RPC representation of a whisper message. -type Message struct { - Sig []byte `json:"sig,omitempty"` - TTL uint32 `json:"ttl"` - Timestamp uint32 `json:"timestamp"` - Topic TopicType `json:"topic"` - Payload []byte `json:"payload"` - Padding []byte `json:"padding"` - PoW float64 `json:"pow"` - Hash []byte `json:"hash"` - Dst []byte `json:"recipientPublicKey,omitempty"` -} - -type messageOverride struct { - Sig hexutil.Bytes - Payload hexutil.Bytes - Padding hexutil.Bytes - Hash hexutil.Bytes - Dst hexutil.Bytes -} - -// ToWhisperMessage converts an internal message into an API version. -func ToWhisperMessage(message *ReceivedMessage) *Message { - msg := Message{ - Payload: message.Payload, - Padding: message.Padding, - Timestamp: message.Sent, - TTL: message.TTL, - PoW: message.PoW, - Hash: message.EnvelopeHash.Bytes(), - Topic: message.Topic, - } - - if message.Dst != nil { - b := crypto.FromECDSAPub(message.Dst) - if b != nil { - msg.Dst = b - } - } - - if isMessageSigned(message.Raw[0]) { - b := crypto.FromECDSAPub(message.SigToPubKey()) - if b != nil { - msg.Sig = b - } - } - - return &msg -} - -// toMessage converts a set of messages to its RPC representation. -func toMessage(messages []*ReceivedMessage) []*Message { - msgs := make([]*Message, len(messages)) - for i, msg := range messages { - msgs[i] = ToWhisperMessage(msg) - } - return msgs -} - -// GetFilterMessages returns the messages that match the filter criteria and -// are received between the last poll and now. -func (api *PublicWhisperAPI) GetFilterMessages(id string) ([]*Message, error) { - api.mu.Lock() - f := api.w.GetFilter(id) - if f == nil { - api.mu.Unlock() - return nil, fmt.Errorf("filter not found") - } - api.lastUsed[id] = time.Now() - api.mu.Unlock() - - receivedMessages := f.Retrieve() - messages := make([]*Message, 0, len(receivedMessages)) - for _, msg := range receivedMessages { - messages = append(messages, ToWhisperMessage(msg)) - } - - return messages, nil -} - -// DeleteMessageFilter deletes a filter. -func (api *PublicWhisperAPI) DeleteMessageFilter(id string) (bool, error) { - api.mu.Lock() - defer api.mu.Unlock() - - delete(api.lastUsed, id) - return true, api.w.Unsubscribe(id) -} - -// NewMessageFilter creates a new filter that can be used to poll for -// (new) messages that satisfy the given criteria. -func (api *PublicWhisperAPI) NewMessageFilter(req Criteria) (string, error) { - var ( - src *ecdsa.PublicKey - keySym []byte - keyAsym *ecdsa.PrivateKey - topics [][]byte - - symKeyGiven = len(req.SymKeyID) > 0 - asymKeyGiven = len(req.PrivateKeyID) > 0 - - err error - ) - - // user must specify either a symmetric or an asymmetric key - if (symKeyGiven && asymKeyGiven) || (!symKeyGiven && !asymKeyGiven) { - return "", ErrSymAsym - } - - if len(req.Sig) > 0 { - if src, err = crypto.UnmarshalPubkey(req.Sig); err != nil { - return "", ErrInvalidSigningPubKey - } - } - - if symKeyGiven { - if keySym, err = api.w.GetSymKey(req.SymKeyID); err != nil { - return "", err - } - if !validateDataIntegrity(keySym, aesKeyLength) { - return "", ErrInvalidSymmetricKey - } - } - - if asymKeyGiven { - if keyAsym, err = api.w.GetPrivateKey(req.PrivateKeyID); err != nil { - return "", err - } - } - - if len(req.Topics) > 0 { - topics = make([][]byte, len(req.Topics)) - for i, topic := range req.Topics { - topics[i] = make([]byte, TopicLength) - copy(topics[i], topic[:]) - } - } - - f := &Filter{ - Src: src, - KeySym: keySym, - KeyAsym: keyAsym, - PoW: req.MinPow, - AllowP2P: req.AllowP2P, - Topics: topics, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - id, err := api.w.Subscribe(f) - if err != nil { - return "", err - } - - api.mu.Lock() - api.lastUsed[id] = time.Now() - api.mu.Unlock() - - return id, nil -} diff --git a/whisper/whisperv6/api_test.go b/whisper/whisperv6/api_test.go deleted file mode 100644 index 004a41c949..0000000000 --- a/whisper/whisperv6/api_test.go +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2018 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 . - -package whisperv6 - -import ( - "bytes" - "crypto/ecdsa" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - set "gopkg.in/fatih/set.v0" -) - -func TestMultipleTopicCopyInNewMessageFilter(t *testing.T) { - w := &Whisper{ - privateKeys: make(map[string]*ecdsa.PrivateKey), - symKeys: make(map[string][]byte), - envelopes: make(map[common.Hash]*Envelope), - expirations: make(map[uint32]*set.SetNonTS), - peers: make(map[*Peer]struct{}), - messageQueue: make(chan *Envelope, messageQueueLimit), - p2pMsgQueue: make(chan *Envelope, messageQueueLimit), - quit: make(chan struct{}), - syncAllowance: DefaultSyncAllowance, - } - w.filters = NewFilters(w) - - keyID, err := w.GenerateSymKey() - if err != nil { - t.Fatalf("Error generating symmetric key: %v", err) - } - api := PublicWhisperAPI{ - w: w, - lastUsed: make(map[string]time.Time), - } - - t1 := [4]byte{0xde, 0xea, 0xbe, 0xef} - t2 := [4]byte{0xca, 0xfe, 0xde, 0xca} - - crit := Criteria{ - SymKeyID: keyID, - Topics: []TopicType{TopicType(t1), TopicType(t2)}, - } - - _, err = api.NewMessageFilter(crit) - if err != nil { - t.Fatalf("Error creating the filter: %v", err) - } - - found := false - candidates := w.filters.getWatchersByTopic(TopicType(t1)) - for _, f := range candidates { - if len(f.Topics) == 2 { - if bytes.Equal(f.Topics[0], t1[:]) && bytes.Equal(f.Topics[1], t2[:]) { - found = true - } - } - } - - if !found { - t.Fatalf("Could not find filter with both topics") - } -} diff --git a/whisper/whisperv6/benchmarks_test.go b/whisper/whisperv6/benchmarks_test.go deleted file mode 100644 index 0473179da5..0000000000 --- a/whisper/whisperv6/benchmarks_test.go +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "crypto/sha256" - "testing" - - "github.com/ethereum/go-ethereum/crypto" - "golang.org/x/crypto/pbkdf2" -) - -func BenchmarkDeriveKeyMaterial(b *testing.B) { - for i := 0; i < b.N; i++ { - pbkdf2.Key([]byte("test"), nil, 65356, aesKeyLength, sha256.New) - } -} - -func BenchmarkEncryptionSym(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - for i := 0; i < b.N; i++ { - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Errorf("failed Wrap with seed %d: %s.", seed, err) - b.Errorf("i = %d, len(msg.Raw) = %d, params.Payload = %d.", i, len(msg.Raw), len(params.Payload)) - return - } - } -} - -func BenchmarkEncryptionAsym(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - - for i := 0; i < b.N; i++ { - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - } -} - -func BenchmarkDecryptionSymValid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - f := Filter{KeySym: params.KeySym} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg == nil { - b.Fatalf("failed to open with seed %d.", seed) - } - } -} - -func BenchmarkDecryptionSymInvalid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - f := Filter{KeySym: []byte("arbitrary stuff here")} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg != nil { - b.Fatalf("opened envelope with invalid key, seed: %d.", seed) - } - } -} - -func BenchmarkDecryptionAsymValid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - f := Filter{KeyAsym: key} - params.KeySym = nil - params.Dst = &key.PublicKey - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg == nil { - b.Fatalf("fail to open, seed: %d.", seed) - } - } -} - -func BenchmarkDecryptionAsymInvalid(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - key, err := crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - msg, _ := NewSentMessage(params) - env, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - key, err = crypto.GenerateKey() - if err != nil { - b.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - f := Filter{KeyAsym: key} - - for i := 0; i < b.N; i++ { - msg := env.Open(&f) - if msg != nil { - b.Fatalf("opened envelope with invalid key, seed: %d.", seed) - } - } -} - -func increment(x []byte) { - for i := 0; i < len(x); i++ { - x[i]++ - if x[i] != 0 { - break - } - } -} - -func BenchmarkPoW(b *testing.B) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - b.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - params.Payload = make([]byte, 32) - params.PoW = 10.0 - params.TTL = 1 - - for i := 0; i < b.N; i++ { - increment(params.Payload) - msg, _ := NewSentMessage(params) - _, err := msg.Wrap(params) - if err != nil { - b.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - } -} diff --git a/whisper/whisperv6/config.go b/whisper/whisperv6/config.go deleted file mode 100644 index 61419de007..0000000000 --- a/whisper/whisperv6/config.go +++ /dev/null @@ -1,29 +0,0 @@ -// 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 . - -package whisperv6 - -// Config represents the configuration state of a whisper node. -type Config struct { - MaxMessageSize uint32 `toml:",omitempty"` - MinimumAcceptedPOW float64 `toml:",omitempty"` -} - -// DefaultConfig represents (shocker!) the default configuration. -var DefaultConfig = Config{ - MaxMessageSize: DefaultMaxMessageSize, - MinimumAcceptedPOW: DefaultMinimumPoW, -} diff --git a/whisper/whisperv6/doc.go b/whisper/whisperv6/doc.go deleted file mode 100644 index 066a9766d4..0000000000 --- a/whisper/whisperv6/doc.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -/* -Package whisper implements the Whisper protocol (version 6). - -Whisper combines aspects of both DHTs and datagram messaging systems (e.g. UDP). -As such it may be likened and compared to both, not dissimilar to the -matter/energy duality (apologies to physicists for the blatant abuse of a -fundamental and beautiful natural principle). - -Whisper is a pure identity-based messaging system. Whisper provides a low-level -(non-application-specific) but easily-accessible API without being based upon -or prejudiced by the low-level hardware attributes and characteristics, -particularly the notion of singular endpoints. -*/ - -// Contains the Whisper protocol constant definitions - -package whisperv6 - -import ( - "fmt" - "time" -) - -// Whisper protocol parameters -const ( - ProtocolVersion = uint64(6) // Protocol version number - ProtocolVersionStr = "6.0" // The same, as a string - ProtocolName = "shh" // Nickname of the protocol in geth - - // whisper protocol message codes, according to EIP-627 - statusCode = 0 // used by whisper protocol - messagesCode = 1 // normal whisper message - powRequirementCode = 2 // PoW requirement - bloomFilterExCode = 3 // bloom filter exchange - p2pRequestCode = 126 // peer-to-peer message, used by Dapp protocol - p2pMessageCode = 127 // peer-to-peer message (to be consumed by the peer, but not forwarded any further) - NumberOfMessageCodes = 128 - - SizeMask = byte(3) // mask used to extract the size of payload size field from the flags - signatureFlag = byte(4) - - TopicLength = 4 // in bytes - signatureLength = 65 // in bytes - aesKeyLength = 32 // in bytes - aesNonceLength = 12 // in bytes; for more info please see cipher.gcmStandardNonceSize & aesgcm.NonceSize() - keyIDSize = 32 // in bytes - BloomFilterSize = 64 // in bytes - flagsLength = 1 - - EnvelopeHeaderLength = 20 - - MaxMessageSize = uint32(10 * 1024 * 1024) // maximum accepted size of a message. - DefaultMaxMessageSize = uint32(1024 * 1024) - DefaultMinimumPoW = 0.2 - - padSizeLimit = 256 // just an arbitrary number, could be changed without breaking the protocol - messageQueueLimit = 1024 - - expirationCycle = time.Second - transmissionCycle = 300 * time.Millisecond - - DefaultTTL = 50 // seconds - DefaultSyncAllowance = 10 // seconds -) - -type unknownVersionError uint64 - -func (e unknownVersionError) Error() string { - return fmt.Sprintf("invalid envelope version %d", uint64(e)) -} - -// MailServer represents a mail server, capable of -// archiving the old messages for subsequent delivery -// to the peers. Any implementation must ensure that both -// functions are thread-safe. Also, they must return ASAP. -// DeliverMail should use directMessagesCode for delivery, -// in order to bypass the expiry checks. -type MailServer interface { - Archive(env *Envelope) - DeliverMail(whisperPeer *Peer, request *Envelope) -} diff --git a/whisper/whisperv6/envelope.go b/whisper/whisperv6/envelope.go deleted file mode 100644 index c42d1fa8ac..0000000000 --- a/whisper/whisperv6/envelope.go +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Envelope element. - -package whisperv6 - -import ( - "crypto/ecdsa" - "encoding/binary" - "fmt" - gmath "math" - "math/big" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/math" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/crypto/ecies" - "github.com/ethereum/go-ethereum/rlp" -) - -// Envelope represents a clear-text data packet to transmit through the Whisper -// network. Its contents may or may not be encrypted and signed. -type Envelope struct { - Expiry uint32 - TTL uint32 - Topic TopicType - Data []byte - Nonce uint64 - - pow float64 // Message-specific PoW as described in the Whisper specification. - - // the following variables should not be accessed directly, use the corresponding function instead: Hash(), Bloom() - hash common.Hash // Cached hash of the envelope to avoid rehashing every time. - bloom []byte -} - -// size returns the size of envelope as it is sent (i.e. public fields only) -func (e *Envelope) size() int { - return EnvelopeHeaderLength + len(e.Data) -} - -// 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.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, msg *sentMessage) *Envelope { - env := Envelope{ - Expiry: uint32(time.Now().Add(time.Second * time.Duration(ttl)).Unix()), - TTL: ttl, - Topic: topic, - Data: msg.Raw, - Nonce: 0, - } - - return &env -} - -// Seal closes the envelope by spending the requested amount of time as a proof -// of work on hashing the data. -func (e *Envelope) Seal(options *MessageParams) error { - if options.PoW == 0 { - // PoW is not required - return nil - } - - var target, bestBit int - if options.PoW < 0 { - // target is not set - the function should run for a period - // of time specified in WorkTime param. Since we can predict - // the execution time, we can also adjust Expiry. - e.Expiry += options.WorkTime - } else { - target = e.powToFirstBit(options.PoW) - } - - buf := make([]byte, 64) - h := crypto.Keccak256(e.rlpWithoutNonce()) - copy(buf[:32], h) - - finish := time.Now().Add(time.Duration(options.WorkTime) * time.Second).UnixNano() - for nonce := uint64(0); time.Now().UnixNano() < finish; { - for i := 0; i < 1024; i++ { - binary.BigEndian.PutUint64(buf[56:], nonce) - d := new(big.Int).SetBytes(crypto.Keccak256(buf)) - firstBit := math.FirstBitSet(d) - if firstBit > bestBit { - e.Nonce, bestBit = nonce, firstBit - if target > 0 && bestBit >= target { - return nil - } - } - nonce++ - } - } - - if target > 0 && bestBit < target { - return fmt.Errorf("failed to reach the PoW target, specified pow time (%d seconds) was insufficient", options.WorkTime) - } - - return nil -} - -// PoW computes (if necessary) and returns the proof of work target -// of the envelope. -func (e *Envelope) PoW() float64 { - if e.pow == 0 { - e.calculatePoW(0) - } - return e.pow -} - -func (e *Envelope) calculatePoW(diff uint32) { - buf := make([]byte, 64) - h := crypto.Keccak256(e.rlpWithoutNonce()) - copy(buf[:32], h) - binary.BigEndian.PutUint64(buf[56:], e.Nonce) - d := new(big.Int).SetBytes(crypto.Keccak256(buf)) - firstBit := math.FirstBitSet(d) - x := gmath.Pow(2, float64(firstBit)) - x /= float64(e.size()) - x /= float64(e.TTL + diff) - e.pow = x -} - -func (e *Envelope) powToFirstBit(pow float64) int { - x := pow - x *= float64(e.size()) - x *= float64(e.TTL) - bits := gmath.Log2(x) - bits = gmath.Ceil(bits) - res := int(bits) - if res < 1 { - res = 1 - } - 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{}) { - encoded, _ := rlp.EncodeToBytes(e) - e.hash = crypto.Keccak256Hash(encoded) - } - return e.hash -} - -// DecodeRLP decodes an Envelope from an RLP data stream. -func (e *Envelope) DecodeRLP(s *rlp.Stream) error { - raw, err := s.Raw() - if err != nil { - return err - } - // The decoding of Envelope uses the struct fields but also needs - // to compute the hash of the whole RLP-encoded envelope. This - // type has the same structure as Envelope but is not an - // rlp.Decoder (does not implement DecodeRLP function). - // Only public members will be encoded. - type rlpenv Envelope - if err := rlp.DecodeBytes(raw, (*rlpenv)(e)); err != nil { - return err - } - e.hash = crypto.Keccak256Hash(raw) - return nil -} - -// OpenAsymmetric tries to decrypt an envelope, potentially encrypted with a particular key. -func (e *Envelope) OpenAsymmetric(key *ecdsa.PrivateKey) (*ReceivedMessage, error) { - message := &ReceivedMessage{Raw: e.Data} - err := message.decryptAsymmetric(key) - switch err { - case nil: - return message, nil - case ecies.ErrInvalidPublicKey: // addressed to somebody else - return nil, err - default: - return nil, fmt.Errorf("unable to open envelope, decrypt failed: %v", err) - } -} - -// 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) - if err != nil { - msg = nil - } - return msg, err -} - -// Open tries to decrypt an envelope, and populates the message fields in case of success. -func (e *Envelope) Open(watcher *Filter) (msg *ReceivedMessage) { - if watcher == nil { - return nil - } - - // The API interface forbids filters doing both symmetric and asymmetric encryption. - if watcher.expectsAsymmetricEncryption() && watcher.expectsSymmetricEncryption() { - return nil - } - - if watcher.expectsAsymmetricEncryption() { - msg, _ = e.OpenAsymmetric(watcher.KeyAsym) - if msg != nil { - msg.Dst = &watcher.KeyAsym.PublicKey - } - } else if watcher.expectsSymmetricEncryption() { - msg, _ = e.OpenSymmetric(watcher.KeySym) - if msg != nil { - msg.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) - } - } - - if msg != nil { - ok := msg.ValidateAndParse() - if !ok { - return nil - } - msg.Topic = e.Topic - msg.PoW = e.PoW() - msg.TTL = e.TTL - msg.Sent = e.Expiry - e.TTL - msg.EnvelopeHash = e.Hash() - } - return msg -} - -// Bloom maps 4-bytes Topic into 64-byte bloom filter with 3 bits set (at most). -func (e *Envelope) Bloom() []byte { - if e.bloom == nil { - e.bloom = TopicToBloom(e.Topic) - } - return e.bloom -} - -// TopicToBloom converts the topic (4 bytes) to the bloom filter (64 bytes) -func TopicToBloom(topic TopicType) []byte { - b := make([]byte, BloomFilterSize) - var index [3]int - for j := 0; j < 3; j++ { - index[j] = int(topic[j]) - if (topic[3] & (1 << uint(j))) != 0 { - index[j] += 256 - } - } - - for j := 0; j < 3; j++ { - byteIndex := index[j] / 8 - bitIndex := index[j] % 8 - b[byteIndex] = (1 << uint(bitIndex)) - } - return b -} - -// GetEnvelope retrieves an envelope from the message queue by its hash. -// It returns nil if the envelope can not be found. -func (w *Whisper) GetEnvelope(hash common.Hash) *Envelope { - w.poolMu.RLock() - defer w.poolMu.RUnlock() - return w.envelopes[hash] -} diff --git a/whisper/whisperv6/envelope_test.go b/whisper/whisperv6/envelope_test.go deleted file mode 100644 index 410b250a3f..0000000000 --- a/whisper/whisperv6/envelope_test.go +++ /dev/null @@ -1,64 +0,0 @@ -// 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 . - -// Contains the tests associated with the Whisper protocol Envelope object. - -package whisperv6 - -import ( - mrand "math/rand" - "testing" - - "github.com/ethereum/go-ethereum/crypto" -) - -func TestEnvelopeOpenAcceptsOnlyOneKeyTypeInFilter(t *testing.T) { - symKey := make([]byte, aesKeyLength) - mrand.Read(symKey) - - asymKey, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - - params := MessageParams{ - PoW: 0.01, - WorkTime: 1, - TTL: uint32(mrand.Intn(1024)), - Payload: make([]byte, 50), - KeySym: symKey, - Dst: nil, - } - - mrand.Read(params.Payload) - - msg, err := NewSentMessage(¶ms) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - - e, err := msg.Wrap(¶ms) - if err != nil { - t.Fatalf("Failed to Wrap the message in an envelope with seed %d: %s", seed, err) - } - - f := Filter{KeySym: symKey, KeyAsym: asymKey} - - decrypted := e.Open(&f) - if decrypted != nil { - t.Fatalf("Managed to decrypt a message with an invalid filter, seed %d", seed) - } -} diff --git a/whisper/whisperv6/filter.go b/whisper/whisperv6/filter.go deleted file mode 100644 index 2f170ddebe..0000000000 --- a/whisper/whisperv6/filter.go +++ /dev/null @@ -1,279 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "crypto/ecdsa" - "fmt" - "sync" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" -) - -// Filter represents a Whisper message filter -type Filter struct { - Src *ecdsa.PublicKey // Sender of the message - KeyAsym *ecdsa.PrivateKey // Private Key of recipient - KeySym []byte // Key associated with the Topic - Topics [][]byte // Topics to filter messages with - PoW float64 // Proof of work as described in the Whisper spec - AllowP2P bool // Indicates whether this filter is interested in direct peer-to-peer messages - SymKeyHash common.Hash // The Keccak256Hash of the symmetric key, needed for optimization - id string // unique identifier - - Messages map[common.Hash]*ReceivedMessage - mutex sync.RWMutex -} - -// Filters represents a collection of filters -type Filters struct { - watchers map[string]*Filter - - topicMatcher map[TopicType]map[*Filter]struct{} // map a topic to the filters that are interested in being notified when a message matches that topic - allTopicsMatcher map[*Filter]struct{} // list all the filters that will be notified of a new message, no matter what its topic is - - whisper *Whisper - mutex sync.RWMutex -} - -// NewFilters returns a newly created filter collection -func NewFilters(w *Whisper) *Filters { - return &Filters{ - watchers: make(map[string]*Filter), - topicMatcher: make(map[TopicType]map[*Filter]struct{}), - allTopicsMatcher: make(map[*Filter]struct{}), - whisper: w, - } -} - -// Install will add a new filter to the filter collection -func (fs *Filters) Install(watcher *Filter) (string, error) { - if watcher.KeySym != nil && watcher.KeyAsym != nil { - return "", fmt.Errorf("filters must choose between symmetric and asymmetric keys") - } - - if watcher.Messages == nil { - watcher.Messages = make(map[common.Hash]*ReceivedMessage) - } - - id, err := GenerateRandomID() - if err != nil { - return "", err - } - - fs.mutex.Lock() - defer fs.mutex.Unlock() - - if fs.watchers[id] != nil { - return "", fmt.Errorf("failed to generate unique ID") - } - - if watcher.expectsSymmetricEncryption() { - watcher.SymKeyHash = crypto.Keccak256Hash(watcher.KeySym) - } - - watcher.id = id - fs.watchers[id] = watcher - fs.addTopicMatcher(watcher) - return id, err -} - -// Uninstall will remove a filter whose id has been specified from -// the filter collection -func (fs *Filters) Uninstall(id string) bool { - fs.mutex.Lock() - defer fs.mutex.Unlock() - if fs.watchers[id] != nil { - fs.removeFromTopicMatchers(fs.watchers[id]) - delete(fs.watchers, id) - return true - } - return false -} - -// addTopicMatcher adds a filter to the topic matchers. -// If the filter's Topics array is empty, it will be tried on every topic. -// Otherwise, it will be tried on the topics specified. -func (fs *Filters) addTopicMatcher(watcher *Filter) { - if len(watcher.Topics) == 0 { - fs.allTopicsMatcher[watcher] = struct{}{} - } else { - for _, t := range watcher.Topics { - topic := BytesToTopic(t) - if fs.topicMatcher[topic] == nil { - fs.topicMatcher[topic] = make(map[*Filter]struct{}) - } - fs.topicMatcher[topic][watcher] = struct{}{} - } - } -} - -// removeFromTopicMatchers removes a filter from the topic matchers -func (fs *Filters) removeFromTopicMatchers(watcher *Filter) { - delete(fs.allTopicsMatcher, watcher) - for _, topic := range watcher.Topics { - delete(fs.topicMatcher[BytesToTopic(topic)], watcher) - } -} - -// getWatchersByTopic returns a slice containing the filters that -// match a specific topic -func (fs *Filters) getWatchersByTopic(topic TopicType) []*Filter { - res := make([]*Filter, 0, len(fs.allTopicsMatcher)) - for watcher := range fs.allTopicsMatcher { - res = append(res, watcher) - } - for watcher := range fs.topicMatcher[topic] { - res = append(res, watcher) - } - return res -} - -// Get returns a filter from the collection with a specific ID -func (fs *Filters) Get(id string) *Filter { - fs.mutex.RLock() - defer fs.mutex.RUnlock() - return fs.watchers[id] -} - -// NotifyWatchers notifies any filter that has declared interest -// for the envelope's topic. -func (fs *Filters) NotifyWatchers(env *Envelope, p2pMessage bool) { - var msg *ReceivedMessage - - fs.mutex.RLock() - defer fs.mutex.RUnlock() - - candidates := fs.getWatchersByTopic(env.Topic) - for _, watcher := range candidates { - if p2pMessage && !watcher.AllowP2P { - log.Trace(fmt.Sprintf("msg [%x], filter [%s]: p2p messages are not allowed", env.Hash(), watcher.id)) - continue - } - - var match bool - if msg != nil { - match = watcher.MatchMessage(msg) - } else { - match = watcher.MatchEnvelope(env) - if match { - msg = env.Open(watcher) - if msg == nil { - log.Trace("processing message: failed to open", "message", env.Hash().Hex(), "filter", watcher.id) - } - } else { - log.Trace("processing message: does not match", "message", env.Hash().Hex(), "filter", watcher.id) - } - } - - if match && msg != nil { - log.Trace("processing message: decrypted", "hash", env.Hash().Hex()) - if watcher.Src == nil || IsPubKeyEqual(msg.Src, watcher.Src) { - watcher.Trigger(msg) - } - } - } -} - -func (f *Filter) expectsAsymmetricEncryption() bool { - return f.KeyAsym != nil -} - -func (f *Filter) expectsSymmetricEncryption() bool { - return f.KeySym != nil -} - -// Trigger adds a yet-unknown message to the filter's list of -// received messages. -func (f *Filter) Trigger(msg *ReceivedMessage) { - f.mutex.Lock() - defer f.mutex.Unlock() - - if _, exist := f.Messages[msg.EnvelopeHash]; !exist { - f.Messages[msg.EnvelopeHash] = msg - } -} - -// Retrieve will return the list of all received messages associated -// to a filter. -func (f *Filter) Retrieve() (all []*ReceivedMessage) { - f.mutex.Lock() - defer f.mutex.Unlock() - - all = make([]*ReceivedMessage, 0, len(f.Messages)) - for _, msg := range f.Messages { - all = append(all, msg) - } - - f.Messages = make(map[common.Hash]*ReceivedMessage) // delete old messages - return all -} - -// MatchMessage checks if the filter matches an already decrypted -// message (i.e. a Message that has already been handled by -// MatchEnvelope when checked by a previous filter). -// Topics are not checked here, since this is done by topic matchers. -func (f *Filter) MatchMessage(msg *ReceivedMessage) bool { - if f.PoW > 0 && msg.PoW < f.PoW { - return false - } - - if f.expectsAsymmetricEncryption() && msg.isAsymmetricEncryption() { - return IsPubKeyEqual(&f.KeyAsym.PublicKey, msg.Dst) - } else if f.expectsSymmetricEncryption() && msg.isSymmetricEncryption() { - return f.SymKeyHash == msg.SymKeyHash - } - return false -} - -// MatchEnvelope checks if it's worth decrypting the message. If -// it returns `true`, client code is expected to attempt decrypting -// the message and subsequently call MatchMessage. -// Topics are not checked here, since this is done by topic matchers. -func (f *Filter) MatchEnvelope(envelope *Envelope) bool { - return f.PoW <= 0 || envelope.pow >= f.PoW -} - -func matchSingleTopic(topic TopicType, bt []byte) bool { - if len(bt) > TopicLength { - bt = bt[:TopicLength] - } - - if len(bt) < TopicLength { - return false - } - - for j, b := range bt { - if topic[j] != b { - return false - } - } - return true -} - -// IsPubKeyEqual checks that two public keys are equal -func IsPubKeyEqual(a, b *ecdsa.PublicKey) bool { - if !ValidatePublicKey(a) { - return false - } else if !ValidatePublicKey(b) { - return false - } - // the curve is always the same, just compare the points - return a.X.Cmp(b.X) == 0 && a.Y.Cmp(b.Y) == 0 -} diff --git a/whisper/whisperv6/filter_test.go b/whisper/whisperv6/filter_test.go deleted file mode 100644 index 0bb7986c39..0000000000 --- a/whisper/whisperv6/filter_test.go +++ /dev/null @@ -1,867 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "math/big" - mrand "math/rand" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" -) - -var seed int64 - -// InitSingleTest should be called in the beginning of every -// test, which uses RNG, in order to make the tests -// reproduciblity independent of their sequence. -func InitSingleTest() { - seed = time.Now().Unix() - mrand.Seed(seed) -} - -func InitDebugTest(i int64) { - seed = i - mrand.Seed(seed) -} - -type FilterTestCase struct { - f *Filter - id string - alive bool - msgCnt int -} - -func generateFilter(t *testing.T, symmetric bool) (*Filter, error) { - var f Filter - f.Messages = make(map[common.Hash]*ReceivedMessage) - - const topicNum = 8 - f.Topics = make([][]byte, topicNum) - for i := 0; i < topicNum; i++ { - f.Topics[i] = make([]byte, 4) - mrand.Read(f.Topics[i][:]) - f.Topics[i][0] = 0x01 - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("generateFilter 1 failed with seed %d.", seed) - return nil, err - } - f.Src = &key.PublicKey - - if symmetric { - f.KeySym = make([]byte, aesKeyLength) - mrand.Read(f.KeySym) - f.SymKeyHash = crypto.Keccak256Hash(f.KeySym) - } else { - f.KeyAsym, err = crypto.GenerateKey() - if err != nil { - t.Fatalf("generateFilter 2 failed with seed %d.", seed) - return nil, err - } - } - - // AcceptP2P & PoW are not set - return &f, nil -} - -func generateTestCases(t *testing.T, SizeTestFilters int) []FilterTestCase { - cases := make([]FilterTestCase, SizeTestFilters) - for i := 0; i < SizeTestFilters; i++ { - f, _ := generateFilter(t, true) - cases[i].f = f - cases[i].alive = mrand.Int()&int(1) == 0 - } - return cases -} - -func TestInstallFilters(t *testing.T) { - InitSingleTest() - - const SizeTestFilters = 256 - w := New(&Config{}) - filters := NewFilters(w) - tst := generateTestCases(t, SizeTestFilters) - - var err error - var j string - for i := 0; i < SizeTestFilters; i++ { - j, err = filters.Install(tst[i].f) - if err != nil { - t.Fatalf("seed %d: failed to install filter: %s", seed, err) - } - tst[i].id = j - if len(j) != keyIDSize*2 { - t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j)) - } - } - - for _, testCase := range tst { - if !testCase.alive { - filters.Uninstall(testCase.id) - } - } - - for i, testCase := range tst { - fil := filters.Get(testCase.id) - exist := fil != nil - if exist != testCase.alive { - t.Fatalf("seed %d: failed alive: %d, %v, %v", seed, i, exist, testCase.alive) - } - if exist && fil.PoW != testCase.f.PoW { - t.Fatalf("seed %d: failed Get: %d, %v, %v", seed, i, exist, testCase.alive) - } - } -} - -func TestInstallSymKeyGeneratesHash(t *testing.T) { - InitSingleTest() - - w := New(&Config{}) - filters := NewFilters(w) - filter, _ := generateFilter(t, true) - - // save the current SymKeyHash for comparison - initialSymKeyHash := filter.SymKeyHash - - // ensure the SymKeyHash is invalid, for Install to recreate it - var invalid common.Hash - filter.SymKeyHash = invalid - - _, err := filters.Install(filter) - - if err != nil { - t.Fatalf("Error installing the filter: %s", err) - } - - for i, b := range filter.SymKeyHash { - if b != initialSymKeyHash[i] { - t.Fatalf("The filter's symmetric key hash was not properly generated by Install") - } - } -} - -func TestInstallIdenticalFilters(t *testing.T) { - InitSingleTest() - - w := New(&Config{}) - filters := NewFilters(w) - filter1, _ := generateFilter(t, true) - - // Copy the first filter since some of its fields - // are randomly gnerated. - filter2 := &Filter{ - KeySym: filter1.KeySym, - Topics: filter1.Topics, - PoW: filter1.PoW, - AllowP2P: filter1.AllowP2P, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - _, err := filters.Install(filter1) - - if err != nil { - t.Fatalf("Error installing the first filter with seed %d: %s", seed, err) - } - - _, err = filters.Install(filter2) - - if err != nil { - t.Fatalf("Error installing the second filter with seed %d: %s", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("Error generating message parameters with seed %d: %s", seed, err) - } - - params.KeySym = filter1.KeySym - params.Topic = BytesToTopic(filter1.Topics[0]) - - filter1.Src = ¶ms.Src.PublicKey - filter2.Src = ¶ms.Src.PublicKey - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(filter1) - if msg == nil { - t.Fatalf("failed to Open with filter1") - } - - if !filter1.MatchEnvelope(env) { - t.Fatalf("failed matching with the first filter") - } - - if !filter2.MatchEnvelope(env) { - t.Fatalf("failed matching with the first filter") - } - - if !filter1.MatchMessage(msg) { - t.Fatalf("failed matching with the second filter") - } - - if !filter2.MatchMessage(msg) { - t.Fatalf("failed matching with the second filter") - } -} - -func TestInstallFilterWithSymAndAsymKeys(t *testing.T) { - InitSingleTest() - - w := New(&Config{}) - filters := NewFilters(w) - filter1, _ := generateFilter(t, true) - - asymKey, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("Unable to create asymetric keys: %v", err) - } - - // Copy the first filter since some of its fields - // are randomly gnerated. - filter := &Filter{ - KeySym: filter1.KeySym, - KeyAsym: asymKey, - Topics: filter1.Topics, - PoW: filter1.PoW, - AllowP2P: filter1.AllowP2P, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - _, err = filters.Install(filter) - - if err == nil { - t.Fatalf("Error detecting that a filter had both an asymmetric and symmetric key, with seed %d", seed) - } -} - -func TestComparePubKey(t *testing.T) { - InitSingleTest() - - key1, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate first key with seed %d: %s.", seed, err) - } - key2, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate second key with seed %d: %s.", seed, err) - } - if IsPubKeyEqual(&key1.PublicKey, &key2.PublicKey) { - t.Fatalf("public keys are equal, seed %d.", seed) - } - - // generate key3 == key1 - mrand.Seed(seed) - key3, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed to generate third key with seed %d: %s.", seed, err) - } - if IsPubKeyEqual(&key1.PublicKey, &key3.PublicKey) { - t.Fatalf("key1 == key3, seed %d.", seed) - } -} - -func TestMatchEnvelope(t *testing.T) { - InitSingleTest() - - fsym, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - fasym, err := generateFilter(t, false) - if err != nil { - t.Fatalf("failed generateFilter() with seed %d: %s.", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.Topic[0] = 0xFF // topic mismatch - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - // encrypt symmetrically - i := mrand.Int() % 4 - fsym.Topics[i] = params.Topic[:] - fasym.Topics[i] = params.Topic[:] - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap() with seed %d: %s.", seed, err) - } - - // symmetric + matching topic: match - match := fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed) - } - - // symmetric + matching topic + insufficient PoW: mismatch - fsym.PoW = env.PoW() + 1.0 - match = fsym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(symmetric + matching topic + insufficient PoW) asymmetric with seed %d.", seed) - } - - // symmetric + matching topic + sufficient PoW: match - fsym.PoW = env.PoW() / 2 - match = fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(symmetric + matching topic + sufficient PoW) with seed %d.", seed) - } - - // symmetric + topics are nil (wildcard): match - prevTopics := fsym.Topics - fsym.Topics = nil - match = fsym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(symmetric + topics are nil) with seed %d.", seed) - } - fsym.Topics = prevTopics - - // encrypt asymmetrically - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - params.KeySym = nil - params.Dst = &key.PublicKey - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap() with seed %d: %s.", seed, err) - } - - // encryption method mismatch - match = fsym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } - - // asymmetric + mismatching topic: mismatch - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + mismatching topic) with seed %d.", seed) - } - - // asymmetric + matching topic: match - fasym.Topics[i] = fasym.Topics[i+1] - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + matching topic) with seed %d.", seed) - } - - // asymmetric + filter without topic (wildcard): match - fasym.Topics = nil - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + filter without topic) with seed %d.", seed) - } - - // asymmetric + insufficient PoW: mismatch - fasym.PoW = env.PoW() + 1.0 - match = fasym.MatchEnvelope(env) - if match { - t.Fatalf("failed MatchEnvelope(asymmetric + insufficient PoW) with seed %d.", seed) - } - - // asymmetric + sufficient PoW: match - fasym.PoW = env.PoW() / 2 - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(asymmetric + sufficient PoW) with seed %d.", seed) - } - - // filter without topic + envelope without topic: match - env.Topic = TopicType{} - match = fasym.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed) - } - - // filter with topic + envelope without topic: mismatch - fasym.Topics = fsym.Topics - match = fasym.MatchEnvelope(env) - if !match { - // topic mismatch should have no affect, as topics are handled by topic matchers - t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed) - } -} - -func TestMatchMessageSym(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - const index = 1 - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[index]) - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(f) - if msg == nil { - t.Fatalf("failed Open with seed %d.", seed) - } - - // Src: match - *f.Src.X = *params.Src.PublicKey.X - *f.Src.Y = *params.Src.PublicKey.Y - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src match) with seed %d.", seed) - } - - // insufficient PoW: mismatch - f.PoW = msg.PoW + 1.0 - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed) - } - - // sufficient PoW: match - f.PoW = msg.PoW / 2 - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed) - } - - // topic mismatch - f.Topics[index][0]++ - if !f.MatchMessage(msg) { - // topic mismatch should have no affect, as topics are handled by topic matchers - t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) - } - f.Topics[index][0]-- - - // key mismatch - f.SymKeyHash[0]++ - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed) - } - f.SymKeyHash[0]-- - - // Src absent: match - f.Src = nil - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed) - } - - // key hash mismatch - h := f.SymKeyHash - f.SymKeyHash = common.Hash{} - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key hash mismatch) with seed %d.", seed) - } - f.SymKeyHash = h - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key hash match) with seed %d.", seed) - } - - // encryption method mismatch - f.KeySym = nil - f.KeyAsym, err = crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } -} - -func TestMatchMessageAsym(t *testing.T) { - InitSingleTest() - - f, err := generateFilter(t, false) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - const index = 1 - params.Topic = BytesToTopic(f.Topics[index]) - params.Dst = &f.KeyAsym.PublicKey - keySymOrig := params.KeySym - params.KeySym = nil - - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - msg := env.Open(f) - if msg == nil { - t.Fatalf("failed to open with seed %d.", seed) - } - - // Src: match - *f.Src.X = *params.Src.PublicKey.X - *f.Src.Y = *params.Src.PublicKey.Y - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchMessage(src match) with seed %d.", seed) - } - - // insufficient PoW: mismatch - f.PoW = msg.PoW + 1.0 - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed) - } - - // sufficient PoW: match - f.PoW = msg.PoW / 2 - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed) - } - - // topic mismatch - f.Topics[index][0]++ - if !f.MatchMessage(msg) { - // topic mismatch should have no affect, as topics are handled by topic matchers - t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed) - } - f.Topics[index][0]-- - - // key mismatch - prev := *f.KeyAsym.PublicKey.X - zero := *big.NewInt(0) - *f.KeyAsym.PublicKey.X = zero - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed) - } - *f.KeyAsym.PublicKey.X = prev - - // Src absent: match - f.Src = nil - if !f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed) - } - - // encryption method mismatch - f.KeySym = keySymOrig - f.KeyAsym = nil - if f.MatchMessage(msg) { - t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed) - } -} - -func cloneFilter(orig *Filter) *Filter { - var clone Filter - clone.Messages = make(map[common.Hash]*ReceivedMessage) - clone.Src = orig.Src - clone.KeyAsym = orig.KeyAsym - clone.KeySym = orig.KeySym - clone.Topics = orig.Topics - clone.PoW = orig.PoW - clone.AllowP2P = orig.AllowP2P - clone.SymKeyHash = orig.SymKeyHash - return &clone -} - -func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - return nil - } - - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[2]) - sentMessage, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := sentMessage.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - return nil - } - return env -} - -func TestWatchers(t *testing.T) { - InitSingleTest() - - const NumFilters = 16 - const NumMessages = 256 - var i int - var j uint32 - var e *Envelope - var x, firstID string - var err error - - w := New(&Config{}) - filters := NewFilters(w) - tst := generateTestCases(t, NumFilters) - for i = 0; i < NumFilters; i++ { - tst[i].f.Src = nil - x, err = filters.Install(tst[i].f) - if err != nil { - t.Fatalf("failed to install filter with seed %d: %s.", seed, err) - } - tst[i].id = x - if len(firstID) == 0 { - firstID = x - } - } - - lastID := x - - var envelopes [NumMessages]*Envelope - for i = 0; i < NumMessages; i++ { - j = mrand.Uint32() % NumFilters - e = generateCompatibeEnvelope(t, tst[j].f) - envelopes[i] = e - tst[j].msgCnt++ - } - - for i = 0; i < NumMessages; i++ { - filters.NotifyWatchers(envelopes[i], false) - } - - var total int - var mail []*ReceivedMessage - var count [NumFilters]int - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - count[i] = len(mail) - total += len(mail) - } - - if total != NumMessages { - t.Fatalf("failed with seed %d: total = %d, want: %d.", seed, total, NumMessages) - } - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - if len(mail) != 0 { - t.Fatalf("failed with seed %d: i = %d.", seed, i) - } - - if tst[i].msgCnt != count[i] { - t.Fatalf("failed with seed %d: count[%d]: get %d, want %d.", seed, i, tst[i].msgCnt, count[i]) - } - } - - // another round with a cloned filter - - clone := cloneFilter(tst[0].f) - filters.Uninstall(lastID) - total = 0 - last := NumFilters - 1 - tst[last].f = clone - filters.Install(clone) - for i = 0; i < NumFilters; i++ { - tst[i].msgCnt = 0 - count[i] = 0 - } - - // make sure that the first watcher receives at least one message - e = generateCompatibeEnvelope(t, tst[0].f) - envelopes[0] = e - tst[0].msgCnt++ - for i = 1; i < NumMessages; i++ { - j = mrand.Uint32() % NumFilters - e = generateCompatibeEnvelope(t, tst[j].f) - envelopes[i] = e - tst[j].msgCnt++ - } - - for i = 0; i < NumMessages; i++ { - filters.NotifyWatchers(envelopes[i], false) - } - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - count[i] = len(mail) - total += len(mail) - } - - combined := tst[0].msgCnt + tst[last].msgCnt - if total != NumMessages+count[0] { - t.Fatalf("failed with seed %d: total = %d, count[0] = %d.", seed, total, count[0]) - } - - if combined != count[0] { - t.Fatalf("failed with seed %d: combined = %d, count[0] = %d.", seed, combined, count[0]) - } - - if combined != count[last] { - t.Fatalf("failed with seed %d: combined = %d, count[last] = %d.", seed, combined, count[last]) - } - - for i = 1; i < NumFilters-1; i++ { - mail = tst[i].f.Retrieve() - if len(mail) != 0 { - t.Fatalf("failed with seed %d: i = %d.", seed, i) - } - - if tst[i].msgCnt != count[i] { - t.Fatalf("failed with seed %d: i = %d, get %d, want %d.", seed, i, tst[i].msgCnt, count[i]) - } - } - - // test AcceptP2P - - total = 0 - filters.NotifyWatchers(envelopes[0], true) - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - total += len(mail) - } - - if total != 0 { - t.Fatalf("failed with seed %d: total: got %d, want 0.", seed, total) - } - - f := filters.Get(firstID) - if f == nil { - t.Fatalf("failed to get the filter with seed %d.", seed) - } - f.AllowP2P = true - total = 0 - filters.NotifyWatchers(envelopes[0], true) - - for i = 0; i < NumFilters; i++ { - mail = tst[i].f.Retrieve() - total += len(mail) - } - - if total != 1 { - t.Fatalf("failed with seed %d: total: got %d, want 1.", seed, total) - } -} - -func TestVariableTopics(t *testing.T) { - InitSingleTest() - - const lastTopicByte = 3 - var match bool - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateFilter with seed %d: %s.", seed, err) - } - - for i := 0; i < 4; i++ { - env.Topic = BytesToTopic(f.Topics[i]) - match = f.MatchEnvelope(env) - if !match { - t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i) - } - - f.Topics[i][lastTopicByte]++ - match = f.MatchEnvelope(env) - if !match { - // topic mismatch should have no affect, as topics are handled by topic matchers - t.Fatalf("MatchEnvelope symmetric with seed %d, step %d.", seed, i) - } - } -} - -func TestMatchSingleTopic_ReturnTrue(t *testing.T) { - bt := []byte("test") - topic := BytesToTopic(bt) - - if !matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_WithTail_ReturnTrue(t *testing.T) { - bt := []byte("test with tail") - topic := BytesToTopic([]byte("test")) - - if !matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_NotEquals_ReturnFalse(t *testing.T) { - bt := []byte("tes") - topic := BytesToTopic(bt) - - if matchSingleTopic(topic, bt) { - t.FailNow() - } -} - -func TestMatchSingleTopic_InsufficientLength_ReturnFalse(t *testing.T) { - bt := []byte("test") - topic := BytesToTopic([]byte("not_equal")) - - if matchSingleTopic(topic, bt) { - t.FailNow() - } -} diff --git a/whisper/whisperv6/gen_criteria_json.go b/whisper/whisperv6/gen_criteria_json.go deleted file mode 100644 index 1a428d6df7..0000000000 --- a/whisper/whisperv6/gen_criteria_json.go +++ /dev/null @@ -1,66 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv6 - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*criteriaOverride)(nil) - -// MarshalJSON marshals type Criteria to a json string -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) -} - -// UnmarshalJSON unmarshals type Criteria to a json string -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 -} diff --git a/whisper/whisperv6/gen_message_json.go b/whisper/whisperv6/gen_message_json.go deleted file mode 100644 index 6218f5df6e..0000000000 --- a/whisper/whisperv6/gen_message_json.go +++ /dev/null @@ -1,84 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv6 - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*messageOverride)(nil) - -// MarshalJSON marshals type Message to a json string -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) -} - -// UnmarshalJSON unmarshals type Message to a json string -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 -} diff --git a/whisper/whisperv6/gen_newmessage_json.go b/whisper/whisperv6/gen_newmessage_json.go deleted file mode 100644 index 75a1279ae3..0000000000 --- a/whisper/whisperv6/gen_newmessage_json.go +++ /dev/null @@ -1,90 +0,0 @@ -// Code generated by github.com/fjl/gencodec. DO NOT EDIT. - -package whisperv6 - -import ( - "encoding/json" - - "github.com/ethereum/go-ethereum/common/hexutil" -) - -var _ = (*newMessageOverride)(nil) - -// MarshalJSON marshals type NewMessage to a json string -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) -} - -// UnmarshalJSON unmarshals type NewMessage to a json string -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 -} diff --git a/whisper/whisperv6/message.go b/whisper/whisperv6/message.go deleted file mode 100644 index 2d4e862441..0000000000 --- a/whisper/whisperv6/message.go +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Message element. - -package whisperv6 - -import ( - "crypto/aes" - "crypto/cipher" - "crypto/ecdsa" - crand "crypto/rand" - "encoding/binary" - "errors" - mrand "math/rand" - "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" -) - -// MessageParams specifies the exact way a message should be wrapped -// into an Envelope. -type MessageParams struct { - TTL uint32 - Src *ecdsa.PrivateKey - Dst *ecdsa.PublicKey - KeySym []byte - Topic TopicType - WorkTime uint32 - PoW float64 - Payload []byte - Padding []byte -} - -// SentMessage represents an end-user data packet to transmit through the -// Whisper protocol. These are wrapped into Envelopes that need not be -// understood by intermediate nodes, just forwarded. -type sentMessage struct { - Raw []byte -} - -// ReceivedMessage represents a data packet to be received through the -// Whisper protocol and successfully decrypted. -type ReceivedMessage struct { - Raw []byte - - Payload []byte - Padding []byte - Signature []byte - Salt []byte - - PoW float64 // Proof of work as described in the Whisper spec - Sent uint32 // Time when the message was posted into the network - TTL uint32 // Maximum time to live allowed for the message - Src *ecdsa.PublicKey // Message recipient (identity used to decode the message) - Dst *ecdsa.PublicKey // Message recipient (identity used to decode the message) - Topic TopicType - - SymKeyHash common.Hash // The Keccak256Hash of the key - EnvelopeHash common.Hash // Message envelope hash to act as a unique id -} - -func isMessageSigned(flags byte) bool { - return (flags & signatureFlag) != 0 -} - -func (msg *ReceivedMessage) isSymmetricEncryption() bool { - return msg.SymKeyHash != common.Hash{} -} - -func (msg *ReceivedMessage) isAsymmetricEncryption() bool { - return msg.Dst != nil -} - -// NewSentMessage creates and initializes a non-signed, non-encrypted Whisper message. -func NewSentMessage(params *MessageParams) (*sentMessage, error) { - const payloadSizeFieldMaxSize = 4 - msg := sentMessage{} - msg.Raw = make([]byte, 1, - flagsLength+payloadSizeFieldMaxSize+len(params.Payload)+len(params.Padding)+signatureLength+padSizeLimit) - msg.Raw[0] = 0 // set all the flags to zero - msg.addPayloadSizeField(params.Payload) - msg.Raw = append(msg.Raw, params.Payload...) - err := msg.appendPadding(params) - return &msg, err -} - -// addPayloadSizeField appends the auxiliary field containing the size of payload -func (msg *sentMessage) addPayloadSizeField(payload []byte) { - fieldSize := getSizeOfPayloadSizeField(payload) - field := make([]byte, 4) - binary.LittleEndian.PutUint32(field, uint32(len(payload))) - field = field[:fieldSize] - msg.Raw = append(msg.Raw, field...) - msg.Raw[0] |= byte(fieldSize) -} - -// getSizeOfPayloadSizeField returns the number of bytes necessary to encode the size of payload -func getSizeOfPayloadSizeField(payload []byte) int { - s := 1 - for i := len(payload); i >= 256; i /= 256 { - s++ - } - return s -} - -// appendPadding appends the padding specified in params. -// If no padding is provided in params, then random padding is generated. -func (msg *sentMessage) appendPadding(params *MessageParams) error { - if len(params.Padding) != 0 { - // padding data was provided by the Dapp, just use it as is - msg.Raw = append(msg.Raw, params.Padding...) - return nil - } - - rawSize := flagsLength + getSizeOfPayloadSizeField(params.Payload) + len(params.Payload) - if params.Src != nil { - rawSize += signatureLength - } - odd := rawSize % padSizeLimit - paddingSize := padSizeLimit - odd - pad := make([]byte, paddingSize) - _, err := crand.Read(pad) - if err != nil { - return err - } - if !validateDataIntegrity(pad, paddingSize) { - return errors.New("failed to generate random padding of size " + strconv.Itoa(paddingSize)) - } - msg.Raw = append(msg.Raw, pad...) - return nil -} - -// sign calculates and sets the cryptographic signature for the message, -// also setting the sign flag. -func (msg *sentMessage) sign(key *ecdsa.PrivateKey) error { - if isMessageSigned(msg.Raw[0]) { - // this should not happen, but no reason to panic - log.Error("failed to sign the message: already signed") - return nil - } - - msg.Raw[0] |= signatureFlag // it is important to set this flag before signing - hash := crypto.Keccak256(msg.Raw) - signature, err := crypto.Sign(hash, key) - if err != nil { - msg.Raw[0] &= (0xFF ^ signatureFlag) // clear the flag - return err - } - msg.Raw = append(msg.Raw, signature...) - return nil -} - -// encryptAsymmetric encrypts a message with a public key. -func (msg *sentMessage) encryptAsymmetric(key *ecdsa.PublicKey) error { - if !ValidatePublicKey(key) { - return errors.New("invalid public key provided for asymmetric encryption") - } - encrypted, err := ecies.Encrypt(crand.Reader, ecies.ImportECDSAPublic(key), msg.Raw, nil, nil) - if err == nil { - msg.Raw = encrypted - } - return err -} - -// 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) (err error) { - if !validateDataIntegrity(key, aesKeyLength) { - return errors.New("invalid key provided for symmetric encryption, size: " + strconv.Itoa(len(key))) - } - block, err := aes.NewCipher(key) - if err != nil { - return err - } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return err - } - salt, err := generateSecureRandomData(aesNonceLength) // never use more than 2^32 random nonces with a given key - if err != nil { - return err - } - encrypted := aesgcm.Seal(nil, salt, msg.Raw, nil) - msg.Raw = append(encrypted, salt...) - return nil -} - -// generateSecureRandomData generates random data where extra security is required. -// The purpose of this function is to prevent some bugs in software or in hardware -// from delivering not-very-random data. This is especially useful for AES nonce, -// where true randomness does not really matter, but it is very important to have -// a unique nonce for every message. -func generateSecureRandomData(length int) ([]byte, error) { - x := make([]byte, length) - y := make([]byte, length) - res := make([]byte, length) - - _, err := crand.Read(x) - if err != nil { - return nil, err - } else if !validateDataIntegrity(x, length) { - return nil, errors.New("crypto/rand failed to generate secure random data") - } - _, err = mrand.Read(y) - if err != nil { - return nil, err - } else if !validateDataIntegrity(y, length) { - return nil, errors.New("math/rand failed to generate secure random data") - } - for i := 0; i < length; i++ { - res[i] = x[i] ^ y[i] - } - if !validateDataIntegrity(res, length) { - return nil, errors.New("failed to generate secure random data") - } - return res, nil -} - -// Wrap bundles the message into an Envelope to transmit over the network. -func (msg *sentMessage) Wrap(options *MessageParams) (envelope *Envelope, err error) { - if options.TTL == 0 { - options.TTL = DefaultTTL - } - if options.Src != nil { - if err = msg.sign(options.Src); err != nil { - return nil, err - } - } - if options.Dst != nil { - err = msg.encryptAsymmetric(options.Dst) - } else if options.KeySym != nil { - err = msg.encryptSymmetric(options.KeySym) - } else { - err = errors.New("unable to encrypt the message: neither symmetric nor assymmetric key provided") - } - if err != nil { - return nil, err - } - - envelope = NewEnvelope(options.TTL, options.Topic, msg) - if err = envelope.Seal(options); err != nil { - return nil, err - } - return envelope, nil -} - -// 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) error { - // symmetric messages are expected to contain the 12-byte nonce at the end of the payload - if len(msg.Raw) < aesNonceLength { - return errors.New("missing salt or invalid payload in symmetric message") - } - salt := msg.Raw[len(msg.Raw)-aesNonceLength:] - - block, err := aes.NewCipher(key) - if err != nil { - return err - } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - return err - } - decrypted, err := aesgcm.Open(nil, salt, msg.Raw[:len(msg.Raw)-aesNonceLength], nil) - if err != nil { - return err - } - msg.Raw = decrypted - msg.Salt = salt - return nil -} - -// decryptAsymmetric decrypts an encrypted payload with a private key. -func (msg *ReceivedMessage) decryptAsymmetric(key *ecdsa.PrivateKey) error { - decrypted, err := ecies.ImportECDSA(key).Decrypt(msg.Raw, nil, nil) - if err == nil { - msg.Raw = decrypted - } - return err -} - -// ValidateAndParse checks the message validity and extracts the fields in case of success. -func (msg *ReceivedMessage) ValidateAndParse() bool { - end := len(msg.Raw) - if end < 1 { - return false - } - - if isMessageSigned(msg.Raw[0]) { - end -= signatureLength - if end <= 1 { - return false - } - msg.Signature = msg.Raw[end : end+signatureLength] - msg.Src = msg.SigToPubKey() - if msg.Src == nil { - return false - } - } - - beg := 1 - payloadSize := 0 - sizeOfPayloadSizeField := int(msg.Raw[0] & SizeMask) // number of bytes indicating the size of payload - if sizeOfPayloadSizeField != 0 { - payloadSize = int(bytesToUintLittleEndian(msg.Raw[beg : beg+sizeOfPayloadSizeField])) - if payloadSize+1 > end { - return false - } - beg += sizeOfPayloadSizeField - msg.Payload = msg.Raw[beg : beg+payloadSize] - } - - beg += payloadSize - msg.Padding = msg.Raw[beg:end] - return true -} - -// SigToPubKey returns the public key associated to the message's -// signature. -func (msg *ReceivedMessage) SigToPubKey() *ecdsa.PublicKey { - defer func() { recover() }() // in case of invalid signature - - pub, err := crypto.SigToPub(msg.hash(), msg.Signature) - if err != nil { - log.Error("failed to recover public key from signature", "err", err) - return nil - } - return pub -} - -// hash calculates the SHA3 checksum of the message flags, payload size field, payload and padding. -func (msg *ReceivedMessage) hash() []byte { - if isMessageSigned(msg.Raw[0]) { - sz := len(msg.Raw) - signatureLength - return crypto.Keccak256(msg.Raw[:sz]) - } - return crypto.Keccak256(msg.Raw) -} diff --git a/whisper/whisperv6/message_test.go b/whisper/whisperv6/message_test.go deleted file mode 100644 index 0a5c1c8533..0000000000 --- a/whisper/whisperv6/message_test.go +++ /dev/null @@ -1,471 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "bytes" - "crypto/aes" - "crypto/cipher" - mrand "math/rand" - "testing" - - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/rlp" -) - -func generateMessageParams() (*MessageParams, error) { - // set all the parameters except p.Dst and p.Padding - - buf := make([]byte, 4) - mrand.Read(buf) - sz := mrand.Intn(400) - - var p MessageParams - p.PoW = 0.01 - p.WorkTime = 1 - p.TTL = uint32(mrand.Intn(1024)) - p.Payload = make([]byte, sz) - p.KeySym = make([]byte, aesKeyLength) - mrand.Read(p.Payload) - mrand.Read(p.KeySym) - p.Topic = BytesToTopic(buf) - - var err error - p.Src, err = crypto.GenerateKey() - if err != nil { - return nil, err - } - - return &p, nil -} - -func singleMessageTest(t *testing.T, symmetric bool) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - - if !symmetric { - params.KeySym = nil - params.Dst = &key.PublicKey - } - - text := make([]byte, 0, 512) - text = append(text, params.Payload...) - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - var decrypted *ReceivedMessage - if symmetric { - decrypted, err = env.OpenSymmetric(params.KeySym) - } else { - decrypted, err = env.OpenAsymmetric(key) - } - - if err != nil { - t.Fatalf("failed to encrypt with seed %d: %s.", seed, err) - } - - if !decrypted.ValidateAndParse() { - t.Fatalf("failed to validate with seed %d, symmetric = %v.", seed, symmetric) - } - - if !bytes.Equal(text, decrypted.Payload) { - t.Fatalf("failed with seed %d: compare payload.", seed) - } - if !isMessageSigned(decrypted.Raw[0]) { - t.Fatalf("failed with seed %d: unsigned.", seed) - } - if len(decrypted.Signature) != signatureLength { - t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature)) - } - if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) { - t.Fatalf("failed with seed %d: signature mismatch.", seed) - } -} - -func TestMessageEncryption(t *testing.T) { - InitSingleTest() - - var symmetric bool - for i := 0; i < 256; i++ { - singleMessageTest(t, symmetric) - symmetric = !symmetric - } -} - -func TestMessageWrap(t *testing.T) { - seed = int64(1777444222) - mrand.Seed(seed) - target := 128.0 - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1 - params.WorkTime = 12 - params.PoW = target - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - pow := env.PoW() - if pow < target { - t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target) - } - - // set PoW target too high, expect error - msg2, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1000000 - params.WorkTime = 1 - params.PoW = 10000000.0 - _, err = msg2.Wrap(params) - if err == nil { - t.Fatalf("unexpectedly reached the PoW target with seed %d.", seed) - } -} - -func TestMessageSeal(t *testing.T) { - // this test depends on deterministic choice of seed (1976726903) - seed = int64(1976726903) - mrand.Seed(seed) - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.TTL = 1 - - env := NewEnvelope(params.TTL, params.Topic, msg) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - env.Expiry = uint32(seed) // make it deterministic - target := 32.0 - params.WorkTime = 4 - params.PoW = target - env.Seal(params) - - env.calculatePoW(0) - pow := env.PoW() - if pow < target { - t.Fatalf("failed Wrap with seed %d: pow < target (%f vs. %f).", seed, pow, target) - } - - params.WorkTime = 1 - params.PoW = 1000000000.0 - env.Seal(params) - env.calculatePoW(0) - pow = env.PoW() - if pow < 2*target { - t.Fatalf("failed Wrap with seed %d: pow too small %f.", seed, pow) - } -} - -func TestEnvelopeOpen(t *testing.T) { - InitSingleTest() - - var symmetric bool - for i := 0; i < 32; i++ { - singleEnvelopeOpenTest(t, symmetric) - symmetric = !symmetric - } -} - -func singleEnvelopeOpenTest(t *testing.T, symmetric bool) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - key, err := crypto.GenerateKey() - if err != nil { - t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err) - } - - if !symmetric { - params.KeySym = nil - params.Dst = &key.PublicKey - } - - text := make([]byte, 0, 512) - text = append(text, params.Payload...) - - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - var f Filter - if symmetric { - f = Filter{KeySym: params.KeySym} - } else { - f = Filter{KeyAsym: key} - } - decrypted := env.Open(&f) - if decrypted == nil { - t.Fatalf("failed to open with seed %d.", seed) - } - - if !bytes.Equal(text, decrypted.Payload) { - t.Fatalf("failed with seed %d: compare payload.", seed) - } - if !isMessageSigned(decrypted.Raw[0]) { - t.Fatalf("failed with seed %d: unsigned.", seed) - } - if len(decrypted.Signature) != signatureLength { - t.Fatalf("failed with seed %d: signature len %d.", seed, len(decrypted.Signature)) - } - if !IsPubKeyEqual(decrypted.Src, ¶ms.Src.PublicKey) { - t.Fatalf("failed with seed %d: signature mismatch.", seed) - } - if decrypted.isAsymmetricEncryption() == symmetric { - t.Fatalf("failed with seed %d: asymmetric %v vs. %v.", seed, decrypted.isAsymmetricEncryption(), symmetric) - } - if decrypted.isSymmetricEncryption() != symmetric { - t.Fatalf("failed with seed %d: symmetric %v vs. %v.", seed, decrypted.isSymmetricEncryption(), symmetric) - } - if !symmetric { - if decrypted.Dst == nil { - t.Fatalf("failed with seed %d: dst is nil.", seed) - } - if !IsPubKeyEqual(decrypted.Dst, &key.PublicKey) { - t.Fatalf("failed with seed %d: Dst.", seed) - } - } -} - -func TestEncryptWithZeroKey(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = make([]byte, aesKeyLength) - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with zero key, seed: %d.", seed) - } - - params, err = generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = make([]byte, 0) - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with empty key, seed: %d.", seed) - } - - params, err = generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - params.KeySym = nil - _, err = msg.Wrap(params) - if err == nil { - t.Fatalf("wrapped with nil key, seed: %d.", seed) - } -} - -func TestRlpEncode(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("wrapped with zero key, seed: %d.", seed) - } - - raw, err := rlp.EncodeToBytes(env) - if err != nil { - t.Fatalf("RLP encode failed: %s.", err) - } - - var decoded Envelope - rlp.DecodeBytes(raw, &decoded) - if err != nil { - t.Fatalf("RLP decode failed: %s.", err) - } - - he := env.Hash() - hd := decoded.Hash() - - if he != hd { - t.Fatalf("Hashes are not equal: %x vs. %x", he, hd) - } -} - -func singlePaddingTest(t *testing.T, padSize int) { - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d and sz=%d: %s.", seed, padSize, err) - } - params.Padding = make([]byte, padSize) - params.PoW = 0.0000000001 - pad := make([]byte, padSize) - _, err = mrand.Read(pad) - if err != nil { - t.Fatalf("padding is not generated (seed %d): %s", seed, err) - } - n := copy(params.Padding, pad) - if n != padSize { - t.Fatalf("padding is not copied (seed %d): %s", seed, err) - } - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed to wrap, seed: %d and sz=%d.", seed, padSize) - } - f := Filter{KeySym: params.KeySym} - decrypted := env.Open(&f) - if decrypted == nil { - t.Fatalf("failed to open, seed and sz=%d: %d.", seed, padSize) - } - if !bytes.Equal(pad, decrypted.Padding) { - t.Fatalf("padding is not retireved as expected with seed %d and sz=%d:\n[%x]\n[%x].", seed, padSize, pad, decrypted.Padding) - } -} - -func TestPadding(t *testing.T) { - InitSingleTest() - - for i := 1; i < 260; i++ { - singlePaddingTest(t, i) - } - - lim := 256 * 256 - for i := lim - 5; i < lim+2; i++ { - singlePaddingTest(t, i) - } - - for i := 0; i < 256; i++ { - n := mrand.Intn(256*254) + 256 - singlePaddingTest(t, n) - } - - for i := 0; i < 256; i++ { - n := mrand.Intn(256*1024) + 256*256 - singlePaddingTest(t, n) - } -} - -func TestPaddingAppendedToSymMessagesWithSignature(t *testing.T) { - params := &MessageParams{ - Payload: make([]byte, 246), - KeySym: make([]byte, aesKeyLength), - } - - pSrc, err := crypto.GenerateKey() - - if err != nil { - t.Fatalf("Error creating the signature key %v", err) - return - } - params.Src = pSrc - - // Simulate a message with a payload just under 256 so that - // payload + flag + signature > 256. Check that the result - // is padded on the next 256 boundary. - msg := sentMessage{} - const payloadSizeFieldMinSize = 1 - msg.Raw = make([]byte, flagsLength+payloadSizeFieldMinSize+len(params.Payload)) - - err = msg.appendPadding(params) - - if err != nil { - t.Fatalf("Error appending padding to message %v", err) - return - } - - if len(msg.Raw) != 512-signatureLength { - t.Errorf("Invalid size %d != 512", len(msg.Raw)) - } -} - -func TestAesNonce(t *testing.T) { - key := hexutil.MustDecode("0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31") - block, err := aes.NewCipher(key) - if err != nil { - t.Fatalf("NewCipher failed: %s", err) - } - aesgcm, err := cipher.NewGCM(block) - if err != nil { - t.Fatalf("NewGCM failed: %s", err) - } - // This is the most important single test in this package. - // If it fails, whisper will not be working. - if aesgcm.NonceSize() != aesNonceLength { - t.Fatalf("Nonce size is wrong. This is a critical error. Apparently AES nonce size have changed in the new version of AES GCM package. Whisper will not be working until this problem is resolved.") - } -} diff --git a/whisper/whisperv6/peer.go b/whisper/whisperv6/peer.go deleted file mode 100644 index 2bf1c905b2..0000000000 --- a/whisper/whisperv6/peer.go +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "fmt" - "math" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rlp" - set "gopkg.in/fatih/set.v0" -) - -// Peer represents a whisper protocol peer connection. -type Peer struct { - host *Whisper - peer *p2p.Peer - ws p2p.MsgReadWriter - - trusted bool - powRequirement float64 - bloomMu sync.Mutex - bloomFilter []byte - fullNode bool - - known *set.Set // Messages already known by the peer to avoid wasting bandwidth - - quit chan struct{} -} - -// newPeer creates a new whisper peer object, but does not run the handshake itself. -func newPeer(host *Whisper, remote *p2p.Peer, rw p2p.MsgReadWriter) *Peer { - return &Peer{ - host: host, - peer: remote, - ws: rw, - trusted: false, - powRequirement: 0.0, - known: set.New(), - quit: make(chan struct{}), - bloomFilter: MakeFullNodeBloom(), - fullNode: true, - } -} - -// start initiates the peer updater, periodically broadcasting the whisper packets -// into the network. -func (peer *Peer) start() { - go peer.update() - log.Trace("start", "peer", peer.ID()) -} - -// stop terminates the peer updater, stopping message forwarding to it. -func (peer *Peer) stop() { - close(peer.quit) - log.Trace("stop", "peer", peer.ID()) -} - -// handshake sends the protocol initiation status message to the remote peer and -// verifies the remote status too. -func (peer *Peer) handshake() error { - // Send the handshake status message asynchronously - errc := make(chan error, 1) - go func() { - pow := peer.host.MinPow() - powConverted := math.Float64bits(pow) - bloom := peer.host.BloomFilter() - errc <- p2p.SendItems(peer.ws, statusCode, ProtocolVersion, powConverted, bloom) - }() - - // Fetch the remote status packet and verify protocol match - packet, err := peer.ws.ReadMsg() - if err != nil { - return err - } - if packet.Code != statusCode { - return fmt.Errorf("peer [%x] sent packet %x before status packet", peer.ID(), packet.Code) - } - s := rlp.NewStream(packet.Payload, uint64(packet.Size)) - _, err = s.List() - if err != nil { - return fmt.Errorf("peer [%x] sent bad status message: %v", peer.ID(), err) - } - peerVersion, err := s.Uint() - if err != nil { - return fmt.Errorf("peer [%x] sent bad status message (unable to decode version): %v", peer.ID(), err) - } - if peerVersion != ProtocolVersion { - return fmt.Errorf("peer [%x]: protocol version mismatch %d != %d", peer.ID(), peerVersion, ProtocolVersion) - } - - // only version is mandatory, subsequent parameters are optional - powRaw, err := s.Uint() - if err == nil { - pow := math.Float64frombits(powRaw) - if math.IsInf(pow, 0) || math.IsNaN(pow) || pow < 0.0 { - return fmt.Errorf("peer [%x] sent bad status message: invalid pow", peer.ID()) - } - peer.powRequirement = pow - - var bloom []byte - err = s.Decode(&bloom) - if err == nil { - sz := len(bloom) - if sz != BloomFilterSize && sz != 0 { - return fmt.Errorf("peer [%x] sent bad status message: wrong bloom filter size %d", peer.ID(), sz) - } - peer.setBloomFilter(bloom) - } - } - - if err := <-errc; err != nil { - return fmt.Errorf("peer [%x] failed to send status packet: %v", peer.ID(), err) - } - return nil -} - -// update executes periodic operations on the peer, including message transmission -// and expiration. -func (peer *Peer) update() { - // Start the tickers for the updates - expire := time.NewTicker(expirationCycle) - transmit := time.NewTicker(transmissionCycle) - - // Loop and transmit until termination is requested - for { - select { - case <-expire.C: - peer.expire() - - case <-transmit.C: - if err := peer.broadcast(); err != nil { - log.Trace("broadcast failed", "reason", err, "peer", peer.ID()) - return - } - - case <-peer.quit: - return - } - } -} - -// mark marks an envelope known to the peer so that it won't be sent back. -func (peer *Peer) mark(envelope *Envelope) { - peer.known.Add(envelope.Hash()) -} - -// marked checks if an envelope is already known to the remote peer. -func (peer *Peer) marked(envelope *Envelope) bool { - return peer.known.Has(envelope.Hash()) -} - -// expire iterates over all the known envelopes in the host and removes all -// expired (unknown) ones from the known list. -func (peer *Peer) expire() { - unmark := make(map[common.Hash]struct{}) - peer.known.Each(func(v interface{}) bool { - if !peer.host.isEnvelopeCached(v.(common.Hash)) { - unmark[v.(common.Hash)] = struct{}{} - } - return true - }) - // Dump all known but no longer cached - for hash := range unmark { - peer.known.Remove(hash) - } -} - -// broadcast iterates over the collection of envelopes and transmits yet unknown -// ones over the network. -func (peer *Peer) broadcast() error { - envelopes := peer.host.Envelopes() - bundle := make([]*Envelope, 0, len(envelopes)) - for _, envelope := range envelopes { - if !peer.marked(envelope) && envelope.PoW() >= peer.powRequirement && peer.bloomMatch(envelope) { - bundle = append(bundle, envelope) - } - } - - if len(bundle) > 0 { - // transmit the batch of envelopes - if err := p2p.Send(peer.ws, messagesCode, bundle); err != nil { - return err - } - - // mark envelopes only if they were successfully sent - for _, e := range bundle { - peer.mark(e) - } - - log.Trace("broadcast", "num. messages", len(bundle)) - } - return nil -} - -// ID returns a peer's id -func (peer *Peer) ID() []byte { - id := peer.peer.ID() - return id[:] -} - -func (peer *Peer) notifyAboutPowRequirementChange(pow float64) error { - i := math.Float64bits(pow) - return p2p.Send(peer.ws, powRequirementCode, i) -} - -func (peer *Peer) notifyAboutBloomFilterChange(bloom []byte) error { - return p2p.Send(peer.ws, bloomFilterExCode, bloom) -} - -func (peer *Peer) bloomMatch(env *Envelope) bool { - peer.bloomMu.Lock() - defer peer.bloomMu.Unlock() - return peer.fullNode || BloomFilterMatch(peer.bloomFilter, env.Bloom()) -} - -func (peer *Peer) setBloomFilter(bloom []byte) { - peer.bloomMu.Lock() - defer peer.bloomMu.Unlock() - peer.bloomFilter = bloom - peer.fullNode = isFullNode(bloom) - if peer.fullNode && peer.bloomFilter == nil { - peer.bloomFilter = MakeFullNodeBloom() - } -} - -func MakeFullNodeBloom() []byte { - bloom := make([]byte, BloomFilterSize) - for i := 0; i < BloomFilterSize; i++ { - bloom[i] = 0xFF - } - return bloom -} diff --git a/whisper/whisperv6/peer_test.go b/whisper/whisperv6/peer_test.go deleted file mode 100644 index 0c9b380901..0000000000 --- a/whisper/whisperv6/peer_test.go +++ /dev/null @@ -1,509 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "bytes" - "crypto/ecdsa" - "fmt" - mrand "math/rand" - "sync" - "sync/atomic" - "testing" - "time" - - "net" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/p2p/discover" - "github.com/ethereum/go-ethereum/p2p/nat" -) - -var keys = []string{ - "d49dcf37238dc8a7aac57dc61b9fee68f0a97f062968978b9fafa7d1033d03a9", - "73fd6143c48e80ed3c56ea159fe7494a0b6b393a392227b422f4c3e8f1b54f98", - "119dd32adb1daa7a4c7bf77f847fb28730785aa92947edf42fdd997b54de40dc", - "deeda8709dea935bb772248a3144dea449ffcc13e8e5a1fd4ef20ce4e9c87837", - "5bd208a079633befa349441bdfdc4d85ba9bd56081525008380a63ac38a407cf", - "1d27fb4912002d58a2a42a50c97edb05c1b3dffc665dbaa42df1fe8d3d95c9b5", - "15def52800c9d6b8ca6f3066b7767a76afc7b611786c1276165fbc61636afb68", - "51be6ab4b2dc89f251ff2ace10f3c1cc65d6855f3e083f91f6ff8efdfd28b48c", - "ef1ef7441bf3c6419b162f05da6037474664f198b58db7315a6f4de52414b4a0", - "09bdf6985aabc696dc1fbeb5381aebd7a6421727343872eb2fadfc6d82486fd9", - "15d811bf2e01f99a224cdc91d0cf76cea08e8c67905c16fee9725c9be71185c4", - "2f83e45cf1baaea779789f755b7da72d8857aeebff19362dd9af31d3c9d14620", - "73f04e34ac6532b19c2aae8f8e52f38df1ac8f5cd10369f92325b9b0494b0590", - "1e2e07b69e5025537fb73770f483dc8d64f84ae3403775ef61cd36e3faf162c1", - "8963d9bbb3911aac6d30388c786756b1c423c4fbbc95d1f96ddbddf39809e43a", - "0422da85abc48249270b45d8de38a4cc3c02032ede1fcf0864a51092d58a2f1f", - "8ae5c15b0e8c7cade201fdc149831aa9b11ff626a7ffd27188886cc108ad0fa8", - "acd8f5a71d4aecfcb9ad00d32aa4bcf2a602939b6a9dd071bab443154184f805", - "a285a922125a7481600782ad69debfbcdb0316c1e97c267aff29ef50001ec045", - "28fd4eee78c6cd4bf78f39f8ab30c32c67c24a6223baa40e6f9c9a0e1de7cef5", - "c5cca0c9e6f043b288c6f1aef448ab59132dab3e453671af5d0752961f013fc7", - "46df99b051838cb6f8d1b73f232af516886bd8c4d0ee07af9a0a033c391380fd", - "c6a06a53cbaadbb432884f36155c8f3244e244881b5ee3e92e974cfa166d793f", - "783b90c75c63dc72e2f8d11b6f1b4de54d63825330ec76ee8db34f06b38ea211", - "9450038f10ca2c097a8013e5121b36b422b95b04892232f930a29292d9935611", - "e215e6246ed1cfdcf7310d4d8cdbe370f0d6a8371e4eb1089e2ae05c0e1bc10f", - "487110939ed9d64ebbc1f300adeab358bc58875faf4ca64990fbd7fe03b78f2b", - "824a70ea76ac81366da1d4f4ac39de851c8ac49dca456bb3f0a186ceefa269a5", - "ba8f34fa40945560d1006a328fe70c42e35cc3d1017e72d26864cd0d1b150f15", - "30a5dfcfd144997f428901ea88a43c8d176b19c79dde54cc58eea001aa3d246c", - "de59f7183aca39aa245ce66a05245fecfc7e2c75884184b52b27734a4a58efa2", - "92629e2ff5f0cb4f5f08fffe0f64492024d36f045b901efb271674b801095c5a", - "7184c1701569e3a4c4d2ddce691edd983b81e42e09196d332e1ae2f1e062cff4", -} - -type TestData struct { - started int64 - counter [NumNodes]int - mutex sync.RWMutex -} - -type TestNode struct { - shh *Whisper - id *ecdsa.PrivateKey - server *p2p.Server - filerID string -} - -const NumNodes = 8 // must not exceed the number of keys (32) - -var result TestData -var nodes [NumNodes]*TestNode -var sharedKey = hexutil.MustDecode("0x03ca634cae0d49acb401d8a4c6b6fe8c55b70d115bf400769cc1400f3258cd31") -var wrongKey = hexutil.MustDecode("0xf91156714d7ec88d3edc1c652c2181dbb3044e8771c683f3b30d33c12b986b11") -var sharedTopic = TopicType{0xF, 0x1, 0x2, 0} -var wrongTopic = TopicType{0, 0, 0, 0} -var expectedMessage = []byte("per aspera ad astra") -var unexpectedMessage = []byte("per rectum ad astra") -var masterBloomFilter []byte -var masterPow = 0.00000001 -var round = 1 -var debugMode = false -var prevTime time.Time -var cntPrev int - -func TestSimulation(t *testing.T) { - // create a chain of whisper nodes, - // installs the filters with shared (predefined) parameters - initialize(t) - - // each node sends one random (not decryptable) message - for i := 0; i < NumNodes; i++ { - sendMsg(t, false, i) - } - - // node #0 sends one expected (decryptable) message - sendMsg(t, true, 0) - - // check if each node have received and decrypted exactly one message - checkPropagation(t, true) - - // check if Status message was correctly decoded - checkBloomFilterExchange(t) - checkPowExchange(t) - - // send new pow and bloom exchange messages - resetParams(t) - - // node #1 sends one expected (decryptable) message - sendMsg(t, true, 1) - - // check if each node (except node #0) have received and decrypted exactly one message - checkPropagation(t, false) - - // check if corresponding protocol-level messages were correctly decoded - checkPowExchangeForNodeZero(t) - checkBloomFilterExchange(t) - - stopServers() -} - -func resetParams(t *testing.T) { - // change pow only for node zero - masterPow = 7777777.0 - nodes[0].shh.SetMinimumPoW(masterPow) - - // change bloom for all nodes - masterBloomFilter = TopicToBloom(sharedTopic) - for i := 0; i < NumNodes; i++ { - nodes[i].shh.SetBloomFilter(masterBloomFilter) - } - - round++ -} - -func initBloom(t *testing.T) { - masterBloomFilter = make([]byte, BloomFilterSize) - _, err := mrand.Read(masterBloomFilter) - if err != nil { - t.Fatalf("rand failed: %s.", err) - } - - msgBloom := TopicToBloom(sharedTopic) - masterBloomFilter = addBloom(masterBloomFilter, msgBloom) - for i := 0; i < 32; i++ { - masterBloomFilter[i] = 0xFF - } - - if !BloomFilterMatch(masterBloomFilter, msgBloom) { - t.Fatalf("bloom mismatch on initBloom.") - } -} - -func initialize(t *testing.T) { - initBloom(t) - - var err error - - for i := 0; i < NumNodes; i++ { - var node TestNode - b := make([]byte, BloomFilterSize) - copy(b, masterBloomFilter) - node.shh = New(&DefaultConfig) - node.shh.SetMinimumPoW(masterPow) - node.shh.SetBloomFilter(b) - if !bytes.Equal(node.shh.BloomFilter(), masterBloomFilter) { - t.Fatalf("bloom mismatch on init.") - } - node.shh.Start(nil) - topics := make([]TopicType, 0) - topics = append(topics, sharedTopic) - f := Filter{KeySym: sharedKey} - f.Topics = [][]byte{topics[0][:]} - node.filerID, err = node.shh.Subscribe(&f) - if err != nil { - t.Fatalf("failed to install the filter: %s.", err) - } - node.id, err = crypto.HexToECDSA(keys[i]) - if err != nil { - t.Fatalf("failed convert the key: %s.", keys[i]) - } - name := common.MakeName("whisper-go", "2.0") - - node.server = &p2p.Server{ - Config: p2p.Config{ - PrivateKey: node.id, - MaxPeers: NumNodes/2 + 1, - Name: name, - Protocols: node.shh.Protocols(), - ListenAddr: "127.0.0.1:0", - NAT: nat.Any(), - }, - } - - go startServer(t, node.server) - - nodes[i] = &node - } - - waitForServersToStart(t) - - for i := 0; i < NumNodes; i++ { - for j := 0; j < i; j++ { - peerNodeId := nodes[j].id - address, _ := net.ResolveTCPAddr("tcp", nodes[j].server.ListenAddr) - peerPort := uint16(address.Port) - peerNode := discover.PubkeyID(&peerNodeId.PublicKey) - peer := discover.NewNode(peerNode, address.IP, peerPort, peerPort) - nodes[i].server.AddPeer(peer) - } - } -} - -func startServer(t *testing.T, s *p2p.Server) { - err := s.Start() - if err != nil { - t.Fatalf("failed to start the fisrt server.") - } - - atomic.AddInt64(&result.started, 1) -} - -func stopServers() { - for i := 0; i < NumNodes; i++ { - n := nodes[i] - if n != nil { - n.shh.Unsubscribe(n.filerID) - n.shh.Stop() - n.server.Stop() - } - } -} - -func checkPropagation(t *testing.T, includingNodeZero bool) { - if t.Failed() { - return - } - - prevTime = time.Now() - // (cycle * iterations) should not exceed 50 seconds, since TTL=50 - const cycle = 200 // time in milliseconds - const iterations = 250 - - first := 0 - if !includingNodeZero { - first = 1 - } - - for j := 0; j < iterations; j++ { - for i := first; i < NumNodes; i++ { - f := nodes[i].shh.GetFilter(nodes[i].filerID) - if f == nil { - t.Fatalf("failed to get filterId %s from node %d, round %d.", nodes[i].filerID, i, round) - } - - mail := f.Retrieve() - validateMail(t, i, mail) - - if isTestComplete() { - checkTestStatus() - return - } - } - - checkTestStatus() - time.Sleep(cycle * time.Millisecond) - } - - if !includingNodeZero { - f := nodes[0].shh.GetFilter(nodes[0].filerID) - if f != nil { - t.Fatalf("node zero received a message with low PoW.") - } - } - - t.Fatalf("Test was not complete (%d round): timeout %d seconds. nodes=%v", round, iterations*cycle/1000, nodes) -} - -func validateMail(t *testing.T, index int, mail []*ReceivedMessage) { - var cnt int - for _, m := range mail { - if bytes.Equal(m.Payload, expectedMessage) { - cnt++ - } - } - - if cnt == 0 { - // no messages received yet: nothing is wrong - return - } - if cnt > 1 { - t.Fatalf("node %d received %d.", index, cnt) - } - - if cnt == 1 { - result.mutex.Lock() - defer result.mutex.Unlock() - result.counter[index] += cnt - if result.counter[index] > 1 { - t.Fatalf("node %d accumulated %d.", index, result.counter[index]) - } - } -} - -func checkTestStatus() { - var cnt int - var arr [NumNodes]int - - for i := 0; i < NumNodes; i++ { - arr[i] = nodes[i].server.PeerCount() - envelopes := nodes[i].shh.Envelopes() - if len(envelopes) >= NumNodes { - cnt++ - } - } - - if debugMode { - if cntPrev != cnt { - fmt.Printf(" %v \t number of nodes that have received all msgs: %d, number of peers per node: %v \n", - time.Since(prevTime), cnt, arr) - prevTime = time.Now() - cntPrev = cnt - } - } -} - -func isTestComplete() bool { - result.mutex.RLock() - defer result.mutex.RUnlock() - - for i := 0; i < NumNodes; i++ { - if result.counter[i] < 1 { - return false - } - } - - for i := 0; i < NumNodes; i++ { - envelopes := nodes[i].shh.Envelopes() - if len(envelopes) < NumNodes+1 { - return false - } - } - - return true -} - -func sendMsg(t *testing.T, expected bool, id int) { - if t.Failed() { - return - } - - opt := MessageParams{KeySym: sharedKey, Topic: sharedTopic, Payload: expectedMessage, PoW: 0.00000001, WorkTime: 1} - if !expected { - opt.KeySym = wrongKey - opt.Topic = wrongTopic - opt.Payload = unexpectedMessage - opt.Payload[0] = byte(id) - } - - msg, err := NewSentMessage(&opt) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - envelope, err := msg.Wrap(&opt) - if err != nil { - t.Fatalf("failed to seal message: %s", err) - } - - err = nodes[id].shh.Send(envelope) - if err != nil { - t.Fatalf("failed to send message: %s", err) - } -} - -func TestPeerBasic(t *testing.T) { - InitSingleTest() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d.", seed) - } - - params.PoW = 0.001 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d.", seed) - } - - p := newPeer(nil, nil, nil) - p.mark(env) - if !p.marked(env) { - t.Fatalf("failed mark with seed %d.", seed) - } -} - -func checkPowExchangeForNodeZero(t *testing.T) { - const iterations = 200 - for j := 0; j < iterations; j++ { - lastCycle := (j == iterations-1) - ok := checkPowExchangeForNodeZeroOnce(t, lastCycle) - if ok { - break - } - time.Sleep(50 * time.Millisecond) - } -} - -func checkPowExchangeForNodeZeroOnce(t *testing.T, mustPass bool) bool { - cnt := 0 - for i, node := range nodes { - for peer := range node.shh.peers { - if peer.peer.ID() == discover.PubkeyID(&nodes[0].id.PublicKey) { - cnt++ - if peer.powRequirement != masterPow { - if mustPass { - t.Fatalf("node %d: failed to set the new pow requirement for node zero.", i) - } else { - return false - } - } - } - } - } - if cnt == 0 { - t.Fatalf("looking for node zero: no matching peers found.") - } - return true -} - -func checkPowExchange(t *testing.T) { - for i, node := range nodes { - for peer := range node.shh.peers { - if peer.peer.ID() != discover.PubkeyID(&nodes[0].id.PublicKey) { - if peer.powRequirement != masterPow { - t.Fatalf("node %d: failed to exchange pow requirement in round %d; expected %f, got %f", - i, round, masterPow, peer.powRequirement) - } - } - } - } -} - -func checkBloomFilterExchangeOnce(t *testing.T, mustPass bool) bool { - for i, node := range nodes { - for peer := range node.shh.peers { - peer.bloomMu.Lock() - equals := bytes.Equal(peer.bloomFilter, masterBloomFilter) - peer.bloomMu.Unlock() - if !equals { - if mustPass { - t.Fatalf("node %d: failed to exchange bloom filter requirement in round %d. \n%x expected \n%x got", - i, round, masterBloomFilter, peer.bloomFilter) - } else { - return false - } - } - } - } - - return true -} - -func checkBloomFilterExchange(t *testing.T) { - const iterations = 200 - for j := 0; j < iterations; j++ { - lastCycle := (j == iterations-1) - ok := checkBloomFilterExchangeOnce(t, lastCycle) - if ok { - break - } - time.Sleep(50 * time.Millisecond) - } -} - -func waitForServersToStart(t *testing.T) { - const iterations = 200 - var started int64 - for j := 0; j < iterations; j++ { - time.Sleep(50 * time.Millisecond) - started = atomic.LoadInt64(&result.started) - if started == NumNodes { - return - } - } - t.Fatalf("Failed to start all the servers, running: %d", started) -} diff --git a/whisper/whisperv6/topic.go b/whisper/whisperv6/topic.go deleted file mode 100644 index 4dd8f283c3..0000000000 --- a/whisper/whisperv6/topic.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -// Contains the Whisper protocol Topic element. - -package whisperv6 - -import ( - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/common/hexutil" -) - -// TopicType represents a cryptographically secure, probabilistic partial -// classifications of a message, determined as the first (left) 4 bytes of the -// SHA3 hash of some arbitrary data given by the original author of the message. -type TopicType [TopicLength]byte - -// BytesToTopic converts from the byte array representation of a topic -// into the TopicType type. -func BytesToTopic(b []byte) (t TopicType) { - sz := TopicLength - if x := len(b); x < TopicLength { - sz = x - } - for i := 0; i < sz; i++ { - t[i] = b[i] - } - return t -} - -// String converts a topic byte array to a string representation. -func (t *TopicType) String() string { - return common.ToHex(t[:]) -} - -// MarshalText returns the hex representation of t. -func (t TopicType) MarshalText() ([]byte, error) { - return hexutil.Bytes(t[:]).MarshalText() -} - -// UnmarshalText parses a hex representation to a topic. -func (t *TopicType) UnmarshalText(input []byte) error { - return hexutil.UnmarshalFixedText("Topic", input, t[:]) -} diff --git a/whisper/whisperv6/topic_test.go b/whisper/whisperv6/topic_test.go deleted file mode 100644 index 454afe0de1..0000000000 --- a/whisper/whisperv6/topic_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "encoding/json" - "testing" -) - -var topicStringTests = []struct { - topic TopicType - str string -}{ - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, str: "0x00000000"}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, str: "0x007f80ff"}, - {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, str: "0xff807f00"}, - {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, str: "0xf26e7779"}, -} - -func TestTopicString(t *testing.T) { - for i, tst := range topicStringTests { - s := tst.topic.String() - if s != tst.str { - t.Fatalf("failed test %d: have %s, want %s.", i, s, tst.str) - } - } -} - -var bytesToTopicTests = []struct { - data []byte - topic TopicType -}{ - {topic: TopicType{0x8f, 0x9a, 0x2b, 0x7d}, data: []byte{0x8f, 0x9a, 0x2b, 0x7d}}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte{0x00, 0x7f, 0x80, 0xff}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00, 0x00}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{0x00, 0x00, 0x00}}, - {topic: TopicType{0x01, 0x00, 0x00, 0x00}, data: []byte{0x01}}, - {topic: TopicType{0x00, 0xfe, 0x00, 0x00}, data: []byte{0x00, 0xfe}}, - {topic: TopicType{0xea, 0x1d, 0x43, 0x00}, data: []byte{0xea, 0x1d, 0x43}}, - {topic: TopicType{0x6f, 0x3c, 0xb0, 0xdd}, data: []byte{0x6f, 0x3c, 0xb0, 0xdd, 0x0f, 0x00, 0x90}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte{}}, - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: nil}, -} - -var unmarshalTestsGood = []struct { - topic TopicType - data []byte -}{ - {topic: TopicType{0x00, 0x00, 0x00, 0x00}, data: []byte(`"0x00000000"`)}, - {topic: TopicType{0x00, 0x7f, 0x80, 0xff}, data: []byte(`"0x007f80ff"`)}, - {topic: TopicType{0xff, 0x80, 0x7f, 0x00}, data: []byte(`"0xff807f00"`)}, - {topic: TopicType{0xf2, 0x6e, 0x77, 0x79}, data: []byte(`"0xf26e7779"`)}, -} - -var unmarshalTestsBad = []struct { - topic TopicType - data []byte -}{ - {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(`"0x000000000"`)}, - {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(`"0000000"`)}, - {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(`"abcdefg0"`)}, -} - -var unmarshalTestsUgly = []struct { - topic TopicType - data []byte -}{ - {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) { - for i, tst := range unmarshalTestsGood { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err != nil { - t.Errorf("failed test %d. input: %v. err: %v", i, tst.data, err) - } else if top != tst.topic { - t.Errorf("failed test %d: have %v, want %v.", i, t, tst.topic) - } - } -} - -func TestUnmarshalTestsBad(t *testing.T) { - // in this test UnmarshalJSON() is supposed to fail - for i, tst := range unmarshalTestsBad { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err == nil { - t.Fatalf("failed test %d. input: %v.", i, tst.data) - } - } -} - -func TestUnmarshalTestsUgly(t *testing.T) { - // in this test UnmarshalJSON() is NOT supposed to fail, but result should be wrong - for i, tst := range unmarshalTestsUgly { - var top TopicType - err := json.Unmarshal(tst.data, &top) - if err != nil { - t.Errorf("failed test %d. input: %v.", i, tst.data) - } else if top == tst.topic { - t.Errorf("failed test %d: have %v, want %v.", i, top, tst.topic) - } - } -} diff --git a/whisper/whisperv6/whisper.go b/whisper/whisperv6/whisper.go deleted file mode 100644 index 3418c7bf7d..0000000000 --- a/whisper/whisperv6/whisper.go +++ /dev/null @@ -1,1050 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "bytes" - "crypto/ecdsa" - "crypto/sha256" - "fmt" - "math" - "runtime" - "sync" - "time" - - "github.com/ethereum/go-ethereum/common" - "github.com/ethereum/go-ethereum/crypto" - "github.com/ethereum/go-ethereum/log" - "github.com/ethereum/go-ethereum/p2p" - "github.com/ethereum/go-ethereum/rlp" - "github.com/ethereum/go-ethereum/rpc" - "github.com/syndtr/goleveldb/leveldb/errors" - "golang.org/x/crypto/pbkdf2" - "golang.org/x/sync/syncmap" - set "gopkg.in/fatih/set.v0" -) - -// Statistics holds several message-related counter for analytics -// purposes. -type Statistics struct { - messagesCleared int - memoryCleared int - memoryUsed int - cycles int - totalMessagesCleared int -} - -const ( - maxMsgSizeIdx = iota // Maximal message length allowed by the whisper node - overflowIdx // Indicator of message queue overflow - minPowIdx // Minimal PoW required by the whisper node - minPowToleranceIdx // Minimal PoW tolerated by the whisper node for a limited time - bloomFilterIdx // Bloom filter for topics of interest for this node - bloomFilterToleranceIdx // Bloom filter tolerated by the whisper node for a limited time -) - -// Whisper represents a dark communication interface through the Ethereum -// network, using its very own P2P communication layer. -type Whisper struct { - protocol p2p.Protocol // Protocol description and parameters - filters *Filters // Message filters installed with Subscribe function - - privateKeys map[string]*ecdsa.PrivateKey // Private key storage - symKeys map[string][]byte // Symmetric key storage - 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 - expirations map[uint32]*set.SetNonTS // Message expiration pool - - 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 - p2pMsgQueue chan *Envelope // Message queue for peer-to-peer messages (not to be forwarded any further) - quit chan struct{} // Channel used for graceful exit - - settings syncmap.Map // holds configuration settings that can be dynamically changed - - syncAllowance int // maximum time in seconds allowed to process the whisper-related messages - - lightClient bool // indicates is this node is pure light client (does not forward any messages) - - statsMu sync.Mutex // guard stats - stats Statistics // Statistics of whisper node - - mailServer MailServer // MailServer interface -} - -// New creates a Whisper client ready to communicate through the Ethereum P2P network. -func New(cfg *Config) *Whisper { - if cfg == nil { - cfg = &DefaultConfig - } - - whisper := &Whisper{ - privateKeys: make(map[string]*ecdsa.PrivateKey), - symKeys: make(map[string][]byte), - envelopes: make(map[common.Hash]*Envelope), - expirations: make(map[uint32]*set.SetNonTS), - peers: make(map[*Peer]struct{}), - messageQueue: make(chan *Envelope, messageQueueLimit), - p2pMsgQueue: make(chan *Envelope, messageQueueLimit), - quit: make(chan struct{}), - syncAllowance: DefaultSyncAllowance, - } - - 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 - whisper.protocol = p2p.Protocol{ - Name: ProtocolName, - Version: uint(ProtocolVersion), - Length: NumberOfMessageCodes, - Run: whisper.HandlePeer, - NodeInfo: func() interface{} { - return map[string]interface{}{ - "version": ProtocolVersionStr, - "maxMessageSize": whisper.MaxMessageSize(), - "minimumPoW": whisper.MinPow(), - } - }, - } - - return whisper -} - -// MinPow returns the PoW value required by this node. -func (whisper *Whisper) MinPow() float64 { - val, exist := whisper.settings.Load(minPowIdx) - if !exist || val == nil { - return DefaultMinimumPoW - } - v, ok := val.(float64) - if !ok { - log.Error("Error loading minPowIdx, using default") - return DefaultMinimumPoW - } - return v -} - -// MinPowTolerance returns the value of minimum PoW which is tolerated for a limited -// time after PoW was changed. If sufficient time have elapsed or no change of PoW -// have ever occurred, the return value will be the same as return value of MinPow(). -func (whisper *Whisper) MinPowTolerance() float64 { - val, exist := whisper.settings.Load(minPowToleranceIdx) - if !exist || val == nil { - return DefaultMinimumPoW - } - return val.(float64) -} - -// BloomFilter returns the aggregated bloom filter for all the topics of interest. -// The nodes are required to send only messages that match the advertised bloom filter. -// If a message does not match the bloom, it will tantamount to spam, and the peer will -// be disconnected. -func (whisper *Whisper) BloomFilter() []byte { - val, exist := whisper.settings.Load(bloomFilterIdx) - if !exist || val == nil { - return nil - } - return val.([]byte) -} - -// BloomFilterTolerance returns the bloom filter which is tolerated for a limited -// time after new bloom was advertised to the peers. If sufficient time have elapsed -// or no change of bloom filter have ever occurred, the return value will be the same -// as return value of BloomFilter(). -func (whisper *Whisper) BloomFilterTolerance() []byte { - val, exist := whisper.settings.Load(bloomFilterToleranceIdx) - if !exist || val == nil { - return nil - } - return val.([]byte) -} - -// MaxMessageSize returns the maximum accepted message size. -func (whisper *Whisper) MaxMessageSize() uint32 { - val, _ := whisper.settings.Load(maxMsgSizeIdx) - return val.(uint32) -} - -// Overflow returns an indication if the message queue is full. -func (whisper *Whisper) Overflow() bool { - val, _ := whisper.settings.Load(overflowIdx) - return val.(bool) -} - -// APIs returns the RPC descriptors the Whisper implementation offers -func (whisper *Whisper) APIs() []rpc.API { - return []rpc.API{ - { - Namespace: ProtocolName, - Version: ProtocolVersionStr, - Service: NewPublicWhisperAPI(whisper), - Public: true, - }, - } -} - -// RegisterServer registers MailServer interface. -// MailServer will process all the incoming messages with p2pRequestCode. -func (whisper *Whisper) RegisterServer(server MailServer) { - whisper.mailServer = server -} - -// Protocols returns the whisper sub-protocols ran by this particular client. -func (whisper *Whisper) Protocols() []p2p.Protocol { - return []p2p.Protocol{whisper.protocol} -} - -// Version returns the whisper sub-protocols version number. -func (whisper *Whisper) Version() uint { - return whisper.protocol.Version -} - -// SetMaxMessageSize sets the maximal message size allowed by this node -func (whisper *Whisper) SetMaxMessageSize(size uint32) error { - if size > MaxMessageSize { - return fmt.Errorf("message size too large [%d>%d]", size, MaxMessageSize) - } - whisper.settings.Store(maxMsgSizeIdx, size) - return nil -} - -// SetBloomFilter sets the new bloom filter -func (whisper *Whisper) SetBloomFilter(bloom []byte) error { - if len(bloom) != BloomFilterSize { - return fmt.Errorf("invalid bloom filter size: %d", len(bloom)) - } - - b := make([]byte, BloomFilterSize) - copy(b, bloom) - - whisper.settings.Store(bloomFilterIdx, b) - whisper.notifyPeersAboutBloomFilterChange(b) - - go func() { - // allow some time before all the peers have processed the notification - time.Sleep(time.Duration(whisper.syncAllowance) * time.Second) - whisper.settings.Store(bloomFilterToleranceIdx, b) - }() - - return nil -} - -// SetMinimumPoW sets the minimal PoW required by this node -func (whisper *Whisper) SetMinimumPoW(val float64) error { - if val < 0.0 { - return fmt.Errorf("invalid PoW: %f", val) - } - - whisper.settings.Store(minPowIdx, val) - whisper.notifyPeersAboutPowRequirementChange(val) - - go func() { - // allow some time before all the peers have processed the notification - time.Sleep(time.Duration(whisper.syncAllowance) * time.Second) - whisper.settings.Store(minPowToleranceIdx, val) - }() - - return nil -} - -// SetMinimumPowTest sets the minimal PoW in test environment -func (whisper *Whisper) SetMinimumPowTest(val float64) { - whisper.settings.Store(minPowIdx, val) - whisper.notifyPeersAboutPowRequirementChange(val) - whisper.settings.Store(minPowToleranceIdx, val) -} - -func (whisper *Whisper) notifyPeersAboutPowRequirementChange(pow float64) { - arr := whisper.getPeers() - for _, p := range arr { - err := p.notifyAboutPowRequirementChange(pow) - if err != nil { - // allow one retry - err = p.notifyAboutPowRequirementChange(pow) - } - if err != nil { - log.Warn("failed to notify peer about new pow requirement", "peer", p.ID(), "error", err) - } - } -} - -func (whisper *Whisper) notifyPeersAboutBloomFilterChange(bloom []byte) { - arr := whisper.getPeers() - for _, p := range arr { - err := p.notifyAboutBloomFilterChange(bloom) - if err != nil { - // allow one retry - err = p.notifyAboutBloomFilterChange(bloom) - } - if err != nil { - log.Warn("failed to notify peer about new bloom filter", "peer", p.ID(), "error", err) - } - } -} - -func (whisper *Whisper) getPeers() []*Peer { - arr := make([]*Peer, len(whisper.peers)) - i := 0 - whisper.peerMu.Lock() - for p := range whisper.peers { - arr[i] = p - i++ - } - whisper.peerMu.Unlock() - return arr -} - -// getPeer retrieves peer by ID -func (whisper *Whisper) getPeer(peerID []byte) (*Peer, error) { - whisper.peerMu.Lock() - defer whisper.peerMu.Unlock() - for p := range whisper.peers { - id := p.peer.ID() - if bytes.Equal(peerID, id[:]) { - return p, nil - } - } - return nil, fmt.Errorf("Could not find peer with ID: %x", peerID) -} - -// AllowP2PMessagesFromPeer marks specific peer trusted, -// which will allow it to send historic (expired) messages. -func (whisper *Whisper) AllowP2PMessagesFromPeer(peerID []byte) error { - p, err := whisper.getPeer(peerID) - if err != nil { - return err - } - p.trusted = true - return nil -} - -// RequestHistoricMessages sends a message with p2pRequestCode to a specific peer, -// which is known to implement MailServer interface, and is supposed to process this -// request and respond with a number of peer-to-peer messages (possibly expired), -// which are not supposed to be forwarded any further. -// The whisper protocol is agnostic of the format and contents of envelope. -func (whisper *Whisper) RequestHistoricMessages(peerID []byte, envelope *Envelope) error { - p, err := whisper.getPeer(peerID) - if err != nil { - return err - } - p.trusted = true - return p2p.Send(p.ws, p2pRequestCode, envelope) -} - -// SendP2PMessage sends a peer-to-peer message to a specific peer. -func (whisper *Whisper) SendP2PMessage(peerID []byte, envelope *Envelope) error { - p, err := whisper.getPeer(peerID) - if err != nil { - return err - } - return whisper.SendP2PDirect(p, envelope) -} - -// SendP2PDirect sends a peer-to-peer message to a specific peer. -func (whisper *Whisper) SendP2PDirect(peer *Peer, envelope *Envelope) error { - return p2p.Send(peer.ws, p2pMessageCode, envelope) -} - -// NewKeyPair generates a new cryptographic identity for the client, and injects -// it into the known identities for message decryption. Returns ID of the new key pair. -func (whisper *Whisper) NewKeyPair() (string, error) { - key, err := crypto.GenerateKey() - if err != nil || !validatePrivateKey(key) { - key, err = crypto.GenerateKey() // retry once - } - if err != nil { - return "", err - } - if !validatePrivateKey(key) { - return "", fmt.Errorf("failed to generate valid key") - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - - if whisper.privateKeys[id] != nil { - return "", fmt.Errorf("failed to generate unique ID") - } - whisper.privateKeys[id] = key - return id, nil -} - -// DeleteKeyPair deletes the specified key if it exists. -func (whisper *Whisper) DeleteKeyPair(key string) bool { - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - - if whisper.privateKeys[key] != nil { - delete(whisper.privateKeys, key) - return true - } - return false -} - -// AddKeyPair imports a asymmetric private key and returns it identifier. -func (whisper *Whisper) AddKeyPair(key *ecdsa.PrivateKey) (string, error) { - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - whisper.keyMu.Lock() - whisper.privateKeys[id] = key - whisper.keyMu.Unlock() - - return id, nil -} - -// HasKeyPair checks if the the whisper node is configured with the private key -// of the specified public pair. -func (whisper *Whisper) HasKeyPair(id string) bool { - whisper.keyMu.RLock() - defer whisper.keyMu.RUnlock() - return whisper.privateKeys[id] != nil -} - -// GetPrivateKey retrieves the private key of the specified identity. -func (whisper *Whisper) GetPrivateKey(id string) (*ecdsa.PrivateKey, error) { - whisper.keyMu.RLock() - defer whisper.keyMu.RUnlock() - key := whisper.privateKeys[id] - if key == nil { - return nil, fmt.Errorf("invalid id") - } - return key, nil -} - -// 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 (whisper *Whisper) GenerateSymKey() (string, error) { - key, err := generateSecureRandomData(aesKeyLength) - if err != nil { - return "", err - } else if !validateDataIntegrity(key, aesKeyLength) { - return "", fmt.Errorf("error in GenerateSymKey: crypto/rand failed to generate random data") - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - - if whisper.symKeys[id] != nil { - return "", fmt.Errorf("failed to generate unique ID") - } - whisper.symKeys[id] = key - return id, nil -} - -// AddSymKeyDirect stores the key, and returns its id. -func (whisper *Whisper) AddSymKeyDirect(key []byte) (string, error) { - if len(key) != aesKeyLength { - return "", fmt.Errorf("wrong key size: %d", len(key)) - } - - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - - if whisper.symKeys[id] != nil { - return "", fmt.Errorf("failed to generate unique ID") - } - whisper.symKeys[id] = key - return id, nil -} - -// AddSymKeyFromPassword generates the key from password, stores it, and returns its id. -func (whisper *Whisper) AddSymKeyFromPassword(password string) (string, error) { - id, err := GenerateRandomID() - if err != nil { - return "", fmt.Errorf("failed to generate ID: %s", err) - } - if whisper.HasSymKey(id) { - return "", fmt.Errorf("failed to generate unique ID") - } - - // kdf should run no less than 0.1 seconds on an average computer, - // because it's an once in a session experience - derived := pbkdf2.Key([]byte(password), nil, 65356, aesKeyLength, sha256.New) - if err != nil { - return "", err - } - - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - - // double check is necessary, because deriveKeyMaterial() is very slow - if whisper.symKeys[id] != nil { - return "", fmt.Errorf("critical error: failed to generate unique ID") - } - whisper.symKeys[id] = derived - return id, nil -} - -// HasSymKey returns true if there is a key associated with the given id. -// Otherwise returns false. -func (whisper *Whisper) HasSymKey(id string) bool { - whisper.keyMu.RLock() - defer whisper.keyMu.RUnlock() - return whisper.symKeys[id] != nil -} - -// DeleteSymKey deletes the key associated with the name string if it exists. -func (whisper *Whisper) DeleteSymKey(id string) bool { - whisper.keyMu.Lock() - defer whisper.keyMu.Unlock() - if whisper.symKeys[id] != nil { - delete(whisper.symKeys, id) - return true - } - return false -} - -// GetSymKey returns the symmetric key associated with the given id. -func (whisper *Whisper) GetSymKey(id string) ([]byte, error) { - whisper.keyMu.RLock() - defer whisper.keyMu.RUnlock() - if whisper.symKeys[id] != nil { - return whisper.symKeys[id], nil - } - return nil, fmt.Errorf("non-existent key ID") -} - -// Subscribe installs a new message handler used for filtering, decrypting -// and subsequent storing of incoming messages. -func (whisper *Whisper) Subscribe(f *Filter) (string, error) { - s, err := whisper.filters.Install(f) - if err == nil { - whisper.updateBloomFilter(f) - } - return s, err -} - -// updateBloomFilter recalculates the new value of bloom filter, -// and informs the peers if necessary. -func (whisper *Whisper) updateBloomFilter(f *Filter) { - aggregate := make([]byte, BloomFilterSize) - for _, t := range f.Topics { - top := BytesToTopic(t) - b := TopicToBloom(top) - aggregate = addBloom(aggregate, b) - } - - if !BloomFilterMatch(whisper.BloomFilter(), aggregate) { - // existing bloom filter must be updated - aggregate = addBloom(whisper.BloomFilter(), aggregate) - whisper.SetBloomFilter(aggregate) - } -} - -// GetFilter returns the filter by id. -func (whisper *Whisper) GetFilter(id string) *Filter { - return whisper.filters.Get(id) -} - -// Unsubscribe removes an installed message handler. -func (whisper *Whisper) Unsubscribe(id string) error { - ok := whisper.filters.Uninstall(id) - if !ok { - return fmt.Errorf("Unsubscribe: Invalid ID") - } - return nil -} - -// Send injects a message into the whisper send queue, to be distributed in the -// network in the coming cycles. -func (whisper *Whisper) Send(envelope *Envelope) error { - ok, err := whisper.add(envelope, false) - if err == nil && !ok { - return fmt.Errorf("failed to add envelope") - } - return err -} - -// Start implements node.Service, starting the background data propagation thread -// of the Whisper protocol. -func (whisper *Whisper) Start(*p2p.Server) error { - log.Info("started whisper v." + ProtocolVersionStr) - go whisper.update() - - numCPU := runtime.NumCPU() - for i := 0; i < numCPU; i++ { - go whisper.processQueue() - } - - return nil -} - -// Stop implements node.Service, stopping the background data propagation thread -// of the Whisper protocol. -func (whisper *Whisper) Stop() error { - close(whisper.quit) - log.Info("whisper stopped") - return nil -} - -// HandlePeer is called by the underlying P2P layer when the whisper sub-protocol -// connection is negotiated. -func (whisper *Whisper) HandlePeer(peer *p2p.Peer, rw p2p.MsgReadWriter) error { - // Create the new peer and start tracking it - whisperPeer := newPeer(whisper, peer, rw) - - whisper.peerMu.Lock() - whisper.peers[whisperPeer] = struct{}{} - whisper.peerMu.Unlock() - - defer func() { - whisper.peerMu.Lock() - delete(whisper.peers, whisperPeer) - whisper.peerMu.Unlock() - }() - - // Run the peer handshake and state updates - if err := whisperPeer.handshake(); err != nil { - return err - } - whisperPeer.start() - defer whisperPeer.stop() - - return whisper.runMessageLoop(whisperPeer, rw) -} - -// runMessageLoop reads and processes inbound messages directly to merge into client-global state. -func (whisper *Whisper) runMessageLoop(p *Peer, rw p2p.MsgReadWriter) error { - for { - // fetch the next packet - packet, err := rw.ReadMsg() - if err != nil { - log.Info("message loop", "peer", p.peer.ID(), "err", err) - return err - } - if packet.Size > whisper.MaxMessageSize() { - log.Warn("oversized message received", "peer", p.peer.ID()) - return errors.New("oversized message received") - } - - switch packet.Code { - case statusCode: - // this should not happen, but no need to panic; just ignore this message. - 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 { - log.Warn("failed to decode envelopes, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid envelopes") - } - - trouble := false - for _, env := range envelopes { - cached, err := whisper.add(env, whisper.lightClient) - if err != nil { - trouble = true - log.Error("bad envelope received, peer will be disconnected", "peer", p.peer.ID(), "err", err) - } - if cached { - p.mark(env) - } - } - - if trouble { - return errors.New("invalid envelope") - } - case powRequirementCode: - s := rlp.NewStream(packet.Payload, uint64(packet.Size)) - i, err := s.Uint() - if err != nil { - log.Warn("failed to decode powRequirementCode message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid powRequirementCode message") - } - f := math.Float64frombits(i) - if math.IsInf(f, 0) || math.IsNaN(f) || f < 0.0 { - log.Warn("invalid value in powRequirementCode message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid value in powRequirementCode message") - } - p.powRequirement = f - case bloomFilterExCode: - var bloom []byte - err := packet.Decode(&bloom) - if err == nil && len(bloom) != BloomFilterSize { - err = fmt.Errorf("wrong bloom filter size %d", len(bloom)) - } - - if err != nil { - log.Warn("failed to decode bloom filter exchange message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid bloom filter exchange message") - } - p.setBloomFilter(bloom) - case p2pMessageCode: - // peer-to-peer message, sent directly to peer bypassing PoW checks, etc. - // this message is not supposed to be forwarded to other peers, and - // therefore might not satisfy the PoW, expiry and other requirements. - // these messages are only accepted from the trusted peer. - if p.trusted { - var envelope Envelope - if err := packet.Decode(&envelope); err != nil { - log.Warn("failed to decode direct message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid direct message") - } - whisper.postEvent(&envelope, true) - } - case p2pRequestCode: - // Must be processed if mail server is implemented. Otherwise ignore. - if whisper.mailServer != nil { - var request Envelope - if err := packet.Decode(&request); err != nil { - log.Warn("failed to decode p2p request message, peer will be disconnected", "peer", p.peer.ID(), "err", err) - return errors.New("invalid p2p request") - } - whisper.mailServer.DeliverMail(p, &request) - } - default: - // New message types might be implemented in the future versions of Whisper. - // For forward compatibility, just ignore. - } - - packet.Discard() - } -} - -// add inserts a new envelope into the message pool to be distributed within the -// whisper network. It also inserts the envelope into the expiration pool at the -// appropriate time-stamp. In case of error, connection should be dropped. -// param isP2P indicates whether the message is peer-to-peer (should not be forwarded). -func (whisper *Whisper) add(envelope *Envelope, isP2P bool) (bool, error) { - now := uint32(time.Now().Unix()) - sent := envelope.Expiry - envelope.TTL - - if sent > now { - if sent-DefaultSyncAllowance > now { - return false, fmt.Errorf("envelope created in the future [%x]", envelope.Hash()) - } - // recalculate PoW, adjusted for the time difference, plus one second for latency - envelope.calculatePoW(sent - now + 1) - } - - if envelope.Expiry < now { - if envelope.Expiry+DefaultSyncAllowance*2 < now { - return false, fmt.Errorf("very old message") - } - log.Debug("expired envelope dropped", "hash", envelope.Hash().Hex()) - return false, nil // drop envelope without error - } - - if uint32(envelope.size()) > whisper.MaxMessageSize() { - return false, fmt.Errorf("huge messages are not allowed [%x]", envelope.Hash()) - } - - if envelope.PoW() < whisper.MinPow() { - // maybe the value was recently changed, and the peers did not adjust yet. - // in this case the previous value is retrieved by MinPowTolerance() - // for a short period of peer synchronization. - if envelope.PoW() < whisper.MinPowTolerance() { - return false, fmt.Errorf("envelope with low PoW received: PoW=%f, hash=[%v]", envelope.PoW(), envelope.Hash().Hex()) - } - } - - if !BloomFilterMatch(whisper.BloomFilter(), envelope.Bloom()) { - // maybe the value was recently changed, and the peers did not adjust yet. - // in this case the previous value is retrieved by BloomFilterTolerance() - // for a short period of peer synchronization. - if !BloomFilterMatch(whisper.BloomFilterTolerance(), envelope.Bloom()) { - return false, fmt.Errorf("envelope does not match bloom filter, hash=[%v], bloom: \n%x \n%x \n%x", - envelope.Hash().Hex(), whisper.BloomFilter(), envelope.Bloom(), envelope.Topic) - } - } - - hash := envelope.Hash() - - whisper.poolMu.Lock() - _, alreadyCached := whisper.envelopes[hash] - if !alreadyCached { - whisper.envelopes[hash] = envelope - if whisper.expirations[envelope.Expiry] == nil { - whisper.expirations[envelope.Expiry] = set.NewNonTS() - } - if !whisper.expirations[envelope.Expiry].Has(hash) { - whisper.expirations[envelope.Expiry].Add(hash) - } - } - whisper.poolMu.Unlock() - - if alreadyCached { - log.Trace("whisper envelope already cached", "hash", envelope.Hash().Hex()) - } else { - log.Trace("cached whisper envelope", "hash", envelope.Hash().Hex()) - whisper.statsMu.Lock() - whisper.stats.memoryUsed += envelope.size() - whisper.statsMu.Unlock() - whisper.postEvent(envelope, isP2P) // notify the local node about the new message - if whisper.mailServer != nil { - whisper.mailServer.Archive(envelope) - } - } - return true, nil -} - -// postEvent queues the message for further processing. -func (whisper *Whisper) postEvent(envelope *Envelope, isP2P bool) { - if isP2P { - whisper.p2pMsgQueue <- envelope - } else { - whisper.checkOverflow() - whisper.messageQueue <- envelope - } -} - -// checkOverflow checks if message queue overflow occurs and reports it if necessary. -func (whisper *Whisper) checkOverflow() { - queueSize := len(whisper.messageQueue) - - if queueSize == messageQueueLimit { - if !whisper.Overflow() { - whisper.settings.Store(overflowIdx, true) - log.Warn("message queue overflow") - } - } else if queueSize <= messageQueueLimit/2 { - if whisper.Overflow() { - whisper.settings.Store(overflowIdx, false) - log.Warn("message queue overflow fixed (back to normal)") - } - } -} - -// processQueue delivers the messages to the watchers during the lifetime of the whisper node. -func (whisper *Whisper) processQueue() { - var e *Envelope - for { - select { - case <-whisper.quit: - return - - case e = <-whisper.messageQueue: - whisper.filters.NotifyWatchers(e, false) - - case e = <-whisper.p2pMsgQueue: - whisper.filters.NotifyWatchers(e, true) - } - } -} - -// update loops until the lifetime of the whisper node, updating its internal -// state by expiring stale messages from the pool. -func (whisper *Whisper) update() { - // Start a ticker to check for expirations - expire := time.NewTicker(expirationCycle) - - // Repeat updates until termination is requested - for { - select { - case <-expire.C: - whisper.expire() - - case <-whisper.quit: - return - } - } -} - -// expire iterates over all the expiration timestamps, removing all stale -// messages from the pools. -func (whisper *Whisper) expire() { - whisper.poolMu.Lock() - defer whisper.poolMu.Unlock() - - whisper.statsMu.Lock() - defer whisper.statsMu.Unlock() - whisper.stats.reset() - now := uint32(time.Now().Unix()) - for expiry, hashSet := range whisper.expirations { - if expiry < now { - // Dump all expired messages and remove timestamp - hashSet.Each(func(v interface{}) bool { - sz := whisper.envelopes[v.(common.Hash)].size() - delete(whisper.envelopes, v.(common.Hash)) - whisper.stats.messagesCleared++ - whisper.stats.memoryCleared += sz - whisper.stats.memoryUsed -= sz - return true - }) - whisper.expirations[expiry].Clear() - delete(whisper.expirations, expiry) - } - } -} - -// Stats returns the whisper node statistics. -func (whisper *Whisper) Stats() Statistics { - whisper.statsMu.Lock() - defer whisper.statsMu.Unlock() - - return whisper.stats -} - -// Envelopes retrieves all the messages currently pooled by the node. -func (whisper *Whisper) Envelopes() []*Envelope { - whisper.poolMu.RLock() - defer whisper.poolMu.RUnlock() - - all := make([]*Envelope, 0, len(whisper.envelopes)) - for _, envelope := range whisper.envelopes { - all = append(all, envelope) - } - return all -} - -// isEnvelopeCached checks if envelope with specific hash has already been received and cached. -func (whisper *Whisper) isEnvelopeCached(hash common.Hash) bool { - whisper.poolMu.Lock() - defer whisper.poolMu.Unlock() - - _, exist := whisper.envelopes[hash] - return exist -} - -// reset resets the node's statistics after each expiry cycle. -func (s *Statistics) reset() { - s.cycles++ - s.totalMessagesCleared += s.messagesCleared - - s.memoryCleared = 0 - s.messagesCleared = 0 -} - -// ValidatePublicKey checks the format of the given public key. -func ValidatePublicKey(k *ecdsa.PublicKey) bool { - return k != nil && k.X != nil && k.Y != nil && k.X.Sign() != 0 && k.Y.Sign() != 0 -} - -// validatePrivateKey checks the format of the given private key. -func validatePrivateKey(k *ecdsa.PrivateKey) bool { - if k == nil || k.D == nil || k.D.Sign() == 0 { - return false - } - return ValidatePublicKey(&k.PublicKey) -} - -// validateDataIntegrity returns false if the data have the wrong or contains all zeros, -// which is the simplest and the most common bug. -func validateDataIntegrity(k []byte, expectedSize int) bool { - if len(k) != expectedSize { - return false - } - if expectedSize > 3 && containsOnlyZeros(k) { - return false - } - return true -} - -// containsOnlyZeros checks if the data contain only zeros. -func containsOnlyZeros(data []byte) bool { - for _, b := range data { - if b != 0 { - return false - } - } - return true -} - -// bytesToUintLittleEndian converts the slice to 64-bit unsigned integer. -func bytesToUintLittleEndian(b []byte) (res uint64) { - mul := uint64(1) - for i := 0; i < len(b); i++ { - res += uint64(b[i]) * mul - mul *= 256 - } - return res -} - -// BytesToUintBigEndian converts the slice to 64-bit unsigned integer. -func BytesToUintBigEndian(b []byte) (res uint64) { - for i := 0; i < len(b); i++ { - res *= 256 - res += uint64(b[i]) - } - return res -} - -// GenerateRandomID generates a random string, which is then returned to be used as a key id -func GenerateRandomID() (id string, err error) { - buf, err := generateSecureRandomData(keyIDSize) - if err != nil { - return "", err - } - if !validateDataIntegrity(buf, keyIDSize) { - return "", fmt.Errorf("error in generateRandomID: crypto/rand failed to generate random data") - } - id = common.Bytes2Hex(buf) - return id, err -} - -func isFullNode(bloom []byte) bool { - if bloom == nil { - return true - } - for _, b := range bloom { - if b != 255 { - return false - } - } - return true -} - -func BloomFilterMatch(filter, sample []byte) bool { - if filter == nil { - return true - } - - for i := 0; i < BloomFilterSize; i++ { - f := filter[i] - s := sample[i] - if (f | s) != f { - return false - } - } - - return true -} - -func addBloom(a, b []byte) []byte { - c := make([]byte, BloomFilterSize) - for i := 0; i < BloomFilterSize; i++ { - c[i] = a[i] | b[i] - } - return c -} diff --git a/whisper/whisperv6/whisper_test.go b/whisper/whisperv6/whisper_test.go deleted file mode 100644 index 7fe256309e..0000000000 --- a/whisper/whisperv6/whisper_test.go +++ /dev/null @@ -1,885 +0,0 @@ -// Copyright 2016 The go-ethereum Authors -// This file is part of the go-ethereum library. -// -// The go-ethereum library is free software: you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// The go-ethereum library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public License -// along with the go-ethereum library. If not, see . - -package whisperv6 - -import ( - "bytes" - "crypto/ecdsa" - "crypto/sha256" - mrand "math/rand" - "testing" - "time" - - "github.com/ethereum/go-ethereum/common" - "golang.org/x/crypto/pbkdf2" -) - -func TestWhisperBasic(t *testing.T) { - w := New(&DefaultConfig) - p := w.Protocols() - shh := p[0] - if shh.Name != ProtocolName { - t.Fatalf("failed Protocol Name: %v.", shh.Name) - } - if uint64(shh.Version) != ProtocolVersion { - t.Fatalf("failed Protocol Version: %v.", shh.Version) - } - if shh.Length != NumberOfMessageCodes { - t.Fatalf("failed Protocol Length: %v.", shh.Length) - } - if shh.Run == nil { - t.Fatalf("failed shh.Run.") - } - if uint64(w.Version()) != ProtocolVersion { - t.Fatalf("failed whisper Version: %v.", shh.Version) - } - if w.GetFilter("non-existent") != nil { - t.Fatalf("failed GetFilter.") - } - - peerID := make([]byte, 64) - mrand.Read(peerID) - peer, _ := w.getPeer(peerID) - if peer != nil { - t.Fatal("found peer for random key.") - } - if err := w.AllowP2PMessagesFromPeer(peerID); err == nil { - t.Fatalf("failed MarkPeerTrusted.") - } - exist := w.HasSymKey("non-existing") - if exist { - t.Fatalf("failed HasSymKey.") - } - key, err := w.GetSymKey("non-existing") - if err == nil { - t.Fatalf("failed GetSymKey(non-existing): false positive.") - } - if key != nil { - t.Fatalf("failed GetSymKey: false positive.") - } - mail := w.Envelopes() - if len(mail) != 0 { - t.Fatalf("failed w.Envelopes().") - } - - derived := pbkdf2.Key([]byte(peerID), nil, 65356, aesKeyLength, sha256.New) - if !validateDataIntegrity(derived, aesKeyLength) { - t.Fatalf("failed validateSymmetricKey with param = %v.", derived) - } - if containsOnlyZeros(derived) { - t.Fatalf("failed containsOnlyZeros with param = %v.", derived) - } - - buf := []byte{0xFF, 0xE5, 0x80, 0x2, 0} - le := bytesToUintLittleEndian(buf) - be := BytesToUintBigEndian(buf) - if le != uint64(0x280e5ff) { - t.Fatalf("failed bytesToIntLittleEndian: %d.", le) - } - if be != uint64(0xffe5800200) { - t.Fatalf("failed BytesToIntBigEndian: %d.", be) - } - - id, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - pk, err := w.GetPrivateKey(id) - if err != nil { - t.Fatalf("failed to retrieve new key pair: %s.", err) - } - if !validatePrivateKey(pk) { - t.Fatalf("failed validatePrivateKey: %v.", pk) - } - if !ValidatePublicKey(&pk.PublicKey) { - t.Fatalf("failed ValidatePublicKey: %v.", pk) - } -} - -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) { - w := New(&DefaultConfig) - id1, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - id2, err := w.NewKeyPair() - if err != nil { - t.Fatalf("failed to generate new key pair: %s.", err) - } - pk1, err := w.GetPrivateKey(id1) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - pk2, err := w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - - if !w.HasKeyPair(id1) { - t.Fatalf("failed HasIdentity(pk1).") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed HasIdentity(pk2).") - } - if pk1 == nil { - t.Fatalf("failed GetIdentity(pk1).") - } - if pk2 == nil { - t.Fatalf("failed GetIdentity(pk2).") - } - - if !validatePrivateKey(pk1) { - t.Fatalf("pk1 is invalid.") - } - if !validatePrivateKey(pk2) { - t.Fatalf("pk2 is invalid.") - } - - // Delete one identity - done := w.DeleteKeyPair(id1) - if !done { - t.Fatalf("failed to delete id1.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - if w.HasKeyPair(id1) { - t.Fatalf("failed DeleteIdentity(pub1): still exist.") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed DeleteIdentity(pub1): pub2 does not exist.") - } - if pk1 != nil { - t.Fatalf("failed DeleteIdentity(pub1): first key still exist.") - } - if pk2 == nil { - t.Fatalf("failed DeleteIdentity(pub1): second key does not exist.") - } - - // Delete again non-existing identity - done = w.DeleteKeyPair(id1) - if done { - t.Fatalf("delete id1: false positive.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err != nil { - t.Fatalf("failed to retrieve the key pair: %s.", err) - } - if w.HasKeyPair(id1) { - t.Fatalf("failed delete non-existing identity: exist.") - } - if !w.HasKeyPair(id2) { - t.Fatalf("failed delete non-existing identity: pub2 does not exist.") - } - if pk1 != nil { - t.Fatalf("failed delete non-existing identity: first key exist.") - } - if pk2 == nil { - t.Fatalf("failed delete non-existing identity: second key does not exist.") - } - - // Delete second identity - done = w.DeleteKeyPair(id2) - if !done { - t.Fatalf("failed to delete id2.") - } - pk1, err = w.GetPrivateKey(id1) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - pk2, err = w.GetPrivateKey(id2) - if err == nil { - t.Fatalf("retrieve the key pair: false positive.") - } - if w.HasKeyPair(id1) { - t.Fatalf("failed delete second identity: first identity exist.") - } - if w.HasKeyPair(id2) { - t.Fatalf("failed delete second identity: still exist.") - } - if pk1 != nil { - t.Fatalf("failed delete second identity: first key exist.") - } - if pk2 != nil { - t.Fatalf("failed delete second identity: second key exist.") - } -} - -func TestWhisperSymKeyManagement(t *testing.T) { - InitSingleTest() - - var err error - var k1, k2 []byte - w := New(&DefaultConfig) - id1 := string("arbitrary-string-1") - id2 := string("arbitrary-string-2") - - id1, err = w.GenerateSymKey() - if err != nil { - t.Fatalf("failed GenerateSymKey with seed %d: %s.", seed, err) - } - - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed GetSymKey(id2): false positive.") - } - if !w.HasSymKey(id1) { - t.Fatalf("failed HasSymKey(id1).") - } - if w.HasSymKey(id2) { - t.Fatalf("failed HasSymKey(id2): false positive.") - } - if k1 == nil { - t.Fatalf("first key does not exist.") - } - if k2 != nil { - t.Fatalf("second key still exist.") - } - - // add existing id, nothing should change - randomKey := make([]byte, aesKeyLength) - mrand.Read(randomKey) - id1, err = w.AddSymKeyDirect(randomKey) - if err != nil { - t.Fatalf("failed AddSymKey with seed %d: %s.", seed, err) - } - - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed w.GetSymKey(id2): false positive.") - } - if !w.HasSymKey(id1) { - t.Fatalf("failed w.HasSymKey(id1).") - } - if w.HasSymKey(id2) { - t.Fatalf("failed w.HasSymKey(id2): false positive.") - } - if k1 == nil { - t.Fatalf("first key does not exist.") - } - if !bytes.Equal(k1, randomKey) { - t.Fatalf("k1 != randomKey.") - } - if k2 != nil { - t.Fatalf("second key already exist.") - } - - id2, err = w.AddSymKeyDirect(randomKey) - if err != nil { - t.Fatalf("failed AddSymKey(id2) with seed %d: %s.", seed, err) - } - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if !w.HasSymKey(id1) { - t.Fatalf("HasSymKey(id1) failed.") - } - if !w.HasSymKey(id2) { - t.Fatalf("HasSymKey(id2) failed.") - } - if k1 == nil { - t.Fatalf("k1 does not exist.") - } - if k2 == nil { - t.Fatalf("k2 does not exist.") - } - if !bytes.Equal(k1, k2) { - t.Fatalf("k1 != k2.") - } - if !bytes.Equal(k1, randomKey) { - t.Fatalf("k1 != randomKey.") - } - if len(k1) != aesKeyLength { - t.Fatalf("wrong length of k1.") - } - if len(k2) != aesKeyLength { - t.Fatalf("wrong length of k2.") - } - - w.DeleteSymKey(id1) - k1, err = w.GetSymKey(id1) - if err == nil { - t.Fatalf("failed w.GetSymKey(id1): false positive.") - } - if k1 != nil { - t.Fatalf("failed GetSymKey(id1): false positive.") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if w.HasSymKey(id1) { - t.Fatalf("failed to delete first key: still exist.") - } - if !w.HasSymKey(id2) { - t.Fatalf("failed to delete first key: second key does not exist.") - } - if k1 != nil { - t.Fatalf("failed to delete first key.") - } - if k2 == nil { - t.Fatalf("failed to delete first key: second key is nil.") - } - - w.DeleteSymKey(id1) - w.DeleteSymKey(id2) - k1, err = w.GetSymKey(id1) - if err == nil { - t.Fatalf("failed w.GetSymKey(id1): false positive.") - } - k2, err = w.GetSymKey(id2) - if err == nil { - t.Fatalf("failed w.GetSymKey(id2): false positive.") - } - if k1 != nil || k2 != nil { - t.Fatalf("k1 or k2 is not nil") - } - if w.HasSymKey(id1) { - t.Fatalf("failed to delete second key: first key exist.") - } - if w.HasSymKey(id2) { - t.Fatalf("failed to delete second key: still exist.") - } - if k1 != nil { - t.Fatalf("failed to delete second key: first key is not nil.") - } - if k2 != nil { - t.Fatalf("failed to delete second key: second key is not nil.") - } - - randomKey = make([]byte, aesKeyLength+1) - mrand.Read(randomKey) - _, err = w.AddSymKeyDirect(randomKey) - if err == nil { - t.Fatalf("added the key with wrong size, seed %d.", seed) - } - - const password = "arbitrary data here" - id1, err = w.AddSymKeyFromPassword(password) - if err != nil { - t.Fatalf("failed AddSymKeyFromPassword(id1) with seed %d: %s.", seed, err) - } - id2, err = w.AddSymKeyFromPassword(password) - if err != nil { - t.Fatalf("failed AddSymKeyFromPassword(id2) with seed %d: %s.", seed, err) - } - k1, err = w.GetSymKey(id1) - if err != nil { - t.Fatalf("failed w.GetSymKey(id1).") - } - k2, err = w.GetSymKey(id2) - if err != nil { - t.Fatalf("failed w.GetSymKey(id2).") - } - if !w.HasSymKey(id1) { - t.Fatalf("HasSymKey(id1) failed.") - } - if !w.HasSymKey(id2) { - t.Fatalf("HasSymKey(id2) failed.") - } - if !validateDataIntegrity(k2, aesKeyLength) { - t.Fatalf("key validation failed.") - } - if !bytes.Equal(k1, k2) { - t.Fatalf("k1 != k2.") - } -} - -func TestExpiry(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - w.SetMinimumPowTest(0.0000001) - defer w.SetMinimumPowTest(DefaultMinimumPoW) - w.Start(nil) - defer w.Stop() - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.TTL = 1 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send envelope with seed %d: %s.", seed, err) - } - - // wait till received or timeout - var received, expired bool - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // wait till expired or timeout - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) == 0 { - expired = true - break - } - } - - if !expired { - t.Fatalf("expire failed, seed: %d.", seed) - } -} - -func TestCustomization(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPowTest(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - const smallPoW = 0.00001 - - f, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.KeySym = f.KeySym - params.Topic = BytesToTopic(f.Topics[2]) - params.PoW = smallPoW - params.TTL = 3600 * 24 // one day - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err == nil { - t.Fatalf("successfully sent envelope with PoW %.06f, false positive (seed %d).", env.PoW(), seed) - } - - w.SetMinimumPowTest(smallPoW / 2) - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send envelope with seed %d: %s.", seed, err) - } - - params.TTL++ - msg, err = NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err = msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - w.SetMaxMessageSize(uint32(env.size() - 1)) - err = w.Send(env) - if err == nil { - t.Fatalf("successfully sent oversized envelope (seed %d): false positive.", seed) - } - - w.SetMaxMessageSize(DefaultMaxMessageSize) - err = w.Send(env) - if err != nil { - t.Fatalf("failed to send second envelope with seed %d: %s.", seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 20; j++ { - time.Sleep(100 * time.Millisecond) - if len(w.Envelopes()) > 1 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - _, err = w.Subscribe(f) - if err != nil { - t.Fatalf("failed subscribe with seed %d: %s.", seed, err) - } - time.Sleep(5 * time.Millisecond) - mail := f.Retrieve() - if len(mail) > 0 { - t.Fatalf("received premature mail") - } -} - -func TestSymmetricSendCycle(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPowTest(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter1, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter1.PoW = DefaultMinimumPoW - - // Copy the first filter since some of its fields - // are randomly gnerated. - filter2 := &Filter{ - KeySym: filter1.KeySym, - Topics: filter1.Topics, - PoW: filter1.PoW, - AllowP2P: filter1.AllowP2P, - Messages: make(map[common.Hash]*ReceivedMessage), - } - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - filter1.Src = ¶ms.Src.PublicKey - filter2.Src = ¶ms.Src.PublicKey - - params.KeySym = filter1.KeySym - params.Topic = BytesToTopic(filter1.Topics[2]) - params.PoW = filter1.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter1) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter2) - if err != nil { - t.Fatalf("failed subscribe 2 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail1 := filter1.Retrieve() - mail2 := filter2.Retrieve() - if len(mail2) == 0 { - t.Fatalf("did not receive any email for filter 2") - } - if len(mail1) == 0 { - t.Fatalf("did not receive any email for filter 1") - } - -} - -func TestSymmetricSendWithoutAKey(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPowTest(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter.PoW = DefaultMinimumPoW - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - filter.Src = nil - - params.KeySym = filter.KeySym - params.Topic = BytesToTopic(filter.Topics[2]) - params.PoW = filter.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail := filter.Retrieve() - if len(mail) == 0 { - t.Fatalf("did not receive message in spite of not setting a public key") - } -} - -func TestSymmetricSendKeyMismatch(t *testing.T) { - InitSingleTest() - - w := New(&DefaultConfig) - defer w.SetMinimumPowTest(DefaultMinimumPoW) - defer w.SetMaxMessageSize(DefaultMaxMessageSize) - w.Start(nil) - defer w.Stop() - - filter, err := generateFilter(t, true) - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - filter.PoW = DefaultMinimumPoW - - params, err := generateMessageParams() - if err != nil { - t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err) - } - - params.KeySym = filter.KeySym - params.Topic = BytesToTopic(filter.Topics[2]) - params.PoW = filter.PoW - params.WorkTime = 10 - params.TTL = 50 - msg, err := NewSentMessage(params) - if err != nil { - t.Fatalf("failed to create new message with seed %d: %s.", seed, err) - } - env, err := msg.Wrap(params) - if err != nil { - t.Fatalf("failed Wrap with seed %d: %s.", seed, err) - } - - _, err = w.Subscribe(filter) - if err != nil { - t.Fatalf("failed subscribe 1 with seed %d: %s.", seed, err) - } - - err = w.Send(env) - if err != nil { - t.Fatalf("Failed sending envelope with PoW %.06f (seed %d): %s", env.PoW(), seed, err) - } - - // wait till received or timeout - var received bool - for j := 0; j < 200; j++ { - time.Sleep(10 * time.Millisecond) - if len(w.Envelopes()) > 0 { - received = true - break - } - } - - if !received { - t.Fatalf("did not receive the sent envelope, seed: %d.", seed) - } - - // check w.messages() - time.Sleep(5 * time.Millisecond) - mail := filter.Retrieve() - if len(mail) > 0 { - t.Fatalf("received a message when keys weren't matching") - } -} - -func TestBloom(t *testing.T) { - topic := TopicType{0, 0, 255, 6} - b := TopicToBloom(topic) - x := make([]byte, BloomFilterSize) - x[0] = byte(1) - x[32] = byte(1) - x[BloomFilterSize-1] = byte(128) - if !BloomFilterMatch(x, b) || !BloomFilterMatch(b, x) { - t.Fatalf("bloom filter does not match the mask") - } - - _, err := mrand.Read(b) - if err != nil { - t.Fatalf("math rand error") - } - _, err = mrand.Read(x) - if err != nil { - t.Fatalf("math rand error") - } - if !BloomFilterMatch(b, b) { - t.Fatalf("bloom filter does not match self") - } - x = addBloom(x, b) - if !BloomFilterMatch(x, b) { - t.Fatalf("bloom filter does not match combined bloom") - } - if !isFullNode(nil) { - t.Fatalf("isFullNode did not recognize nil as full node") - } - x[17] = 254 - if isFullNode(x) { - t.Fatalf("isFullNode false positive") - } - for i := 0; i < BloomFilterSize; i++ { - b[i] = byte(255) - } - if !isFullNode(b) { - t.Fatalf("isFullNode false negative") - } - if BloomFilterMatch(x, b) { - t.Fatalf("bloomFilterMatch false positive") - } - if !BloomFilterMatch(b, x) { - t.Fatalf("bloomFilterMatch false negative") - } - - w := New(&DefaultConfig) - f := w.BloomFilter() - if f != nil { - t.Fatalf("wrong bloom on creation") - } - err = w.SetBloomFilter(x) - if err != nil { - t.Fatalf("failed to set bloom filter: %s", err) - } - f = w.BloomFilter() - if !BloomFilterMatch(f, x) || !BloomFilterMatch(x, f) { - t.Fatalf("retireved wrong bloom filter") - } -}