aboutsummaryrefslogtreecommitdiffstats
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/bootnode/main.go7
-rw-r--r--cmd/ethkey/inspect.go16
-rw-r--r--cmd/ethkey/message_test.go2
-rw-r--r--cmd/ethkey/run_test.go2
-rw-r--r--cmd/evm/json_logger.go14
-rw-r--r--cmd/evm/main.go5
-rw-r--r--cmd/evm/runner.go9
-rw-r--r--cmd/faucet/faucet.go28
-rw-r--r--cmd/geth/chaincmd.go20
-rw-r--r--cmd/geth/config.go17
-rw-r--r--cmd/geth/consolecmd.go9
-rw-r--r--cmd/geth/main.go5
-rw-r--r--cmd/geth/usage.go7
-rw-r--r--cmd/p2psim/main.go16
-rw-r--r--cmd/puppeth/genesis.go28
-rw-r--r--cmd/puppeth/module_dashboard.go79
-rw-r--r--cmd/puppeth/module_faucet.go8
-rw-r--r--cmd/puppeth/module_node.go54
-rw-r--r--cmd/puppeth/module_wallet.go2
-rw-r--r--cmd/puppeth/wizard.go3
-rw-r--r--cmd/puppeth/wizard_explorer.go2
-rw-r--r--cmd/puppeth/wizard_faucet.go8
-rw-r--r--cmd/puppeth/wizard_intro.go7
-rw-r--r--cmd/puppeth/wizard_netstats.go14
-rw-r--r--cmd/puppeth/wizard_node.go10
-rw-r--r--cmd/puppeth/wizard_wallet.go2
-rw-r--r--cmd/swarm/config.go58
-rw-r--r--cmd/swarm/config_test.go95
-rw-r--r--cmd/swarm/main.go82
-rw-r--r--cmd/swarm/manifest.go6
-rw-r--r--cmd/swarm/run_test.go2
-rw-r--r--cmd/swarm/upload_test.go2
-rw-r--r--cmd/utils/cmd.go31
-rw-r--r--cmd/utils/flags.go100
-rw-r--r--cmd/wnode/main.go258
35 files changed, 659 insertions, 349 deletions
diff --git a/cmd/bootnode/main.go b/cmd/bootnode/main.go
index ecfc6fc24..2e93cc04d 100644
--- a/cmd/bootnode/main.go
+++ b/cmd/bootnode/main.go
@@ -122,7 +122,12 @@ func main() {
utils.Fatalf("%v", err)
}
} else {
- if _, err := discover.ListenUDP(nodeKey, conn, realaddr, nil, "", restrictList); err != nil {
+ cfg := discover.Config{
+ PrivateKey: nodeKey,
+ AnnounceAddr: realaddr,
+ NetRestrict: restrictList,
+ }
+ if _, err := discover.ListenUDP(conn, cfg); err != nil {
utils.Fatalf("%v", err)
}
}
diff --git a/cmd/ethkey/inspect.go b/cmd/ethkey/inspect.go
index 219a5460b..dbf5afc0c 100644
--- a/cmd/ethkey/inspect.go
+++ b/cmd/ethkey/inspect.go
@@ -1,3 +1,19 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
package main
import (
diff --git a/cmd/ethkey/message_test.go b/cmd/ethkey/message_test.go
index fb16f03d0..39352b1d2 100644
--- a/cmd/ethkey/message_test.go
+++ b/cmd/ethkey/message_test.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
diff --git a/cmd/ethkey/run_test.go b/cmd/ethkey/run_test.go
index 8ce4fe5cd..6006f6b5b 100644
--- a/cmd/ethkey/run_test.go
+++ b/cmd/ethkey/run_test.go
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// Copyright 2018 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
diff --git a/cmd/evm/json_logger.go b/cmd/evm/json_logger.go
index 47daf7dbb..0e7a91189 100644
--- a/cmd/evm/json_logger.go
+++ b/cmd/evm/json_logger.go
@@ -1,18 +1,18 @@
// Copyright 2017 The go-ethereum Authors
-// This file is part of the go-ethereum library.
+// This file is part of go-ethereum.
//
-// 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
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU 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,
+// go-ethereum 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.
+// GNU General Public License for more details.
//
-// You should have received a copy of the GNU Lesser General Public License
-// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package main
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index 6c39cf8b8..a59cb1fb8 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -86,10 +86,6 @@ var (
Name: "create",
Usage: "indicates the action should be create rather than call",
}
- DisableGasMeteringFlag = cli.BoolFlag{
- Name: "nogasmetering",
- Usage: "disable gas metering",
- }
GenesisFlag = cli.StringFlag{
Name: "prestate",
Usage: "JSON file with prestate (genesis) config",
@@ -128,7 +124,6 @@ func init() {
ValueFlag,
DumpFlag,
InputFlag,
- DisableGasMeteringFlag,
MemProfileFlag,
CPUProfileFlag,
StatDumpFlag,
diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go
index 96de0c76a..8a7399840 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -96,7 +96,9 @@ func runCmd(ctx *cli.Context) error {
}
if ctx.GlobalString(GenesisFlag.Name) != "" {
gen := readGenesis(ctx.GlobalString(GenesisFlag.Name))
- _, statedb = gen.ToBlock()
+ db, _ := ethdb.NewMemDatabase()
+ genesis := gen.ToBlock(db)
+ statedb, _ = state.New(genesis.Root(), state.NewDatabase(db))
chainConfig = gen.Config
} else {
db, _ := ethdb.NewMemDatabase()
@@ -159,9 +161,8 @@ func runCmd(ctx *cli.Context) error {
GasPrice: utils.GlobalBig(ctx, PriceFlag.Name),
Value: utils.GlobalBig(ctx, ValueFlag.Name),
EVMConfig: vm.Config{
- Tracer: tracer,
- Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
- DisableGasMetering: ctx.GlobalBool(DisableGasMeteringFlag.Name),
+ Tracer: tracer,
+ Debug: ctx.GlobalBool(DebugFlag.Name) || ctx.GlobalBool(MachineFlag.Name),
},
}
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
index 99527f9d1..5bad09bbd 100644
--- a/cmd/faucet/faucet.go
+++ b/cmd/faucet/faucet.go
@@ -533,9 +533,11 @@ func (f *faucet) loop() {
}
defer sub.Unsubscribe()
- for {
- select {
- case head := <-heads:
+ // Start a goroutine to update the state from head notifications in the background
+ update := make(chan *types.Header)
+
+ go func() {
+ for head := range update {
// New chain head arrived, query the current stats and stream to clients
var (
balance *big.Int
@@ -588,6 +590,17 @@ func (f *faucet) loop() {
}
}
f.lock.RUnlock()
+ }
+ }()
+ // Wait for various events and assing to the appropriate background threads
+ for {
+ select {
+ case head := <-heads:
+ // New head arrived, send if for state update if there's none running
+ select {
+ case update <- head:
+ default:
+ }
case <-f.update:
// Pending requests updated, stream to clients
@@ -686,8 +699,6 @@ func authTwitter(url string) (string, string, common.Address, error) {
if len(parts) < 4 || parts[len(parts)-2] != "status" {
return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
}
- username := parts[len(parts)-3]
-
// Twitter's API isn't really friendly with direct links. Still, we don't
// want to do ask read permissions from users, so just load the public posts and
// scrape it for the Ethereum address and profile URL.
@@ -697,6 +708,13 @@ func authTwitter(url string) (string, string, common.Address, error) {
}
defer res.Body.Close()
+ // Resolve the username from the final redirect, no intermediate junk
+ parts = strings.Split(res.Request.URL.String(), "/")
+ if len(parts) < 4 || parts[len(parts)-2] != "status" {
+ return "", "", common.Address{}, errors.New("Invalid Twitter status URL")
+ }
+ username := parts[len(parts)-3]
+
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return "", "", common.Address{}, err
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index 4a9a7b11b..c9ab72b6d 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -67,6 +67,9 @@ It expects the genesis file as argument.`,
utils.DataDirFlag,
utils.CacheFlag,
utils.LightModeFlag,
+ utils.GCModeFlag,
+ utils.CacheDatabaseFlag,
+ utils.CacheGCFlag,
},
Category: "BLOCKCHAIN COMMANDS",
Description: `
@@ -202,7 +205,7 @@ func importChain(ctx *cli.Context) error {
if len(ctx.Args()) == 1 {
if err := utils.ImportChain(chain, ctx.Args().First()); err != nil {
- utils.Fatalf("Import error: %v", err)
+ log.Error("Import error", "err", err)
}
} else {
for _, arg := range ctx.Args() {
@@ -211,7 +214,7 @@ func importChain(ctx *cli.Context) error {
}
}
}
-
+ chain.Stop()
fmt.Printf("Import done in %v.\n\n", time.Since(start))
// Output pre-compaction stats mostly to see the import trashing
@@ -222,6 +225,13 @@ func importChain(ctx *cli.Context) error {
utils.Fatalf("Failed to read database stats: %v", err)
}
fmt.Println(stats)
+
+ ioStats, err := db.LDB().GetProperty("leveldb.iostats")
+ if err != nil {
+ utils.Fatalf("Failed to read database iostats: %v", err)
+ }
+ fmt.Println(ioStats)
+
fmt.Printf("Trie cache misses: %d\n", trie.CacheMisses())
fmt.Printf("Trie cache unloads: %d\n\n", trie.CacheUnloads())
@@ -252,6 +262,12 @@ func importChain(ctx *cli.Context) error {
}
fmt.Println(stats)
+ ioStats, err = db.LDB().GetProperty("leveldb.iostats")
+ if err != nil {
+ utils.Fatalf("Failed to read database iostats: %v", err)
+ }
+ fmt.Println(ioStats)
+
return nil
}
diff --git a/cmd/geth/config.go b/cmd/geth/config.go
index 9c703758e..50e4de2e7 100644
--- a/cmd/geth/config.go
+++ b/cmd/geth/config.go
@@ -18,7 +18,6 @@ package main
import (
"bufio"
- "encoding/hex"
"errors"
"fmt"
"io"
@@ -29,7 +28,6 @@ import (
cli "gopkg.in/urfave/cli.v1"
"github.com/ethereum/go-ethereum/cmd/utils"
- "github.com/ethereum/go-ethereum/contracts/release"
"github.com/ethereum/go-ethereum/dashboard"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/node"
@@ -177,21 +175,6 @@ func makeFullNode(ctx *cli.Context) *node.Node {
if cfg.Ethstats.URL != "" {
utils.RegisterEthStatsService(stack, cfg.Ethstats.URL)
}
-
- // Add the release oracle service so it boots along with node.
- if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
- config := release.Config{
- Oracle: relOracle,
- Major: uint32(params.VersionMajor),
- Minor: uint32(params.VersionMinor),
- Patch: uint32(params.VersionPatch),
- }
- commit, _ := hex.DecodeString(gitCommit)
- copy(config.Commit[:], commit)
- return release.NewReleaseService(ctx, config)
- }); err != nil {
- utils.Fatalf("Failed to register the Geth release oracle service: %v", err)
- }
return stack
}
diff --git a/cmd/geth/consolecmd.go b/cmd/geth/consolecmd.go
index 9d5cc38a1..2500a969c 100644
--- a/cmd/geth/consolecmd.go
+++ b/cmd/geth/consolecmd.go
@@ -22,6 +22,7 @@ import (
"os/signal"
"path/filepath"
"strings"
+ "syscall"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/console"
@@ -42,7 +43,7 @@ var (
Description: `
The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
-See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.`,
+See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console.`,
}
attachCommand = cli.Command{
@@ -55,7 +56,7 @@ See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.`,
Description: `
The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
-See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.
+See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console.
This command allows to open a console on a running geth node.`,
}
@@ -68,7 +69,7 @@ This command allows to open a console on a running geth node.`,
Category: "CONSOLE COMMANDS",
Description: `
The JavaScript VM exposes a node admin interface as well as the Ðapp
-JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console`,
+JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/JavaScript-Console`,
}
)
@@ -207,7 +208,7 @@ func ephemeralConsole(ctx *cli.Context) error {
}
// Wait for pending callbacks, but stop for Ctrl-C.
abort := make(chan os.Signal, 1)
- signal.Notify(abort, os.Interrupt)
+ signal.Notify(abort, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-abort
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index b955bd243..f5a3fa941 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -65,7 +65,6 @@ var (
utils.DashboardAddrFlag,
utils.DashboardPortFlag,
utils.DashboardRefreshFlag,
- utils.DashboardAssetsFlag,
utils.EthashCacheDirFlag,
utils.EthashCachesInMemoryFlag,
utils.EthashCachesOnDiskFlag,
@@ -85,10 +84,13 @@ var (
utils.FastSyncFlag,
utils.LightModeFlag,
utils.SyncModeFlag,
+ utils.GCModeFlag,
utils.LightServFlag,
utils.LightPeersFlag,
utils.LightKDFFlag,
utils.CacheFlag,
+ utils.CacheDatabaseFlag,
+ utils.CacheGCFlag,
utils.TrieCacheGenFlag,
utils.ListenPortFlag,
utils.MaxPeersFlag,
@@ -111,6 +113,7 @@ var (
utils.VMEnableDebugFlag,
utils.NetworkIdFlag,
utils.RPCCORSDomainFlag,
+ utils.RPCVirtualHostsFlag,
utils.EthStatsURLFlag,
utils.MetricsEnabledFlag,
utils.FakePoWFlag,
diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go
index a834d5b7a..a1558c233 100644
--- a/cmd/geth/usage.go
+++ b/cmd/geth/usage.go
@@ -22,10 +22,11 @@ import (
"io"
"sort"
+ "strings"
+
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/internal/debug"
"gopkg.in/urfave/cli.v1"
- "strings"
)
// AppHelpTemplate is the test template for the default, global app help topic.
@@ -74,6 +75,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.TestnetFlag,
utils.RinkebyFlag,
utils.SyncModeFlag,
+ utils.GCModeFlag,
utils.EthStatsURLFlag,
utils.IdentityFlag,
utils.LightServFlag,
@@ -127,6 +129,8 @@ var AppHelpFlagGroups = []flagGroup{
Name: "PERFORMANCE TUNING",
Flags: []cli.Flag{
utils.CacheFlag,
+ utils.CacheDatabaseFlag,
+ utils.CacheGCFlag,
utils.TrieCacheGenFlag,
},
},
@@ -152,6 +156,7 @@ var AppHelpFlagGroups = []flagGroup{
utils.IPCDisabledFlag,
utils.IPCPathFlag,
utils.RPCCORSDomainFlag,
+ utils.RPCVirtualHostsFlag,
utils.JSpathFlag,
utils.ExecFlag,
utils.PreloadJSFlag,
diff --git a/cmd/p2psim/main.go b/cmd/p2psim/main.go
index 56b74d135..0c8ed038d 100644
--- a/cmd/p2psim/main.go
+++ b/cmd/p2psim/main.go
@@ -1,3 +1,19 @@
+// Copyright 2017 The go-ethereum Authors
+// This file is part of go-ethereum.
+//
+// go-ethereum is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// go-ethereum 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
+
// p2psim provides a command-line client for a simulation HTTP API.
//
// Here is an example of creating a 2 node network with the first node
diff --git a/cmd/puppeth/genesis.go b/cmd/puppeth/genesis.go
index f747f4739..1974a94aa 100644
--- a/cmd/puppeth/genesis.go
+++ b/cmd/puppeth/genesis.go
@@ -168,19 +168,18 @@ type parityChainSpec struct {
Engine struct {
Ethash struct {
Params struct {
- MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
- DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
- GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
- DurationLimit *hexutil.Big `json:"durationLimit"`
- BlockReward *hexutil.Big `json:"blockReward"`
- HomesteadTransition uint64 `json:"homesteadTransition"`
- EIP150Transition uint64 `json:"eip150Transition"`
- EIP160Transition uint64 `json:"eip160Transition"`
- EIP161abcTransition uint64 `json:"eip161abcTransition"`
- EIP161dTransition uint64 `json:"eip161dTransition"`
- EIP649Reward *hexutil.Big `json:"eip649Reward"`
- EIP100bTransition uint64 `json:"eip100bTransition"`
- EIP649Transition uint64 `json:"eip649Transition"`
+ MinimumDifficulty *hexutil.Big `json:"minimumDifficulty"`
+ DifficultyBoundDivisor *hexutil.Big `json:"difficultyBoundDivisor"`
+ DurationLimit *hexutil.Big `json:"durationLimit"`
+ BlockReward *hexutil.Big `json:"blockReward"`
+ HomesteadTransition uint64 `json:"homesteadTransition"`
+ EIP150Transition uint64 `json:"eip150Transition"`
+ EIP160Transition uint64 `json:"eip160Transition"`
+ EIP161abcTransition uint64 `json:"eip161abcTransition"`
+ EIP161dTransition uint64 `json:"eip161dTransition"`
+ EIP649Reward *hexutil.Big `json:"eip649Reward"`
+ EIP100bTransition uint64 `json:"eip100bTransition"`
+ EIP649Transition uint64 `json:"eip649Transition"`
} `json:"params"`
} `json:"Ethash"`
} `json:"engine"`
@@ -188,6 +187,7 @@ type parityChainSpec struct {
Params struct {
MaximumExtraDataSize hexutil.Uint64 `json:"maximumExtraDataSize"`
MinGasLimit hexutil.Uint64 `json:"minGasLimit"`
+ GasLimitBoundDivisor hexutil.Uint64 `json:"gasLimitBoundDivisor"`
NetworkID hexutil.Uint64 `json:"networkID"`
MaxCodeSize uint64 `json:"maxCodeSize"`
EIP155Transition uint64 `json:"eip155Transition"`
@@ -270,7 +270,6 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
}
spec.Engine.Ethash.Params.MinimumDifficulty = (*hexutil.Big)(params.MinimumDifficulty)
spec.Engine.Ethash.Params.DifficultyBoundDivisor = (*hexutil.Big)(params.DifficultyBoundDivisor)
- spec.Engine.Ethash.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
spec.Engine.Ethash.Params.DurationLimit = (*hexutil.Big)(params.DurationLimit)
spec.Engine.Ethash.Params.BlockReward = (*hexutil.Big)(ethash.FrontierBlockReward)
spec.Engine.Ethash.Params.HomesteadTransition = genesis.Config.HomesteadBlock.Uint64()
@@ -284,6 +283,7 @@ func newParityChainSpec(network string, genesis *core.Genesis, bootnodes []strin
spec.Params.MaximumExtraDataSize = (hexutil.Uint64)(params.MaximumExtraDataSize)
spec.Params.MinGasLimit = (hexutil.Uint64)(params.MinGasLimit)
+ spec.Params.GasLimitBoundDivisor = (hexutil.Uint64)(params.GasLimitBoundDivisor)
spec.Params.NetworkID = (hexutil.Uint64)(genesis.Config.ChainId.Uint64())
spec.Params.MaxCodeSize = params.MaxCodeSize
spec.Params.EIP155Transition = genesis.Config.EIP155Block.Uint64()
diff --git a/cmd/puppeth/module_dashboard.go b/cmd/puppeth/module_dashboard.go
index 1092c4c88..3832b247f 100644
--- a/cmd/puppeth/module_dashboard.go
+++ b/cmd/puppeth/module_dashboard.go
@@ -117,7 +117,7 @@ var dashboardContent = `
<br/>
<p>To run an archive node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
- <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=1024 --syncmode=full{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFullFlat}}</pre>
+ <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=1024 --syncmode=full{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre>
</p>
<br/>
<p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
@@ -136,7 +136,7 @@ var dashboardContent = `
<br/>
<p>To run a full node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
- <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=512{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFullFlat}}</pre>
+ <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=512{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre>
</p>
<br/>
<p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
@@ -158,7 +158,7 @@ var dashboardContent = `
<br/>
<p>To run a light node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
- <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre>
+ <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre>
</p>
<br/>
<p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
@@ -177,7 +177,7 @@ var dashboardContent = `
<br/>
<p>To run an embedded node, download <a href="/{{.GethGenesis}}"><code>{{.GethGenesis}}</code></a> and start Geth with:
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
- <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=16 --ethash.cachesinmem=1 --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesLightFlat}}</pre>
+ <pre>geth --networkid={{.NetworkID}} --datadir=$HOME/.{{.Network}} --cache=16 --ethash.cachesinmem=1 --syncmode=light{{if .Ethstats}} --ethstats='{{.Ethstats}}'{{end}} --bootnodes={{.BootnodesFlat}}</pre>
</p>
<br/>
<p>You can download Geth from <a href="https://geth.ethereum.org/downloads/" target="about:blank">https://geth.ethereum.org/downloads/</a>.</p>
@@ -208,7 +208,7 @@ var dashboardContent = `
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
</p>
<p>With your local chain initialized, you can start the Ethereum Wallet:
- <pre>ethereumwallet --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFullFlat}}</pre>
+ <pre>ethereumwallet --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFlat}}</pre>
<p>
<br/>
<p>You can download the Ethereum Wallet from <a href="https://github.com/ethereum/mist/releases" target="about:blank">https://github.com/ethereum/mist/releases</a>.</p>
@@ -229,7 +229,7 @@ var dashboardContent = `
<pre>geth --datadir=$HOME/.{{.Network}} init {{.GethGenesis}}</pre>
</p>
<p>With your local chain initialized, you can start Mist:
- <pre>mist --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFullFlat}}</pre>
+ <pre>mist --rpc $HOME/.{{.Network}}/geth.ipc --node-networkid={{.NetworkID}} --node-datadir=$HOME/.{{.Network}}{{if .Ethstats}} --node-ethstats='{{.Ethstats}}'{{end}} --node-bootnodes={{.BootnodesFlat}}</pre>
<p>
<br/>
<p>You can download the Mist browser from <a href="https://github.com/ethereum/mist/releases" target="about:blank">https://github.com/ethereum/mist/releases</a>.</p>
@@ -261,7 +261,7 @@ var dashboardContent = `
<p>Inside your Java code you can now import the geth archive and connect to Ethereum:
<pre>import org.ethereum.geth.*;</pre>
<pre>
-Enodes bootnodes = new Enodes();{{range .BootnodesLight}}
+Enodes bootnodes = new Enodes();{{range .Bootnodes}}
bootnodes.append(new Enode("{{.}}"));{{end}}
NodeConfig config = new NodeConfig();
@@ -294,7 +294,7 @@ node.start();
<pre>
var error: NSError?
-let bootnodes = GethNewEnodesEmpty(){{range .BootnodesLight}}
+let bootnodes = GethNewEnodesEmpty(){{range .Bootnodes}}
bootnodes?.append(GethNewEnode("{{.}}", &error)){{end}}
let config = GethNewNodeConfig()
@@ -595,44 +595,43 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
statsLogin = ""
}
indexfile := new(bytes.Buffer)
- bootCpp := make([]string, len(conf.bootFull))
- for i, boot := range conf.bootFull {
+ bootCpp := make([]string, len(conf.bootnodes))
+ for i, boot := range conf.bootnodes {
bootCpp[i] = "required:" + strings.TrimPrefix(boot, "enode://")
}
- bootHarmony := make([]string, len(conf.bootFull))
- for i, boot := range conf.bootFull {
+ bootHarmony := make([]string, len(conf.bootnodes))
+ for i, boot := range conf.bootnodes {
bootHarmony[i] = fmt.Sprintf("-Dpeer.active.%d.url=%s", i, boot)
}
- bootPython := make([]string, len(conf.bootFull))
- for i, boot := range conf.bootFull {
+ bootPython := make([]string, len(conf.bootnodes))
+ for i, boot := range conf.bootnodes {
bootPython[i] = "'" + boot + "'"
}
template.Must(template.New("").Parse(dashboardContent)).Execute(indexfile, map[string]interface{}{
- "Network": network,
- "NetworkID": conf.Genesis.Config.ChainId,
- "NetworkTitle": strings.Title(network),
- "EthstatsPage": config.ethstats,
- "ExplorerPage": config.explorer,
- "WalletPage": config.wallet,
- "FaucetPage": config.faucet,
- "GethGenesis": network + ".json",
- "BootnodesFull": conf.bootFull,
- "BootnodesLight": conf.bootLight,
- "BootnodesFullFlat": strings.Join(conf.bootFull, ","),
- "BootnodesLightFlat": strings.Join(conf.bootLight, ","),
- "Ethstats": statsLogin,
- "Ethash": conf.Genesis.Config.Ethash != nil,
- "CppGenesis": network + "-cpp.json",
- "CppBootnodes": strings.Join(bootCpp, " "),
- "HarmonyGenesis": network + "-harmony.json",
- "HarmonyBootnodes": strings.Join(bootHarmony, " "),
- "ParityGenesis": network + "-parity.json",
- "PythonGenesis": network + "-python.json",
- "PythonBootnodes": strings.Join(bootPython, ","),
- "Homestead": conf.Genesis.Config.HomesteadBlock,
- "Tangerine": conf.Genesis.Config.EIP150Block,
- "Spurious": conf.Genesis.Config.EIP155Block,
- "Byzantium": conf.Genesis.Config.ByzantiumBlock,
+ "Network": network,
+ "NetworkID": conf.Genesis.Config.ChainId,
+ "NetworkTitle": strings.Title(network),
+ "EthstatsPage": config.ethstats,
+ "ExplorerPage": config.explorer,
+ "WalletPage": config.wallet,
+ "FaucetPage": config.faucet,
+ "GethGenesis": network + ".json",
+ "Bootnodes": conf.bootnodes,
+ "BootnodesFlat": strings.Join(conf.bootnodes, ","),
+ "Ethstats": statsLogin,
+ "Ethash": conf.Genesis.Config.Ethash != nil,
+ "CppGenesis": network + "-cpp.json",
+ "CppBootnodes": strings.Join(bootCpp, " "),
+ "HarmonyGenesis": network + "-harmony.json",
+ "HarmonyBootnodes": strings.Join(bootHarmony, " "),
+ "ParityGenesis": network + "-parity.json",
+ "PythonGenesis": network + "-python.json",
+ "PythonBootnodes": strings.Join(bootPython, ","),
+ "Homestead": conf.Genesis.Config.HomesteadBlock,
+ "Tangerine": conf.Genesis.Config.EIP150Block,
+ "Spurious": conf.Genesis.Config.EIP155Block,
+ "Byzantium": conf.Genesis.Config.ByzantiumBlock,
+ "Constantinople": conf.Genesis.Config.ConstantinopleBlock,
})
files[filepath.Join(workdir, "index.html")] = indexfile.Bytes()
@@ -651,7 +650,7 @@ func deployDashboard(client *sshClient, network string, conf *config, config *da
harmonySpecJSON, _ := conf.Genesis.MarshalJSON()
files[filepath.Join(workdir, network+"-harmony.json")] = harmonySpecJSON
- paritySpec, err := newParityChainSpec(network, conf.Genesis, conf.bootFull)
+ paritySpec, err := newParityChainSpec(network, conf.Genesis, conf.bootnodes)
if err != nil {
return nil, err
}
diff --git a/cmd/puppeth/module_faucet.go b/cmd/puppeth/module_faucet.go
index 92b4cb286..976bf04d0 100644
--- a/cmd/puppeth/module_faucet.go
+++ b/cmd/puppeth/module_faucet.go
@@ -93,7 +93,7 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
"NetworkID": config.node.network,
"Bootnodes": strings.Join(bootnodes, ","),
"Ethstats": config.node.ethstats,
- "EthPort": config.node.portFull,
+ "EthPort": config.node.port,
"CaptchaToken": config.captchaToken,
"CaptchaSecret": config.captchaSecret,
"FaucetName": strings.Title(network),
@@ -110,7 +110,7 @@ func deployFaucet(client *sshClient, network string, bootnodes []string, config
"Datadir": config.node.datadir,
"VHost": config.host,
"ApiPort": config.port,
- "EthPort": config.node.portFull,
+ "EthPort": config.node.port,
"EthName": config.node.ethstats[:strings.Index(config.node.ethstats, ":")],
"CaptchaToken": config.captchaToken,
"CaptchaSecret": config.captchaSecret,
@@ -158,7 +158,7 @@ func (info *faucetInfos) Report() map[string]string {
report := map[string]string{
"Website address": info.host,
"Website listener port": strconv.Itoa(info.port),
- "Ethereum listener port": strconv.Itoa(info.node.portFull),
+ "Ethereum listener port": strconv.Itoa(info.node.port),
"Funding amount (base tier)": fmt.Sprintf("%d Ethers", info.amount),
"Funding cooldown (base tier)": fmt.Sprintf("%d mins", info.minutes),
"Funding tiers": strconv.Itoa(info.tiers),
@@ -228,7 +228,7 @@ func checkFaucet(client *sshClient, network string) (*faucetInfos, error) {
return &faucetInfos{
node: &nodeInfos{
datadir: infos.volumes["/root/.faucet"],
- portFull: infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"],
+ port: infos.portmap[infos.envvars["ETH_PORT"]+"/tcp"],
ethstats: infos.envvars["ETH_NAME"],
keyJSON: keyJSON,
keyPass: keyPass,
diff --git a/cmd/puppeth/module_node.go b/cmd/puppeth/module_node.go
index 69cb19c34..2609fd976 100644
--- a/cmd/puppeth/module_node.go
+++ b/cmd/puppeth/module_node.go
@@ -42,7 +42,7 @@ ADD genesis.json /genesis.json
RUN \
echo 'geth --cache 512 init /genesis.json' > geth.sh && \{{if .Unlock}}
echo 'mkdir -p /root/.ethereum/keystore/ && cp /signer.json /root/.ethereum/keystore/' >> geth.sh && \{{end}}
- echo $'geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .BootV4}}--bootnodesv4 {{.BootV4}}{{end}} {{if .BootV5}}--bootnodesv5 {{.BootV5}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine --minerthreads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> geth.sh
+ echo $'geth --networkid {{.NetworkID}} --cache 512 --port {{.Port}} --maxpeers {{.Peers}} {{.LightFlag}} --ethstats \'{{.Ethstats}}\' {{if .Bootnodes}}--bootnodes {{.Bootnodes}}{{end}} {{if .Etherbase}}--etherbase {{.Etherbase}} --mine --minerthreads 1{{end}} {{if .Unlock}}--unlock 0 --password /signer.pass --mine{{end}} --targetgaslimit {{.GasTarget}} --gasprice {{.GasPrice}}' >> geth.sh
ENTRYPOINT ["/bin/sh", "geth.sh"]
`
@@ -56,15 +56,13 @@ services:
build: .
image: {{.Network}}/{{.Type}}
ports:
- - "{{.FullPort}}:{{.FullPort}}"
- - "{{.FullPort}}:{{.FullPort}}/udp"{{if .Light}}
- - "{{.LightPort}}:{{.LightPort}}/udp"{{end}}
+ - "{{.Port}}:{{.Port}}"
+ - "{{.Port}}:{{.Port}}/udp"
volumes:
- {{.Datadir}}:/root/.ethereum{{if .Ethashdir}}
- {{.Ethashdir}}:/root/.ethash{{end}}
environment:
- - FULL_PORT={{.FullPort}}/tcp
- - LIGHT_PORT={{.LightPort}}/udp
+ - PORT={{.Port}}/tcp
- TOTAL_PEERS={{.TotalPeers}}
- LIGHT_PEERS={{.LightPeers}}
- STATS_NAME={{.Ethstats}}
@@ -82,12 +80,11 @@ services:
// deployNode deploys a new Ethereum node container to a remote machine via SSH,
// docker and docker-compose. If an instance with the specified network name
// already exists there, it will be overwritten!
-func deployNode(client *sshClient, network string, bootv4, bootv5 []string, config *nodeInfos, nocache bool) ([]byte, error) {
+func deployNode(client *sshClient, network string, bootnodes []string, config *nodeInfos, nocache bool) ([]byte, error) {
kind := "sealnode"
if config.keyJSON == "" && config.etherbase == "" {
kind = "bootnode"
- bootv4 = make([]string, 0)
- bootv5 = make([]string, 0)
+ bootnodes = make([]string, 0)
}
// Generate the content to upload to the server
workdir := fmt.Sprintf("%d", rand.Int63())
@@ -100,11 +97,10 @@ func deployNode(client *sshClient, network string, bootv4, bootv5 []string, conf
dockerfile := new(bytes.Buffer)
template.Must(template.New("").Parse(nodeDockerfile)).Execute(dockerfile, map[string]interface{}{
"NetworkID": config.network,
- "Port": config.portFull,
+ "Port": config.port,
"Peers": config.peersTotal,
"LightFlag": lightFlag,
- "BootV4": strings.Join(bootv4, ","),
- "BootV5": strings.Join(bootv5, ","),
+ "Bootnodes": strings.Join(bootnodes, ","),
"Ethstats": config.ethstats,
"Etherbase": config.etherbase,
"GasTarget": uint64(1000000 * config.gasTarget),
@@ -119,10 +115,9 @@ func deployNode(client *sshClient, network string, bootv4, bootv5 []string, conf
"Datadir": config.datadir,
"Ethashdir": config.ethashdir,
"Network": network,
- "FullPort": config.portFull,
+ "Port": config.port,
"TotalPeers": config.peersTotal,
"Light": config.peersLight > 0,
- "LightPort": config.portFull + 1,
"LightPeers": config.peersLight,
"Ethstats": config.ethstats[:strings.Index(config.ethstats, ":")],
"Etherbase": config.etherbase,
@@ -157,10 +152,8 @@ type nodeInfos struct {
datadir string
ethashdir string
ethstats string
- portFull int
- portLight int
- enodeFull string
- enodeLight string
+ port int
+ enode string
peersTotal int
peersLight int
etherbase string
@@ -174,15 +167,11 @@ type nodeInfos struct {
// most - but not all - fields for reporting to the user.
func (info *nodeInfos) Report() map[string]string {
report := map[string]string{
- "Data directory": info.datadir,
- "Listener port (full nodes)": strconv.Itoa(info.portFull),
- "Peer count (all total)": strconv.Itoa(info.peersTotal),
- "Peer count (light nodes)": strconv.Itoa(info.peersLight),
- "Ethstats username": info.ethstats,
- }
- if info.peersLight > 0 {
- // Light server enabled
- report["Listener port (light nodes)"] = strconv.Itoa(info.portLight)
+ "Data directory": info.datadir,
+ "Listener port": strconv.Itoa(info.port),
+ "Peer count (all total)": strconv.Itoa(info.peersTotal),
+ "Peer count (light nodes)": strconv.Itoa(info.peersLight),
+ "Ethstats username": info.ethstats,
}
if info.gasTarget > 0 {
// Miner or signer node
@@ -250,7 +239,7 @@ func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error)
keyPass = string(bytes.TrimSpace(out))
}
// Run a sanity check to see if the devp2p is reachable
- port := infos.portmap[infos.envvars["FULL_PORT"]]
+ port := infos.portmap[infos.envvars["PORT"]]
if err = checkPort(client.server, port); err != nil {
log.Warn(fmt.Sprintf("%s devp2p port seems unreachable", strings.Title(kind)), "server", client.server, "port", port, "err", err)
}
@@ -259,8 +248,7 @@ func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error)
genesis: genesis,
datadir: infos.volumes["/root/.ethereum"],
ethashdir: infos.volumes["/root/.ethash"],
- portFull: infos.portmap[infos.envvars["FULL_PORT"]],
- portLight: infos.portmap[infos.envvars["LIGHT_PORT"]],
+ port: port,
peersTotal: totalPeers,
peersLight: lightPeers,
ethstats: infos.envvars["STATS_NAME"],
@@ -270,9 +258,7 @@ func checkNode(client *sshClient, network string, boot bool) (*nodeInfos, error)
gasTarget: gasTarget,
gasPrice: gasPrice,
}
- stats.enodeFull = fmt.Sprintf("enode://%s@%s:%d", id, client.address, stats.portFull)
- if stats.portLight != 0 {
- stats.enodeLight = fmt.Sprintf("enode://%s@%s:%d?discport=%d", id, client.address, stats.portFull, stats.portLight)
- }
+ stats.enode = fmt.Sprintf("enode://%s@%s:%d", id, client.address, stats.port)
+
return stats, nil
}
diff --git a/cmd/puppeth/module_wallet.go b/cmd/puppeth/module_wallet.go
index 67f47c70e..5e5032bed 100644
--- a/cmd/puppeth/module_wallet.go
+++ b/cmd/puppeth/module_wallet.go
@@ -37,7 +37,7 @@ ADD genesis.json /genesis.json
RUN \
echo 'node server.js &' > wallet.sh && \
echo 'geth --cache 512 init /genesis.json' >> wallet.sh && \
- echo $'geth --networkid {{.NetworkID}} --port {{.NodePort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --rpc --rpcaddr=0.0.0.0 --rpccorsdomain "*"' >> wallet.sh
+ echo $'geth --networkid {{.NetworkID}} --port {{.NodePort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --rpc --rpcaddr=0.0.0.0 --rpccorsdomain "*" --rpcvhosts "*"' >> wallet.sh
RUN \
sed -i 's/PuppethNetworkID/{{.NetworkID}}/g' dist/js/etherwallet-master.js && \
diff --git a/cmd/puppeth/wizard.go b/cmd/puppeth/wizard.go
index 2e2b4644c..b88a61de7 100644
--- a/cmd/puppeth/wizard.go
+++ b/cmd/puppeth/wizard.go
@@ -40,8 +40,7 @@ import (
// between sessions.
type config struct {
path string // File containing the configuration values
- bootFull []string // Bootnodes to always connect to by full nodes
- bootLight []string // Bootnodes to always connect to by light nodes
+ bootnodes []string // Bootnodes to always connect to by all nodes
ethstats string // Ethstats settings to cache for node deploys
Genesis *core.Genesis `json:"genesis,omitempty"` // Genesis block to cache for node deploys
diff --git a/cmd/puppeth/wizard_explorer.go b/cmd/puppeth/wizard_explorer.go
index 10ef72f78..413511c1c 100644
--- a/cmd/puppeth/wizard_explorer.go
+++ b/cmd/puppeth/wizard_explorer.go
@@ -55,7 +55,7 @@ func (w *wizard) deployExplorer() {
}
existed := err == nil
- chainspec, err := newParityChainSpec(w.network, w.conf.Genesis, w.conf.bootFull)
+ chainspec, err := newParityChainSpec(w.network, w.conf.Genesis, w.conf.bootnodes)
if err != nil {
log.Error("Failed to create chain spec for explorer", "err", err)
return
diff --git a/cmd/puppeth/wizard_faucet.go b/cmd/puppeth/wizard_faucet.go
index 191575b16..9a429bc96 100644
--- a/cmd/puppeth/wizard_faucet.go
+++ b/cmd/puppeth/wizard_faucet.go
@@ -38,7 +38,7 @@ func (w *wizard) deployFaucet() {
infos, err := checkFaucet(client, w.network)
if err != nil {
infos = &faucetInfos{
- node: &nodeInfos{portFull: 30303, peersTotal: 25},
+ node: &nodeInfos{port: 30303, peersTotal: 25},
port: 80,
host: client.server,
amount: 1,
@@ -113,8 +113,8 @@ func (w *wizard) deployFaucet() {
}
// Figure out which port to listen on
fmt.Println()
- fmt.Printf("Which TCP/UDP port should the light client listen on? (default = %d)\n", infos.node.portFull)
- infos.node.portFull = w.readDefaultInt(infos.node.portFull)
+ fmt.Printf("Which TCP/UDP port should the light client listen on? (default = %d)\n", infos.node.port)
+ infos.node.port = w.readDefaultInt(infos.node.port)
// Set a proper name to report on the stats page
fmt.Println()
@@ -168,7 +168,7 @@ func (w *wizard) deployFaucet() {
fmt.Printf("Should the faucet be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n"
}
- if out, err := deployFaucet(client, w.network, w.conf.bootLight, infos, nocache); err != nil {
+ if out, err := deployFaucet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
log.Error("Failed to deploy faucet container", "err", err)
if len(out) > 0 {
fmt.Printf("%s\n", out)
diff --git a/cmd/puppeth/wizard_intro.go b/cmd/puppeth/wizard_intro.go
index 84998afc9..60aa0f7ff 100644
--- a/cmd/puppeth/wizard_intro.go
+++ b/cmd/puppeth/wizard_intro.go
@@ -59,15 +59,16 @@ func (w *wizard) run() {
fmt.Println()
// Make sure we have a good network name to work with fmt.Println()
+ // Docker accepts hyphens in image names, but doesn't like it for container names
if w.network == "" {
- fmt.Println("Please specify a network name to administer (no spaces, please)")
+ fmt.Println("Please specify a network name to administer (no spaces or hyphens, please)")
for {
w.network = w.readString()
- if !strings.Contains(w.network, " ") {
+ if !strings.Contains(w.network, " ") && !strings.Contains(w.network, "-") {
fmt.Printf("\nSweet, you can set this via --network=%s next time!\n\n", w.network)
break
}
- log.Error("I also like to live dangerously, still no spaces")
+ log.Error("I also like to live dangerously, still no spaces or hyphens")
}
}
log.Info("Administering Ethereum network", "name", w.network)
diff --git a/cmd/puppeth/wizard_netstats.go b/cmd/puppeth/wizard_netstats.go
index e19180bb1..90bf7ae3c 100644
--- a/cmd/puppeth/wizard_netstats.go
+++ b/cmd/puppeth/wizard_netstats.go
@@ -37,8 +37,7 @@ func (w *wizard) networkStats() {
}
// Clear out some previous configs to refill from current scan
w.conf.ethstats = ""
- w.conf.bootFull = w.conf.bootFull[:0]
- w.conf.bootLight = w.conf.bootLight[:0]
+ w.conf.bootnodes = w.conf.bootnodes[:0]
// Iterate over all the specified hosts and check their status
var pend sync.WaitGroup
@@ -76,8 +75,7 @@ func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *s
var (
genesis string
ethstats string
- bootFull []string
- bootLight []string
+ bootnodes []string
)
// Ensure a valid SSH connection to the remote server
logger := log.New("server", server)
@@ -123,10 +121,7 @@ func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *s
stat.services["bootnode"] = infos.Report()
genesis = string(infos.genesis)
- bootFull = append(bootFull, infos.enodeFull)
- if infos.enodeLight != "" {
- bootLight = append(bootLight, infos.enodeLight)
- }
+ bootnodes = append(bootnodes, infos.enode)
}
logger.Debug("Checking for sealnode availability")
if infos, err := checkNode(client, w.network, false); err != nil {
@@ -184,8 +179,7 @@ func (w *wizard) gatherStats(server string, pubkey []byte, client *sshClient) *s
if ethstats != "" {
w.conf.ethstats = ethstats
}
- w.conf.bootFull = append(w.conf.bootFull, bootFull...)
- w.conf.bootLight = append(w.conf.bootLight, bootLight...)
+ w.conf.bootnodes = append(w.conf.bootnodes, bootnodes...)
return stat
}
diff --git a/cmd/puppeth/wizard_node.go b/cmd/puppeth/wizard_node.go
index 097e2e41a..a60948bc6 100644
--- a/cmd/puppeth/wizard_node.go
+++ b/cmd/puppeth/wizard_node.go
@@ -48,9 +48,9 @@ func (w *wizard) deployNode(boot bool) {
infos, err := checkNode(client, w.network, boot)
if err != nil {
if boot {
- infos = &nodeInfos{portFull: 30303, peersTotal: 512, peersLight: 256}
+ infos = &nodeInfos{port: 30303, peersTotal: 512, peersLight: 256}
} else {
- infos = &nodeInfos{portFull: 30303, peersTotal: 50, peersLight: 0, gasTarget: 4.7, gasPrice: 18}
+ infos = &nodeInfos{port: 30303, peersTotal: 50, peersLight: 0, gasTarget: 4.7, gasPrice: 18}
}
}
existed := err == nil
@@ -79,8 +79,8 @@ func (w *wizard) deployNode(boot bool) {
}
// Figure out which port to listen on
fmt.Println()
- fmt.Printf("Which TCP/UDP port to listen on? (default = %d)\n", infos.portFull)
- infos.portFull = w.readDefaultInt(infos.portFull)
+ fmt.Printf("Which TCP/UDP port to listen on? (default = %d)\n", infos.port)
+ infos.port = w.readDefaultInt(infos.port)
// Figure out how many peers to allow (different based on node type)
fmt.Println()
@@ -163,7 +163,7 @@ func (w *wizard) deployNode(boot bool) {
fmt.Printf("Should the node be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n"
}
- if out, err := deployNode(client, w.network, w.conf.bootFull, w.conf.bootLight, infos, nocache); err != nil {
+ if out, err := deployNode(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
log.Error("Failed to deploy Ethereum node container", "err", err)
if len(out) > 0 {
fmt.Printf("%s\n", out)
diff --git a/cmd/puppeth/wizard_wallet.go b/cmd/puppeth/wizard_wallet.go
index 7c3896a17..933cd9ae5 100644
--- a/cmd/puppeth/wizard_wallet.go
+++ b/cmd/puppeth/wizard_wallet.go
@@ -98,7 +98,7 @@ func (w *wizard) deployWallet() {
fmt.Printf("Should the wallet be built from scratch (y/n)? (default = no)\n")
nocache = w.readDefaultString("n") != "n"
}
- if out, err := deployWallet(client, w.network, w.conf.bootFull, infos, nocache); err != nil {
+ if out, err := deployWallet(client, w.network, w.conf.bootnodes, infos, nocache); err != nil {
log.Error("Failed to deploy wallet container", "err", err)
if len(out) > 0 {
fmt.Printf("%s\n", out)
diff --git a/cmd/swarm/config.go b/cmd/swarm/config.go
index 29b5faefa..adac772ba 100644
--- a/cmd/swarm/config.go
+++ b/cmd/swarm/config.go
@@ -23,6 +23,7 @@ import (
"os"
"reflect"
"strconv"
+ "strings"
"unicode"
cli "gopkg.in/urfave/cli.v1"
@@ -97,10 +98,15 @@ func buildConfig(ctx *cli.Context) (config *bzzapi.Config, err error) {
config = bzzapi.NewDefaultConfig()
//first load settings from config file (if provided)
config, err = configFileOverride(config, ctx)
+ if err != nil {
+ return nil, err
+ }
//override settings provided by environment variables
config = envVarsOverride(config)
//override settings provided by command line
config = cmdLineOverride(config, ctx)
+ //validate configuration parameters
+ err = validateConfig(config)
return
}
@@ -194,12 +200,16 @@ func cmdLineOverride(currentConfig *bzzapi.Config, ctx *cli.Context) *bzzapi.Con
utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API)
}
- //EnsApi can be set to "", so can't check for empty string, as it is allowed!
if ctx.GlobalIsSet(EnsAPIFlag.Name) {
- currentConfig.EnsApi = ctx.GlobalString(EnsAPIFlag.Name)
+ ensAPIs := ctx.GlobalStringSlice(EnsAPIFlag.Name)
+ // preserve backward compatibility to disable ENS with --ens-api=""
+ if len(ensAPIs) == 1 && ensAPIs[0] == "" {
+ ensAPIs = nil
+ }
+ currentConfig.EnsAPIs = ensAPIs
}
- if ensaddr := ctx.GlobalString(EnsAddrFlag.Name); ensaddr != "" {
+ if ensaddr := ctx.GlobalString(DeprecatedEnsAddrFlag.Name); ensaddr != "" {
currentConfig.EnsRoot = common.HexToAddress(ensaddr)
}
@@ -266,9 +276,8 @@ func envVarsOverride(currentConfig *bzzapi.Config) (config *bzzapi.Config) {
utils.Fatalf(SWARM_ERR_SWAP_SET_NO_API)
}
- //EnsApi can be set to "", so can't check for empty string, as it is allowed
- if ensapi, exists := os.LookupEnv(SWARM_ENV_ENS_API); exists {
- currentConfig.EnsApi = ensapi
+ if ensapi := os.Getenv(SWARM_ENV_ENS_API); ensapi != "" {
+ currentConfig.EnsAPIs = strings.Split(ensapi, ",")
}
if ensaddr := os.Getenv(SWARM_ENV_ENS_ADDR); ensaddr != "" {
@@ -309,6 +318,43 @@ func checkDeprecated(ctx *cli.Context) {
if ctx.GlobalString(DeprecatedEthAPIFlag.Name) != "" {
utils.Fatalf("--ethapi is no longer a valid command line flag, please use --ens-api and/or --swap-api.")
}
+ // warn if --ens-api flag is set
+ if ctx.GlobalString(DeprecatedEnsAddrFlag.Name) != "" {
+ log.Warn("--ens-addr is no longer a valid command line flag, please use --ens-api to specify contract address.")
+ }
+}
+
+//validate configuration parameters
+func validateConfig(cfg *bzzapi.Config) (err error) {
+ for _, ensAPI := range cfg.EnsAPIs {
+ if ensAPI != "" {
+ if err := validateEnsAPIs(ensAPI); err != nil {
+ return fmt.Errorf("invalid format [tld:][contract-addr@]url for ENS API endpoint configuration %q: %v", ensAPI, err)
+ }
+ }
+ }
+ return nil
+}
+
+//validate EnsAPIs configuration parameter
+func validateEnsAPIs(s string) (err error) {
+ // missing contract address
+ if strings.HasPrefix(s, "@") {
+ return errors.New("missing contract address")
+ }
+ // missing url
+ if strings.HasSuffix(s, "@") {
+ return errors.New("missing url")
+ }
+ // missing tld
+ if strings.HasPrefix(s, ":") {
+ return errors.New("missing tld")
+ }
+ // missing url
+ if strings.HasSuffix(s, ":") {
+ return errors.New("missing url")
+ }
+ return nil
}
//print a Config as string
diff --git a/cmd/swarm/config_test.go b/cmd/swarm/config_test.go
index 166980d14..9bf584f50 100644
--- a/cmd/swarm/config_test.go
+++ b/cmd/swarm/config_test.go
@@ -457,3 +457,98 @@ func TestCmdLineOverridesFile(t *testing.T) {
node.Shutdown()
}
+
+func TestValidateConfig(t *testing.T) {
+ for _, c := range []struct {
+ cfg *api.Config
+ err string
+ }{
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "/data/testnet/geth.ipc",
+ }},
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "http://127.0.0.1:1234",
+ }},
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "ws://127.0.0.1:1234",
+ }},
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "test:/data/testnet/geth.ipc",
+ }},
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "test:ws://127.0.0.1:1234",
+ }},
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "314159265dD8dbb310642f98f50C066173C1259b@/data/testnet/geth.ipc",
+ }},
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "314159265dD8dbb310642f98f50C066173C1259b@http://127.0.0.1:1234",
+ }},
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "314159265dD8dbb310642f98f50C066173C1259b@ws://127.0.0.1:1234",
+ }},
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "test:314159265dD8dbb310642f98f50C066173C1259b@/data/testnet/geth.ipc",
+ }},
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "eth:314159265dD8dbb310642f98f50C066173C1259b@http://127.0.0.1:1234",
+ }},
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "eth:314159265dD8dbb310642f98f50C066173C1259b@ws://127.0.0.1:12344",
+ }},
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "eth:",
+ }},
+ err: "invalid format [tld:][contract-addr@]url for ENS API endpoint configuration \"eth:\": missing url",
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "314159265dD8dbb310642f98f50C066173C1259b@",
+ }},
+ err: "invalid format [tld:][contract-addr@]url for ENS API endpoint configuration \"314159265dD8dbb310642f98f50C066173C1259b@\": missing url",
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ ":314159265dD8dbb310642f98f50C066173C1259",
+ }},
+ err: "invalid format [tld:][contract-addr@]url for ENS API endpoint configuration \":314159265dD8dbb310642f98f50C066173C1259\": missing tld",
+ },
+ {
+ cfg: &api.Config{EnsAPIs: []string{
+ "@/data/testnet/geth.ipc",
+ }},
+ err: "invalid format [tld:][contract-addr@]url for ENS API endpoint configuration \"@/data/testnet/geth.ipc\": missing contract address",
+ },
+ } {
+ err := validateConfig(c.cfg)
+ if c.err != "" && err.Error() != c.err {
+ t.Errorf("expected error %q, got %q", c.err, err)
+ }
+ if c.err == "" && err != nil {
+ t.Errorf("unexpected error %q", err)
+ }
+ }
+}
diff --git a/cmd/swarm/main.go b/cmd/swarm/main.go
index 77315a426..360020b77 100644
--- a/cmd/swarm/main.go
+++ b/cmd/swarm/main.go
@@ -17,11 +17,9 @@
package main
import (
- "context"
"crypto/ecdsa"
"fmt"
"io/ioutil"
- "math/big"
"os"
"os/signal"
"runtime"
@@ -29,14 +27,12 @@ import (
"strconv"
"strings"
"syscall"
- "time"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/console"
- "github.com/ethereum/go-ethereum/contracts/ens"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/internal/debug"
@@ -45,9 +41,9 @@ import (
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/swarm"
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
+ swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics"
"gopkg.in/urfave/cli.v1"
)
@@ -110,16 +106,11 @@ var (
Usage: "Swarm Syncing enabled (default true)",
EnvVar: SWARM_ENV_SYNC_ENABLE,
}
- EnsAPIFlag = cli.StringFlag{
+ EnsAPIFlag = cli.StringSliceFlag{
Name: "ens-api",
- Usage: "URL of the Ethereum API provider to use for ENS record lookups",
+ Usage: "ENS API endpoint for a TLD and with contract address, can be repeated, format [tld:][contract-addr@]url",
EnvVar: SWARM_ENV_ENS_API,
}
- EnsAddrFlag = cli.StringFlag{
- Name: "ens-addr",
- Usage: "ENS contract address (default is detected as testnet or mainnet using --ens-api)",
- EnvVar: SWARM_ENV_ENS_ADDR,
- }
SwarmApiFlag = cli.StringFlag{
Name: "bzzapi",
Usage: "Swarm HTTP endpoint",
@@ -156,6 +147,10 @@ var (
Name: "ethapi",
Usage: "DEPRECATED: please use --ens-api and --swap-api",
}
+ DeprecatedEnsAddrFlag = cli.StringFlag{
+ Name: "ens-addr",
+ Usage: "DEPRECATED: ENS contract address, please use --ens-api with contract address according to its format",
+ }
)
//declare a few constant error messages, useful for later error check comparisons in test
@@ -343,7 +338,6 @@ DEPRECATED: use 'swarm db clean'.
// bzzd-specific flags
CorsStringFlag,
EnsAPIFlag,
- EnsAddrFlag,
SwarmTomlConfigPathFlag,
SwarmConfigPathFlag,
SwarmSwapEnabledFlag,
@@ -363,11 +357,17 @@ DEPRECATED: use 'swarm db clean'.
SwarmUploadMimeType,
//deprecated flags
DeprecatedEthAPIFlag,
+ DeprecatedEnsAddrFlag,
}
app.Flags = append(app.Flags, debug.Flags...)
+ app.Flags = append(app.Flags, swarmmetrics.Flags...)
app.Before = func(ctx *cli.Context) error {
runtime.GOMAXPROCS(runtime.NumCPU())
- return debug.Setup(ctx)
+ if err := debug.Setup(ctx); err != nil {
+ return err
+ }
+ swarmmetrics.Setup(ctx)
+ return nil
}
app.After = func(ctx *cli.Context) error {
debug.Exit()
@@ -448,38 +448,6 @@ func bzzd(ctx *cli.Context) error {
return nil
}
-// detectEnsAddr determines the ENS contract address by getting both the
-// version and genesis hash using the client and matching them to either
-// mainnet or testnet addresses
-func detectEnsAddr(client *rpc.Client) (common.Address, error) {
- ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
- defer cancel()
-
- var version string
- if err := client.CallContext(ctx, &version, "net_version"); err != nil {
- return common.Address{}, err
- }
-
- block, err := ethclient.NewClient(client).BlockByNumber(ctx, big.NewInt(0))
- if err != nil {
- return common.Address{}, err
- }
-
- switch {
-
- case version == "1" && block.Hash() == params.MainnetGenesisHash:
- log.Info("using Mainnet ENS contract address", "addr", ens.MainNetAddress)
- return ens.MainNetAddress, nil
-
- case version == "3" && block.Hash() == params.TestnetGenesisHash:
- log.Info("using Testnet ENS contract address", "addr", ens.TestNetAddress)
- return ens.TestNetAddress, nil
-
- default:
- return common.Address{}, fmt.Errorf("unknown version and genesis hash: %s %s", version, block.Hash())
- }
-}
-
func registerBzzService(bzzconfig *bzzapi.Config, ctx *cli.Context, stack *node.Node) {
//define the swarm service boot function
@@ -494,27 +462,7 @@ func registerBzzService(bzzconfig *bzzapi.Config, ctx *cli.Context, stack *node.
}
}
- var ensClient *ethclient.Client
- if bzzconfig.EnsApi != "" {
- log.Info("connecting to ENS API", "url", bzzconfig.EnsApi)
- client, err := rpc.Dial(bzzconfig.EnsApi)
- if err != nil {
- return nil, fmt.Errorf("error connecting to ENS API %s: %s", bzzconfig.EnsApi, err)
- }
- ensClient = ethclient.NewClient(client)
-
- //no ENS root address set yet
- if bzzconfig.EnsRoot == (common.Address{}) {
- ensAddr, err := detectEnsAddr(client)
- if err == nil {
- bzzconfig.EnsRoot = ensAddr
- } else {
- log.Warn(fmt.Sprintf("could not determine ENS contract address, using default %s", bzzconfig.EnsRoot), "err", err)
- }
- }
- }
-
- return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, bzzconfig.SwapEnabled, bzzconfig.SyncEnabled, bzzconfig.Cors)
+ return swarm.NewSwarm(ctx, swapClient, bzzconfig)
}
//register within the ethereum node
if err := stack.Register(boot); err != nil {
diff --git a/cmd/swarm/manifest.go b/cmd/swarm/manifest.go
index aa276e0f9..41a69a5d0 100644
--- a/cmd/swarm/manifest.go
+++ b/cmd/swarm/manifest.go
@@ -35,7 +35,7 @@ const bzzManifestJSON = "application/bzz-manifest+json"
func add(ctx *cli.Context) {
args := ctx.Args()
if len(args) < 3 {
- utils.Fatalf("Need atleast three arguments <MHASH> <path> <HASH> [<content-type>]")
+ utils.Fatalf("Need at least three arguments <MHASH> <path> <HASH> [<content-type>]")
}
var (
@@ -69,7 +69,7 @@ func update(ctx *cli.Context) {
args := ctx.Args()
if len(args) < 3 {
- utils.Fatalf("Need atleast three arguments <MHASH> <path> <HASH>")
+ utils.Fatalf("Need at least three arguments <MHASH> <path> <HASH>")
}
var (
@@ -101,7 +101,7 @@ func update(ctx *cli.Context) {
func remove(ctx *cli.Context) {
args := ctx.Args()
if len(args) < 2 {
- utils.Fatalf("Need atleast two arguments <MHASH> <path>")
+ utils.Fatalf("Need at least two arguments <MHASH> <path>")
}
var (
diff --git a/cmd/swarm/run_test.go b/cmd/swarm/run_test.go
index ed1502868..594cfa55c 100644
--- a/cmd/swarm/run_test.go
+++ b/cmd/swarm/run_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
diff --git a/cmd/swarm/upload_test.go b/cmd/swarm/upload_test.go
index 5656186e1..df7fc216a 100644
--- a/cmd/swarm/upload_test.go
+++ b/cmd/swarm/upload_test.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// Copyright 2017 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go
index 23b10c2d7..186d18d8f 100644
--- a/cmd/utils/cmd.go
+++ b/cmd/utils/cmd.go
@@ -25,6 +25,7 @@ import (
"os/signal"
"runtime"
"strings"
+ "syscall"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
@@ -64,7 +65,7 @@ func StartNode(stack *node.Node) {
}
go func() {
sigc := make(chan os.Signal, 1)
- signal.Notify(sigc, os.Interrupt)
+ signal.Notify(sigc, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(sigc)
<-sigc
log.Info("Got interrupt, shutting down...")
@@ -85,7 +86,7 @@ func ImportChain(chain *core.BlockChain, fn string) error {
// If a signal is received, the import will stop at the next batch.
interrupt := make(chan os.Signal, 1)
stop := make(chan struct{})
- signal.Notify(interrupt, os.Interrupt)
+ signal.Notify(interrupt, syscall.SIGINT, syscall.SIGTERM)
defer signal.Stop(interrupt)
defer close(interrupt)
go func() {
@@ -116,7 +117,6 @@ func ImportChain(chain *core.BlockChain, fn string) error {
return err
}
}
-
stream := rlp.NewStream(reader, 0)
// Run actual the import.
@@ -150,25 +150,34 @@ func ImportChain(chain *core.BlockChain, fn string) error {
if checkInterrupt() {
return fmt.Errorf("interrupted")
}
- if hasAllBlocks(chain, blocks[:i]) {
+ missing := missingBlocks(chain, blocks[:i])
+ if len(missing) == 0 {
log.Info("Skipping batch as all blocks present", "batch", batch, "first", blocks[0].Hash(), "last", blocks[i-1].Hash())
continue
}
-
- if _, err := chain.InsertChain(blocks[:i]); err != nil {
+ if _, err := chain.InsertChain(missing); err != nil {
return fmt.Errorf("invalid block %d: %v", n, err)
}
}
return nil
}
-func hasAllBlocks(chain *core.BlockChain, bs []*types.Block) bool {
- for _, b := range bs {
- if !chain.HasBlock(b.Hash(), b.NumberU64()) {
- return false
+func missingBlocks(chain *core.BlockChain, blocks []*types.Block) []*types.Block {
+ head := chain.CurrentBlock()
+ for i, block := range blocks {
+ // If we're behind the chain head, only check block, state is available at head
+ if head.NumberU64() > block.NumberU64() {
+ if !chain.HasBlock(block.Hash(), block.NumberU64()) {
+ return blocks[i:]
+ }
+ continue
+ }
+ // If we're above the chain head, state availability is a must
+ if !chain.HasBlockAndState(block.Hash(), block.NumberU64()) {
+ return blocks[i:]
}
}
- return true
+ return nil
}
func ExportChain(blockchain *core.BlockChain, fn string) error {
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 89dcd230c..ff78a0fcc 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -170,7 +170,11 @@ var (
Usage: `Blockchain sync mode ("fast", "full", or "light")`,
Value: &defaultSyncMode,
}
-
+ GCModeFlag = cli.StringFlag{
+ Name: "gcmode",
+ Usage: `Blockchain garbage collection mode ("full", "archive")`,
+ Value: "full",
+ }
LightServFlag = cli.IntFlag{
Name: "lightserv",
Usage: "Maximum percentage of time allowed for serving LES requests (0-90)",
@@ -179,7 +183,7 @@ var (
LightPeersFlag = cli.IntFlag{
Name: "lightpeers",
Usage: "Maximum number of LES client peers",
- Value: 20,
+ Value: eth.DefaultConfig.LightPeers,
}
LightKDFFlag = cli.BoolFlag{
Name: "lightkdf",
@@ -205,11 +209,6 @@ var (
Usage: "Dashboard metrics collection refresh rate",
Value: dashboard.DefaultConfig.Refresh,
}
- DashboardAssetsFlag = cli.StringFlag{
- Name: "dashboard.assets",
- Usage: "Developer flag to serve the dashboard from the local file system",
- Value: dashboard.DefaultConfig.Assets,
- }
// Ethash settings
EthashCacheDirFlag = DirectoryFlag{
Name: "ethash.cachedir",
@@ -293,8 +292,18 @@ var (
// Performance tuning settings
CacheFlag = cli.IntFlag{
Name: "cache",
- Usage: "Megabytes of memory allocated to internal caching (min 16MB / database forced)",
- Value: 128,
+ Usage: "Megabytes of memory allocated to internal caching",
+ Value: 1024,
+ }
+ CacheDatabaseFlag = cli.IntFlag{
+ Name: "cache.database",
+ Usage: "Percentage of cache memory allowance to use for database io",
+ Value: 75,
+ }
+ CacheGCFlag = cli.IntFlag{
+ Name: "cache.gc",
+ Usage: "Percentage of cache memory allowance to use for trie pruning",
+ Value: 25,
}
TrieCacheGenFlag = cli.IntFlag{
Name: "trie-cache-gens",
@@ -383,6 +392,11 @@ var (
Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced)",
Value: "",
}
+ RPCVirtualHostsFlag = cli.StringFlag{
+ Name: "rpcvhosts",
+ Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard.",
+ Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
+ }
RPCApiFlag = cli.StringFlag{
Name: "rpcapi",
Usage: "API's offered over the HTTP-RPC interface",
@@ -612,7 +626,7 @@ func setBootstrapNodesV5(ctx *cli.Context, cfg *p2p.Config) {
urls = strings.Split(ctx.GlobalString(BootnodesFlag.Name), ",")
}
case ctx.GlobalBool(RinkebyFlag.Name):
- urls = params.RinkebyV5Bootnodes
+ urls = params.RinkebyBootnodes
case cfg.BootstrapNodesV5 != nil:
return // already set, don't apply defaults.
}
@@ -676,6 +690,9 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
if ctx.GlobalIsSet(RPCApiFlag.Name) {
cfg.HTTPModules = splitAndTrim(ctx.GlobalString(RPCApiFlag.Name))
}
+ if ctx.GlobalIsSet(RPCVirtualHostsFlag.Name) {
+ cfg.HTTPVirtualHosts = splitAndTrim(ctx.GlobalString(RPCVirtualHostsFlag.Name))
+ }
}
// setWS creates the WebSocket RPC listener interface string from the set
@@ -714,13 +731,15 @@ func setIPC(ctx *cli.Context, cfg *node.Config) {
// makeDatabaseHandles raises out the number of allowed file handles per process
// for Geth and returns half of the allowance to assign to the database.
func makeDatabaseHandles() int {
- if err := fdlimit.Raise(2048); err != nil {
- Fatalf("Failed to raise file descriptor allowance: %v", err)
- }
limit, err := fdlimit.Current()
if err != nil {
Fatalf("Failed to retrieve file descriptor allowance: %v", err)
}
+ if limit < 2048 {
+ if err := fdlimit.Raise(2048); err != nil {
+ Fatalf("Failed to raise file descriptor allowance: %v", err)
+ }
+ }
if limit > 2048 { // cap database file descriptors even if more is available
limit = 2048
}
@@ -789,20 +808,43 @@ func SetP2PConfig(ctx *cli.Context, cfg *p2p.Config) {
setBootstrapNodes(ctx, cfg)
setBootstrapNodesV5(ctx, cfg)
+ lightClient := ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalString(SyncModeFlag.Name) == "light"
+ lightServer := ctx.GlobalInt(LightServFlag.Name) != 0
+ lightPeers := ctx.GlobalInt(LightPeersFlag.Name)
+
if ctx.GlobalIsSet(MaxPeersFlag.Name) {
cfg.MaxPeers = ctx.GlobalInt(MaxPeersFlag.Name)
+ if lightServer && !ctx.GlobalIsSet(LightPeersFlag.Name) {
+ cfg.MaxPeers += lightPeers
+ }
+ } else {
+ if lightServer {
+ cfg.MaxPeers += lightPeers
+ }
+ if lightClient && ctx.GlobalIsSet(LightPeersFlag.Name) && cfg.MaxPeers < lightPeers {
+ cfg.MaxPeers = lightPeers
+ }
}
+ if !(lightClient || lightServer) {
+ lightPeers = 0
+ }
+ ethPeers := cfg.MaxPeers - lightPeers
+ if lightClient {
+ ethPeers = 0
+ }
+ log.Info("Maximum peer count", "ETH", ethPeers, "LES", lightPeers, "total", cfg.MaxPeers)
+
if ctx.GlobalIsSet(MaxPendingPeersFlag.Name) {
cfg.MaxPendingPeers = ctx.GlobalInt(MaxPendingPeersFlag.Name)
}
- if ctx.GlobalIsSet(NoDiscoverFlag.Name) || ctx.GlobalBool(LightModeFlag.Name) {
+ if ctx.GlobalIsSet(NoDiscoverFlag.Name) || lightClient {
cfg.NoDiscovery = true
}
// if we're running a light client or server, force enable the v5 peer discovery
// unless it is explicitly disabled with --nodiscover note that explicitly specifying
// --v5disc overrides --nodiscover, in which case the later only disables v4 discovery
- forceV5Discovery := (ctx.GlobalBool(LightModeFlag.Name) || ctx.GlobalInt(LightServFlag.Name) > 0) && !ctx.GlobalBool(NoDiscoverFlag.Name)
+ forceV5Discovery := (lightClient || lightServer) && !ctx.GlobalBool(NoDiscoverFlag.Name)
if ctx.GlobalIsSet(DiscoveryV5Flag.Name) {
cfg.DiscoveryV5 = ctx.GlobalBool(DiscoveryV5Flag.Name)
} else if forceV5Discovery {
@@ -999,11 +1041,19 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
cfg.NetworkId = ctx.GlobalUint64(NetworkIdFlag.Name)
}
- if ctx.GlobalIsSet(CacheFlag.Name) {
- cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name)
+ if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheDatabaseFlag.Name) {
+ cfg.DatabaseCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
}
cfg.DatabaseHandles = makeDatabaseHandles()
+ if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
+ Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
+ }
+ cfg.NoPruning = ctx.GlobalString(GCModeFlag.Name) == "archive"
+
+ if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
+ cfg.TrieCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
+ }
if ctx.GlobalIsSet(MinerThreadsFlag.Name) {
cfg.MinerThreads = ctx.GlobalInt(MinerThreadsFlag.Name)
}
@@ -1068,7 +1118,6 @@ func SetDashboardConfig(ctx *cli.Context, cfg *dashboard.Config) {
cfg.Host = ctx.GlobalString(DashboardAddrFlag.Name)
cfg.Port = ctx.GlobalInt(DashboardPortFlag.Name)
cfg.Refresh = ctx.GlobalDuration(DashboardRefreshFlag.Name)
- cfg.Assets = ctx.GlobalString(DashboardAssetsFlag.Name)
}
// RegisterEthService adds an Ethereum client to the stack.
@@ -1135,7 +1184,7 @@ func SetupNetwork(ctx *cli.Context) {
// MakeChainDatabase open an LevelDB using the flags passed to the client and will hard crash if it fails.
func MakeChainDatabase(ctx *cli.Context, stack *node.Node) ethdb.Database {
var (
- cache = ctx.GlobalInt(CacheFlag.Name)
+ cache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheDatabaseFlag.Name) / 100
handles = makeDatabaseHandles()
)
name := "chaindata"
@@ -1187,8 +1236,19 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
})
}
}
+ if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
+ Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
+ }
+ cache := &core.CacheConfig{
+ Disabled: ctx.GlobalString(GCModeFlag.Name) == "archive",
+ TrieNodeLimit: eth.DefaultConfig.TrieCache,
+ TrieTimeLimit: eth.DefaultConfig.TrieTimeout,
+ }
+ if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
+ cache.TrieNodeLimit = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
+ }
vmcfg := vm.Config{EnablePreimageRecording: ctx.GlobalBool(VMEnableDebugFlag.Name)}
- chain, err = core.NewBlockChain(chainDb, config, engine, vmcfg)
+ chain, err = core.NewBlockChain(chainDb, cache, config, engine, vmcfg)
if err != nil {
Fatalf("Can't create BlockChain: %v", err)
}
diff --git a/cmd/wnode/main.go b/cmd/wnode/main.go
index 05e6b2908..988c50ce3 100644
--- a/cmd/wnode/main.go
+++ b/cmd/wnode/main.go
@@ -22,6 +22,7 @@ package main
import (
"bufio"
"crypto/ecdsa"
+ crand "crypto/rand"
"crypto/sha512"
"encoding/binary"
"encoding/hex"
@@ -43,11 +44,12 @@ import (
"github.com/ethereum/go-ethereum/p2p/discover"
"github.com/ethereum/go-ethereum/p2p/nat"
"github.com/ethereum/go-ethereum/whisper/mailserver"
- whisper "github.com/ethereum/go-ethereum/whisper/whisperv5"
+ whisper "github.com/ethereum/go-ethereum/whisper/whisperv6"
"golang.org/x/crypto/pbkdf2"
)
const quitCommand = "~Q"
+const entropySize = 32
// singletons
var (
@@ -55,33 +57,37 @@ var (
shh *whisper.Whisper
done chan struct{}
mailServer mailserver.WMailServer
+ entropy [entropySize]byte
input = bufio.NewReader(os.Stdin)
)
// encryption
var (
- symKey []byte
- pub *ecdsa.PublicKey
- asymKey *ecdsa.PrivateKey
- nodeid *ecdsa.PrivateKey
- topic whisper.TopicType
- asymKeyID string
- filterID string
- symPass string
- msPassword string
+ symKey []byte
+ pub *ecdsa.PublicKey
+ asymKey *ecdsa.PrivateKey
+ nodeid *ecdsa.PrivateKey
+ topic whisper.TopicType
+
+ asymKeyID string
+ asymFilterID string
+ symFilterID string
+ symPass string
+ msPassword string
)
// cmd arguments
var (
- bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't actively connect to peers, wait for incoming connections")
- forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither send nor decrypt messages")
+ bootstrapMode = flag.Bool("standalone", false, "boostrap node: don't initiate connection to peers, just wait for incoming connections")
+ forwarderMode = flag.Bool("forwarder", false, "forwarder mode: only forward messages, neither encrypt nor decrypt messages")
mailServerMode = flag.Bool("mailserver", false, "mail server mode: delivers expired messages on demand")
requestMail = flag.Bool("mailclient", false, "request expired messages from the bootstrap server")
asymmetricMode = flag.Bool("asym", false, "use asymmetric encryption")
generateKey = flag.Bool("generatekey", false, "generate and show the private key")
fileExMode = flag.Bool("fileexchange", false, "file exchange mode")
- testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics")
+ fileReader = flag.Bool("filereader", false, "load and decrypt messages saved as files, display as plain text")
+ testMode = flag.Bool("test", false, "use of predefined parameters for diagnostics (password, etc.)")
echoMode = flag.Bool("echo", false, "echo mode: prints some arguments for diagnostics")
argVerbosity = flag.Int("verbosity", int(log.LvlError), "log verbosity level")
@@ -97,13 +103,14 @@ var (
argIDFile = flag.String("idfile", "", "file name with node id (private key)")
argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)")
argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)")
- argSaveDir = flag.String("savedir", "", "directory where incoming messages will be saved as files")
+ argSaveDir = flag.String("savedir", "", "directory where all incoming messages will be saved as files")
)
func main() {
processArgs()
initialize()
run()
+ shutdown()
}
func processArgs() {
@@ -190,6 +197,8 @@ func initialize() {
if len(*argIP) == 0 {
argIP = scanLineA("Please enter your IP and port (e.g. 127.0.0.1:30348): ")
}
+ } else if *fileReader {
+ *bootstrapMode = true
} else {
if len(*argEnode) == 0 {
argEnode = scanLineA("Please enter the peer's enode: ")
@@ -198,11 +207,6 @@ func initialize() {
peers = append(peers, peer)
}
- cfg := &whisper.Config{
- MaxMessageSize: uint32(*argMaxSize),
- MinimumAcceptedPOW: *argPoW,
- }
-
if *mailServerMode {
if len(msPassword) == 0 {
msPassword, err = console.Stdin.PromptPassword("Please enter the Mail Server password: ")
@@ -210,14 +214,15 @@ func initialize() {
utils.Fatalf("Failed to read Mail Server password: %s", err)
}
}
+ }
- shh = whisper.New(cfg)
- shh.RegisterServer(&mailServer)
- mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
- } else {
- shh = whisper.New(cfg)
+ cfg := &whisper.Config{
+ MaxMessageSize: uint32(*argMaxSize),
+ MinimumAcceptedPOW: *argPoW,
}
+ shh = whisper.New(cfg)
+
if *argPoW != whisper.DefaultMinimumPoW {
err := shh.SetMinimumPoW(*argPoW)
if err != nil {
@@ -259,11 +264,21 @@ func initialize() {
maxPeers = 800
}
+ _, err = crand.Read(entropy[:])
+ if err != nil {
+ utils.Fatalf("crypto/rand failed: %s", err)
+ }
+
+ if *mailServerMode {
+ shh.RegisterServer(&mailServer)
+ mailServer.Init(shh, *argDBPath, msPassword, *argServerPoW)
+ }
+
server = &p2p.Server{
Config: p2p.Config{
PrivateKey: nodeid,
MaxPeers: maxPeers,
- Name: common.MakeName("wnode", "5.0"),
+ Name: common.MakeName("wnode", "6.0"),
Protocols: shh.Protocols(),
ListenAddr: *argIP,
NAT: nat.Any(),
@@ -274,10 +289,11 @@ func initialize() {
}
}
-func startServer() {
+func startServer() error {
err := server.Start()
if err != nil {
- utils.Fatalf("Failed to start Whisper peer: %s.", err)
+ fmt.Printf("Failed to start Whisper peer: %s.", err)
+ return err
}
fmt.Printf("my public key: %s \n", common.ToHex(crypto.FromECDSAPub(&asymKey.PublicKey)))
@@ -293,9 +309,14 @@ func startServer() {
configureNode()
}
- if !*forwarderMode {
+ if *fileExMode {
+ fmt.Printf("Please type the file name to be send. To quit type: '%s'\n", quitCommand)
+ } else if *fileReader {
+ fmt.Printf("Please type the file name to be decrypted. To quit type: '%s'\n", quitCommand)
+ } else if !*forwarderMode {
fmt.Printf("Please type the message. To quit type: '%s'\n", quitCommand)
}
+ return nil
}
func isKeyValid(k *ecdsa.PublicKey) bool {
@@ -363,13 +384,22 @@ func configureNode() {
}
}
- filter := whisper.Filter{
+ symFilter := whisper.Filter{
KeySym: symKey,
+ Topics: [][]byte{topic[:]},
+ AllowP2P: p2pAccept,
+ }
+ symFilterID, err = shh.Subscribe(&symFilter)
+ if err != nil {
+ utils.Fatalf("Failed to install filter: %s", err)
+ }
+
+ asymFilter := whisper.Filter{
KeyAsym: asymKey,
Topics: [][]byte{topic[:]},
AllowP2P: p2pAccept,
}
- filterID, err = shh.Subscribe(&filter)
+ asymFilterID, err = shh.Subscribe(&asymFilter)
if err != nil {
utils.Fatalf("Failed to install filter: %s", err)
}
@@ -400,8 +430,10 @@ func waitForConnection(timeout bool) {
}
func run() {
- defer mailServer.Close()
- startServer()
+ err := startServer()
+ if err != nil {
+ return
+ }
defer server.Stop()
shh.Start(nil)
defer shh.Stop()
@@ -414,21 +446,26 @@ func run() {
requestExpiredMessagesLoop()
} else if *fileExMode {
sendFilesLoop()
+ } else if *fileReader {
+ fileReaderLoop()
} else {
sendLoop()
}
}
+func shutdown() {
+ close(done)
+ mailServer.Close()
+}
+
func sendLoop() {
for {
s := scanLine("")
if s == quitCommand {
fmt.Println("Quit command received")
- close(done)
- break
+ return
}
sendMsg([]byte(s))
-
if *asymmetricMode {
// print your own message for convenience,
// because in asymmetric mode it is impossible to decrypt it
@@ -444,13 +481,11 @@ func sendFilesLoop() {
s := scanLine("")
if s == quitCommand {
fmt.Println("Quit command received")
- close(done)
- break
+ return
}
b, err := ioutil.ReadFile(s)
if err != nil {
fmt.Printf(">>> Error: %s \n", err)
- continue
} else {
h := sendMsg(b)
if (h == common.Hash{}) {
@@ -464,6 +499,38 @@ func sendFilesLoop() {
}
}
+func fileReaderLoop() {
+ watcher1 := shh.GetFilter(symFilterID)
+ watcher2 := shh.GetFilter(asymFilterID)
+ if watcher1 == nil && watcher2 == nil {
+ fmt.Println("Error: neither symmetric nor asymmetric filter is installed")
+ return
+ }
+
+ for {
+ s := scanLine("")
+ if s == quitCommand {
+ fmt.Println("Quit command received")
+ return
+ }
+ raw, err := ioutil.ReadFile(s)
+ if err != nil {
+ fmt.Printf(">>> Error: %s \n", err)
+ } else {
+ env := whisper.Envelope{Data: raw} // the topic is zero
+ msg := env.Open(watcher1) // force-open envelope regardless of the topic
+ if msg == nil {
+ msg = env.Open(watcher2)
+ }
+ if msg == nil {
+ fmt.Printf(">>> Error: failed to decrypt the message \n")
+ } else {
+ printMessageInfo(msg)
+ }
+ }
+ }
+}
+
func scanLine(prompt string) string {
if len(prompt) > 0 {
fmt.Print(prompt)
@@ -506,6 +573,7 @@ func sendMsg(payload []byte) common.Hash {
if err != nil {
utils.Fatalf("failed to create new message: %s", err)
}
+
envelope, err := msg.Wrap(&params)
if err != nil {
fmt.Printf("failed to seal message: %v \n", err)
@@ -522,9 +590,14 @@ func sendMsg(payload []byte) common.Hash {
}
func messageLoop() {
- f := shh.GetFilter(filterID)
- if f == nil {
- utils.Fatalf("filter is not installed")
+ sf := shh.GetFilter(symFilterID)
+ if sf == nil {
+ utils.Fatalf("symmetric filter is not installed")
+ }
+
+ af := shh.GetFilter(asymFilterID)
+ if af == nil {
+ utils.Fatalf("asymmetric filter is not installed")
}
ticker := time.NewTicker(time.Millisecond * 50)
@@ -532,12 +605,21 @@ func messageLoop() {
for {
select {
case <-ticker.C:
- messages := f.Retrieve()
+ m1 := sf.Retrieve()
+ m2 := af.Retrieve()
+ messages := append(m1, m2...)
for _, msg := range messages {
- if *fileExMode || len(msg.Payload) > 2048 {
- writeMessageToFile(*argSaveDir, msg)
- } else {
+ reportedOnce := false
+ if !*fileExMode && len(msg.Payload) <= 2048 {
printMessageInfo(msg)
+ reportedOnce = true
+ }
+
+ // All messages are saved upon specifying argSaveDir.
+ // fileExMode only specifies how messages are displayed on the console after they are saved.
+ // if fileExMode == true, only the hashes are displayed, since messages might be too big.
+ if len(*argSaveDir) > 0 {
+ writeMessageToFile(*argSaveDir, msg, !reportedOnce)
}
}
case <-done:
@@ -562,7 +644,11 @@ func printMessageInfo(msg *whisper.ReceivedMessage) {
}
}
-func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) {
+func writeMessageToFile(dir string, msg *whisper.ReceivedMessage, show bool) {
+ if len(dir) == 0 {
+ return
+ }
+
timestamp := fmt.Sprintf("%d", msg.Sent)
name := fmt.Sprintf("%x", msg.EnvelopeHash)
@@ -571,27 +657,32 @@ func writeMessageToFile(dir string, msg *whisper.ReceivedMessage) {
address = crypto.PubkeyToAddress(*msg.Src)
}
- if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
- // message from myself: don't save, only report
- fmt.Printf("\n%s <%x>: message received: '%s'\n", timestamp, address, name)
- } else if len(dir) > 0 {
- fullpath := filepath.Join(dir, name)
- err := ioutil.WriteFile(fullpath, msg.Payload, 0644)
- if err != nil {
- fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err)
- } else {
- fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(msg.Payload))
- }
- } else {
- fmt.Printf("\n%s {%x}: big message received (%d bytes), but not saved: %s\n", timestamp, address, len(msg.Payload), name)
+ env := shh.GetEnvelope(msg.EnvelopeHash)
+ if env == nil {
+ fmt.Printf("\nUnexpected error: envelope not found: %x\n", msg.EnvelopeHash)
+ return
+ }
+
+ // this is a sample code; uncomment if you don't want to save your own messages.
+ //if whisper.IsPubKeyEqual(msg.Src, &asymKey.PublicKey) {
+ // fmt.Printf("\n%s <%x>: message from myself received, not saved: '%s'\n", timestamp, address, name)
+ // return
+ //}
+
+ fullpath := filepath.Join(dir, name)
+ err := ioutil.WriteFile(fullpath, env.Data, 0644)
+ if err != nil {
+ fmt.Printf("\n%s {%x}: message received but not saved: %s\n", timestamp, address, err)
+ } else if show {
+ fmt.Printf("\n%s {%x}: message received and saved as '%s' (%d bytes)\n", timestamp, address, name, len(env.Data))
}
}
func requestExpiredMessagesLoop() {
- var key, peerID []byte
+ var key, peerID, bloom []byte
var timeLow, timeUpp uint32
var t string
- var xt, empty whisper.TopicType
+ var xt whisper.TopicType
keyID, err := shh.AddSymKeyFromPassword(msPassword)
if err != nil {
@@ -601,37 +692,43 @@ func requestExpiredMessagesLoop() {
if err != nil {
utils.Fatalf("Failed to save symmetric key for mail request: %s", err)
}
- peerID = extractIdFromEnode(*argEnode)
+ peerID = extractIDFromEnode(*argEnode)
shh.AllowP2PMessagesFromPeer(peerID)
for {
timeLow = scanUint("Please enter the lower limit of the time range (unix timestamp): ")
timeUpp = scanUint("Please enter the upper limit of the time range (unix timestamp): ")
- t = scanLine("Please enter the topic (hexadecimal): ")
- if len(t) >= whisper.TopicLength*2 {
+ t = scanLine("Enter the topic (hex). Press enter to request all messages, regardless of the topic: ")
+ if len(t) == whisper.TopicLength*2 {
x, err := hex.DecodeString(t)
if err != nil {
- utils.Fatalf("Failed to parse the topic: %s", err)
+ fmt.Printf("Failed to parse the topic: %s \n", err)
+ continue
}
xt = whisper.BytesToTopic(x)
+ bloom = whisper.TopicToBloom(xt)
+ obfuscateBloom(bloom)
+ } else if len(t) == 0 {
+ bloom = whisper.MakeFullNodeBloom()
+ } else {
+ fmt.Println("Error: topic is invalid, request aborted")
+ continue
}
+
if timeUpp == 0 {
timeUpp = 0xFFFFFFFF
}
- data := make([]byte, 8+whisper.TopicLength)
+ data := make([]byte, 8, 8+whisper.BloomFilterSize)
binary.BigEndian.PutUint32(data, timeLow)
binary.BigEndian.PutUint32(data[4:], timeUpp)
- copy(data[8:], xt[:])
- if xt == empty {
- data = data[:8]
- }
+ data = append(data, bloom...)
var params whisper.MessageParams
params.PoW = *argServerPoW
params.Payload = data
params.KeySym = key
- params.Src = nodeid
+ params.Src = asymKey
params.WorkTime = 5
msg, err := whisper.NewSentMessage(&params)
@@ -652,10 +749,27 @@ func requestExpiredMessagesLoop() {
}
}
-func extractIdFromEnode(s string) []byte {
+func extractIDFromEnode(s string) []byte {
n, err := discover.ParseNode(s)
if err != nil {
utils.Fatalf("Failed to parse enode: %s", err)
}
return n.ID[:]
}
+
+// obfuscateBloom adds 16 random bits to the the bloom
+// filter, in order to obfuscate the containing topics.
+// it does so deterministically within every session.
+// despite additional bits, it will match on average
+// 32000 times less messages than full node's bloom filter.
+func obfuscateBloom(bloom []byte) {
+ const half = entropySize / 2
+ for i := 0; i < half; i++ {
+ x := int(entropy[i])
+ if entropy[half+i] < 128 {
+ x += 256
+ }
+
+ bloom[x/8] = 1 << uint(x%8) // set the bit number X
+ }
+}