diff --git a/cmd/swarm/swarm-snapshot/create.go b/cmd/swarm/swarm-snapshot/create.go
new file mode 100644
index 0000000000..7526c7d8c3
--- /dev/null
+++ b/cmd/swarm/swarm-snapshot/create.go
@@ -0,0 +1,165 @@
+// 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
+// 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 .
+
+package main
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/cmd/utils"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/node"
+ "github.com/ethereum/go-ethereum/p2p/enode"
+ "github.com/ethereum/go-ethereum/p2p/simulations"
+ "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
+ "github.com/ethereum/go-ethereum/swarm/network"
+ "github.com/ethereum/go-ethereum/swarm/network/simulation"
+ cli "gopkg.in/urfave/cli.v1"
+)
+
+const testMinProxBinSize = 2
+const NoConnectionTimeout = 2 * time.Second
+
+func create(ctx *cli.Context) error {
+ log.PrintOrigins(true)
+ log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
+
+ if len(ctx.Args()) < 1 {
+ return errors.New("argument should be the filename to verify or write-to")
+ }
+ filename, err := touchPath(ctx.Args()[0])
+ if err != nil {
+ return err
+ }
+ err = discoverySnapshot(filename, 10)
+ if err != nil {
+ utils.Fatalf("Simulation failed: %s", err)
+ }
+
+ return err
+}
+
+func discoverySnapshot(filename string, nodes int) error {
+ //disable discovery if topology is specified
+ discovery = topology == ""
+ log.Debug("discoverySnapshot", "filename", filename, "nodes", nodes, "discovery", discovery)
+ i := 0
+ var lock sync.Mutex
+ var pivotNodeID enode.ID
+ sim := simulation.New(map[string]simulation.ServiceFunc{
+ "bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
+ lock.Lock()
+ i++
+ if i == pivot {
+ pivotNodeID = ctx.Config.ID
+ }
+ lock.Unlock()
+
+ addr := network.NewAddr(ctx.Config.Node())
+ kp := network.NewKadParams()
+ kp.MinProxBinSize = testMinProxBinSize
+
+ kad := network.NewKademlia(addr.Over(), kp)
+ hp := network.NewHiveParams()
+ hp.KeepAliveInterval = time.Duration(200) * time.Millisecond
+ hp.Discovery = discovery
+
+ config := &network.BzzConfig{
+ OverlayAddr: addr.Over(),
+ UnderlayAddr: addr.Under(),
+ HiveParams: hp,
+ }
+ return network.NewBzz(config, kad, nil, nil, nil), nil, nil
+ },
+ })
+ defer sim.Close()
+
+ _, err := sim.AddNodes(10)
+ if err != nil {
+ utils.Fatalf("%v", err)
+ }
+
+ events := make(chan *simulations.Event)
+ sub := sim.Net.Events().Subscribe(events)
+ select {
+ case ev := <-events:
+ //only catch node up events
+ if ev.Type == simulations.EventTypeConn {
+ utils.Fatalf("this shouldn't happen as connections weren't initiated yet")
+ }
+ case <-time.After(NoConnectionTimeout):
+ }
+
+ sub.Unsubscribe()
+
+ if len(sim.Net.Conns) > 0 {
+ utils.Fatalf("no connections should exist after just adding nodes")
+ }
+
+ err := sim.Net.ConnectNodesRing(nil)
+ if err != nil {
+ utils.Fatalf("had an error connecting the nodes in a %v topology: %v", topology, err)
+ }
+
+ if discovery {
+ ctx, cancelSimRun := context.WithTimeout(context.Background(), 2*time.Minute)
+ defer cancelSimRun()
+
+ if _, err := sim.WaitTillHealthy(ctx, 2); err != nil {
+ utils.Fatalf("%v", err)
+ }
+ }
+
+ var snap *simulations.Snapshot
+ if len(services) > 0 {
+ var addServices []string
+ var removeServices []string
+ for _, osvc := range strings.Split(services, ",") {
+ if strings.Index(osvc, "+") == 0 {
+ addServices = append(addServices, osvc[1:])
+ } else if strings.Index(osvc, "-") == 0 {
+ removeServices = append(removeServices, osvc[1:])
+ } else {
+ panic("stick to the rules, you know what they are")
+ }
+ }
+ snap, err = sim.Net.SnapshotWithServices(addServices, removeServices)
+ } else {
+ snap, err = sim.Net.Snapshot()
+ }
+
+ if err != nil {
+ return errors.New("no shapshot dude")
+ }
+ jsonsnapshot, err := json.Marshal(snap)
+ if err != nil {
+ return fmt.Errorf("corrupt json snapshot: %v", err)
+ }
+ err = ioutil.WriteFile(filename, jsonsnapshot, 0666)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/cmd/swarm/swarm-snapshot/create_test.go b/cmd/swarm/swarm-snapshot/create_test.go
new file mode 100644
index 0000000000..d6f593913b
--- /dev/null
+++ b/cmd/swarm/swarm-snapshot/create_test.go
@@ -0,0 +1,75 @@
+// 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
+// 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 .
+
+package main
+
+import (
+ "io/ioutil"
+ "os"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/log"
+)
+
+func init() {
+ log.PrintOrigins(true)
+ log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
+}
+
+//TestSnapshotCreate is a high level e2e test that tests for snapshot generation
+func TestSnapshotCreate(t *testing.T) {
+
+ for _, v := range []struct {
+ name string
+ args []string
+ }{
+ {
+ name: "no topology - discovery enabled",
+ args: []string{
+ "c",
+ },
+ },
+ {
+ name: "yes topology - discovery disabled",
+ args: []string{
+ "--topology",
+ "ring",
+ "c",
+ },
+ },
+ } {
+ t.Run(v.name, func(t *testing.T) {
+ file, err := ioutil.TempFile("", "swarm-snapshot")
+ defer os.Remove(file.Name())
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ file.Close()
+ snap := runSnapshot(t, append(v.args, file.Name())...)
+
+ snap.ExpectExit()
+ if snap.ExitStatus() != 0 {
+ t.Fatal("expected exit code 0")
+ }
+
+ _, err = os.Stat(file.Name())
+ if err != nil {
+ t.Fatal("could not stat snapshot json")
+ }
+ })
+ }
+}
diff --git a/cmd/swarm/swarm-snapshot/helper.go b/cmd/swarm/swarm-snapshot/helper.go
new file mode 100644
index 0000000000..8c4fab928d
--- /dev/null
+++ b/cmd/swarm/swarm-snapshot/helper.go
@@ -0,0 +1,40 @@
+package main
+
+import (
+ "os"
+ "path"
+ "path/filepath"
+)
+
+func touchPath(filename string) (string, error) {
+ if path.IsAbs(filename) {
+ if _, err := os.Stat(filename); err == nil {
+ // path exists, we will override the file
+ return filename, nil
+ }
+ }
+
+ d, f := path.Split(filename)
+ dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
+ if err != nil {
+ return "", err
+ }
+
+ _, err = os.Stat(path.Join(dir, filename))
+ if err == nil {
+ // path exists, we will override
+ return filename, nil
+ }
+
+ dirPath := path.Join(dir, d)
+ filePath := path.Join(dirPath, f)
+ if d != "" {
+ err = os.MkdirAll(dirPath, os.ModeDir)
+ if err != nil {
+ return "", err
+ }
+ }
+
+ filename = filePath
+ return filename, nil
+}
diff --git a/cmd/swarm/swarm-snapshot/main.go b/cmd/swarm/swarm-snapshot/main.go
new file mode 100644
index 0000000000..6338b2886b
--- /dev/null
+++ b/cmd/swarm/swarm-snapshot/main.go
@@ -0,0 +1,110 @@
+// 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
+// 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 .
+
+package main
+
+import (
+ "os"
+ "sort"
+
+ "github.com/ethereum/go-ethereum/cmd/utils"
+ "github.com/ethereum/go-ethereum/log"
+ cli "gopkg.in/urfave/cli.v1"
+)
+
+var (
+ gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
+)
+
+var (
+ topology string
+ services string
+ pivot int
+ nodes int
+ verbosity int
+)
+
+var app = utils.NewApp("", "Swarm Snapshot Util")
+var discovery = true
+
+func init() {
+ log.PrintOrigins(true)
+ log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(verbosity), log.StreamHandler(os.Stdout, log.TerminalFormat(true))))
+
+ app.Name = "swarm-snapshot"
+ app.Usage = ""
+
+ app.Flags = []cli.Flag{
+ cli.StringFlag{
+ Name: "topology",
+ Value: "chain",
+ Usage: "the desired topology to connect the nodes in (star, ring, chain, full)",
+ Destination: &topology,
+ },
+ cli.IntFlag{
+ Name: "pivot",
+ Value: 0,
+ Usage: "pivot node zero-index",
+ Destination: &pivot,
+ },
+ cli.IntFlag{
+ Name: "nodes",
+ Value: 10,
+ Usage: "swarm nodes",
+ Destination: &nodes,
+ },
+ cli.IntFlag{
+ Name: "verbosity",
+ Value: 1,
+ Usage: "verbosity",
+ Destination: &verbosity,
+ },
+ cli.StringFlag{
+ Name: "services",
+ Value: "",
+ Usage: "comma separated list of services to boot the nodes with",
+ Destination: &services,
+ },
+ }
+
+ app.Commands = []cli.Command{
+ {
+ Name: "create",
+ Aliases: []string{"c"},
+ Usage: "create a swarm snapshot",
+ Action: create,
+ },
+ {
+ Name: "verify",
+ Aliases: []string{"v"},
+ Usage: "verify a swarm snapshot",
+ Action: verify,
+ },
+ }
+
+ sort.Sort(cli.FlagsByName(app.Flags))
+ sort.Sort(cli.CommandsByName(app.Commands))
+}
+
+func main() {
+ err := app.Run(os.Args)
+ if err != nil {
+ log.Error(err.Error())
+
+ os.Exit(1)
+ }
+ os.Exit(0)
+}
diff --git a/cmd/swarm/swarm-snapshot/run_test.go b/cmd/swarm/swarm-snapshot/run_test.go
new file mode 100644
index 0000000000..3a66ac987f
--- /dev/null
+++ b/cmd/swarm/swarm-snapshot/run_test.go
@@ -0,0 +1,49 @@
+// 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
+// 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 .
+
+package main
+
+import (
+ "fmt"
+ "os"
+ "testing"
+
+ "github.com/docker/docker/pkg/reexec"
+ "github.com/ethereum/go-ethereum/internal/cmdtest"
+)
+
+func init() {
+ reexec.Register("swarm-snapshot", func() {
+ if err := app.Run(os.Args); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ os.Exit(0)
+ })
+}
+
+func runSnapshot(t *testing.T, args ...string) *cmdtest.TestCmd {
+ tt := cmdtest.NewTestCmd(t, nil)
+ tt.Run("swarm-snapshot", args...)
+ return tt
+}
+
+func TestMain(m *testing.M) {
+ if reexec.Init() {
+ return
+ }
+ os.Exit(m.Run())
+}
diff --git a/cmd/swarm/swarm-snapshot/testdata/snapshot.json b/cmd/swarm/swarm-snapshot/testdata/snapshot.json
new file mode 100644
index 0000000000..dd6e49dbe1
--- /dev/null
+++ b/cmd/swarm/swarm-snapshot/testdata/snapshot.json
@@ -0,0 +1 @@
+{"nodes":[{"node":{"config":{"id":"a3790562c8209b7e96e7abef9ad28de2e470a01927234e4848966e0cd15652e9","private_key":"3ff3a0c7dc8b63cac9255233d84cccad53d61f8c9e47539329c07e4f248fc6dd","name":"node_279f0a191892115b8db4e2689cc5e1bc19019e8f5228f4f093f35c48fa2f606fd00a2ec5cf0fa4657ad3606e14643c3be8d49350ad2673f2ad12e17c0174b0d8","services":["bzz"],"enable_msg_events":true,"port":62851},"up":true}},{"node":{"config":{"id":"4ce65548fc8490c02e4c6c0549be7329a8a05c090d62870ad1075e52e204a3b5","private_key":"1ab5fd3f1885661af1829b42683ca1379ca90b1b2c5a0132027346c74001a154","name":"node_2c3d17a3b019d9d32922f06aef03ac03f67777e382f0b86c6e4dac07e590f2dbf4cbd1cc7ed136b52bba897e7163dee926caa627111db6d948581ad1b2edbc3f","services":["bzz"],"enable_msg_events":true,"port":62852},"up":true}},{"node":{"config":{"id":"8bf97ec29b6b3ca638ed82fa30d03d0d8309c9e8f1db2071f4a37de805327108","private_key":"a90fea5197ac84a162db7a06ec739e075ee334026e792d83a3269526888c8b5d","name":"node_6e327c2d9d3ffdae707ed067d15b83ad0ea81b96bb2a07331436f01c1e194ed90f630ef0eb6511f4849ede30c403e3c4ff3c2cb8021317de97f3ed5b1d990f49","services":["bzz"],"enable_msg_events":true,"port":62853},"up":true}},{"node":{"config":{"id":"9121c3fea7ccc99775fb5ade460f7a0cb76003231d13d44ed3e4f3cca1947227","private_key":"76b6f09c28f5568e7724e9850116bdbd6356152fefedc4b0a9747361d712964b","name":"node_9aa1df8ddddd035e81ffc950f063714c3b6be24013108f8b99c32c25fc3a8582904f68c73fe63ebfb96143a12ab398f36c4070a3055c039491ca2f20be6774c2","services":["bzz"],"enable_msg_events":true,"port":62854},"up":true}},{"node":{"config":{"id":"fb555885b96d5742a10e688bd7c1bb842f0434c319bf076d07f545e4c0550601","private_key":"f7e7d60abb7d43d973131804e684ec99e77e81bf38165dda29f3ae026eb169f1","name":"node_a1226991909b92dd4f22150b798e2b6312ffc62bad7a47d250e811c7e60cf5ceb6a87998f295e0bd392c0f5c15213115000781e498dd7eae79b67c7e7e19cf81","services":["bzz"],"enable_msg_events":true,"port":62855},"up":true}},{"node":{"config":{"id":"f64d3106d2bb99e69e26c07de168f7fceaf85672859f7c1c038fd4dd6b6d7588","private_key":"f88cd88c2c219d13b699ba7c7cf9adf16fc10be5d8e1f35b2ea260e7987ab76e","name":"node_1dcbfc79418f9a665e3154e07663b296ce086e54c23458b7113a011dca62e635245cab144d392716e5e90b60336b60927270b72bb4a1c5c85fbf959060fb3ee4","services":["bzz"],"enable_msg_events":true,"port":62856},"up":true}},{"node":{"config":{"id":"3c4287b7722f71eb03a3d54fb6b18aeb0bf8495cffbe8c103fe90229f08392ca","private_key":"8fa1673a72b2ab000a4a7c8ad4540484d5286b69c93a5c608ef0977f783b7c4c","name":"node_571afb6ce94d28cb7d5cd49b3af35d2b1e44c917a9d3d207ab6c7ae960eb625377334e62317f6c69b3004e2eef92682649df828f4a645a75309ee7014ee4a940","services":["bzz"],"enable_msg_events":true,"port":62857},"up":true}},{"node":{"config":{"id":"615c7fcf7a80635e42719c0ae1fb924ba703ee39e021898be56b81ef0f575f62","private_key":"daa9c74fbb0b897acdf20f1b955a608b086ce5b45dc8e2b76a44f277d25e3377","name":"node_f1e669bb6e5b89c04e07e3dcdc445d498ac54c8ab2e9a62e622a492b6f9781bfa27f75ef7448d06caa28d9afa40c1c089066358cbcca40018bc0f903eab9508e","services":["bzz"],"enable_msg_events":true,"port":62858},"up":true}},{"node":{"config":{"id":"84842bc52628c0bed88539ee40f211de307b3e728ab5c0ca78032b86028b49db","private_key":"eb94b600be5949e3ca545e81b4d0a5fff3ae9f51f63c722d10f5098f9e1a883a","name":"node_526d25e0aa47e64d8f681255348a3d3c94fc68bedcb7111532f4ed50d47ef064b1e97e3a6bcd13ac8eaf44ce676b22d2b343a94dfa38b48cf964bfc3f338d4bd","services":["bzz"],"enable_msg_events":true,"port":62859},"up":true}},{"node":{"config":{"id":"91129c7135bff2b7cb1e1162bdd5399192790bdb4fc2eaa5b4cea53fe7a69d8c","private_key":"4e5d074bc46efbd0d18310e6a74e72e98b92080329d9c4e2b5ae99eb2fcfaf89","name":"node_fa81c440829adcbb3eba9196dc8255e319a976e53db0aceedcda25c2605f21b8778bf8c89e08744f7455c909568855f2a5be7bc88646796d21598ddda810390a","services":["bzz"],"enable_msg_events":true,"port":62860},"up":true}},{"node":{"config":{"id":"6d8b6881d36bc5f4a4a82f5d707807af2eef2eaf4527eb6b16f62cd75a92bb8c","private_key":"73c752d26f3438acdc54e9eaae6b1194d3706c65932939a593f1c8c5d451daf6","name":"node_5702eeccd7cb8c18dd25e5700919a544fb5e15fd53c5d4b56226021a9031a2206a694eb3b7334e4bd24fc510f7c1ea4f7a98e17e3394b4bd8d356a4d048b47dc","services":["bzz"],"enable_msg_events":true,"port":62861},"up":true}},{"node":{"config":{"id":"55cc9bd08390eec7bc050924e3011b251c6c4e088ac2adda27493064b75f0ab8","private_key":"2e6321d9dda81fc0f863f09868b280dea3300105cf9d224294aa138caf10b4a6","name":"node_6311f5a830849b47094d84129fd987fcd3d511d000bf0a8b888c9a457545950fdae6ae10a2a85b47721a1d1ece8c1c62d866a26577380048d30f205c4e7cd7e8","services":["bzz"],"enable_msg_events":true,"port":62862},"up":true}},{"node":{"config":{"id":"a873e971042dfd5aaeb56d7d9089e35bc8dd410451a66740ebe7ea0752870120","private_key":"200b7caaf8a33b8ea47947eb842fb8d8aac90951c70bb2f555f902380eb7f1ee","name":"node_dbef367277df652c8d3380efcf5f9d445d77fa6e7957b50b2467f90b7190e70d4eb4febbbffa6b3fc2b04bbf74dc7191e2b98923c3a62ecb80ba5336195994c7","services":["bzz"],"enable_msg_events":true,"port":62863},"up":true}},{"node":{"config":{"id":"9fcdef2b818e8a4fcc07fedb706d9f88cc59703230af4f52043c84f9bd37cc57","private_key":"b58a24805bb0c417e5666f2bf3f3342b8359d3c0d30a631c0104fcdfb6d0809b","name":"node_a4cdf723b3d7ead36bcb1bba582c8758271d5f08651694e1def0ae2ceca079d4fca81730a84a14749a81bc3ea41a66f1320788310823de5d1ceecd608ed5b88e","services":["bzz"],"enable_msg_events":true,"port":62864},"up":true}},{"node":{"config":{"id":"fc373cf4e892501073ed87b98231f0d001e6db70a1a08148035fbdfa2dffb3d3","private_key":"422d3b2c1b37b851ad524c8f26700fb5eec0d9a2fff1cb157ae931baa52d9521","name":"node_67cc8be0fd092c82e53efc53c418db257eff1135ad0058fd10e3eb77b205cb3e6fa87dfed4ec008c371f77546d4c117d8c34734bfe594da4c96fc08e0d2ede32","services":["bzz"],"enable_msg_events":true,"port":62865},"up":true}},{"node":{"config":{"id":"af99d46c187735ff037e1264f2dbca2985694d906de0e2c9e52d5513a6feb331","private_key":"7871010c8b88b9594c71b9f43aecfefdca39da348e31f47ef2645c20c100d072","name":"node_4da7d1c9577afb52a5fe1de9f99824ceff314828c581caed8a3a33168df699d7b3befb4e888419264a9eeb3c9e5e9dac468e01edd71eb6f0ef41d15f7651042d","services":["bzz"],"enable_msg_events":true,"port":62866},"up":true}}],"conns":[{"one":"af99d46c187735ff037e1264f2dbca2985694d906de0e2c9e52d5513a6feb331","other":"a3790562c8209b7e96e7abef9ad28de2e470a01927234e4848966e0cd15652e9","up":true},{"one":"f64d3106d2bb99e69e26c07de168f7fceaf85672859f7c1c038fd4dd6b6d7588","other":"3c4287b7722f71eb03a3d54fb6b18aeb0bf8495cffbe8c103fe90229f08392ca","up":true},{"one":"a3790562c8209b7e96e7abef9ad28de2e470a01927234e4848966e0cd15652e9","other":"4ce65548fc8490c02e4c6c0549be7329a8a05c090d62870ad1075e52e204a3b5","up":true},{"one":"4ce65548fc8490c02e4c6c0549be7329a8a05c090d62870ad1075e52e204a3b5","other":"8bf97ec29b6b3ca638ed82fa30d03d0d8309c9e8f1db2071f4a37de805327108","up":true},{"one":"8bf97ec29b6b3ca638ed82fa30d03d0d8309c9e8f1db2071f4a37de805327108","other":"9121c3fea7ccc99775fb5ade460f7a0cb76003231d13d44ed3e4f3cca1947227","up":true},{"one":"9121c3fea7ccc99775fb5ade460f7a0cb76003231d13d44ed3e4f3cca1947227","other":"fb555885b96d5742a10e688bd7c1bb842f0434c319bf076d07f545e4c0550601","up":true},{"one":"fb555885b96d5742a10e688bd7c1bb842f0434c319bf076d07f545e4c0550601","other":"f64d3106d2bb99e69e26c07de168f7fceaf85672859f7c1c038fd4dd6b6d7588","up":true},{"one":"6d8b6881d36bc5f4a4a82f5d707807af2eef2eaf4527eb6b16f62cd75a92bb8c","other":"55cc9bd08390eec7bc050924e3011b251c6c4e088ac2adda27493064b75f0ab8","up":true},{"one":"3c4287b7722f71eb03a3d54fb6b18aeb0bf8495cffbe8c103fe90229f08392ca","other":"615c7fcf7a80635e42719c0ae1fb924ba703ee39e021898be56b81ef0f575f62","up":true},{"one":"615c7fcf7a80635e42719c0ae1fb924ba703ee39e021898be56b81ef0f575f62","other":"84842bc52628c0bed88539ee40f211de307b3e728ab5c0ca78032b86028b49db","up":true},{"one":"84842bc52628c0bed88539ee40f211de307b3e728ab5c0ca78032b86028b49db","other":"91129c7135bff2b7cb1e1162bdd5399192790bdb4fc2eaa5b4cea53fe7a69d8c","up":true},{"one":"91129c7135bff2b7cb1e1162bdd5399192790bdb4fc2eaa5b4cea53fe7a69d8c","other":"6d8b6881d36bc5f4a4a82f5d707807af2eef2eaf4527eb6b16f62cd75a92bb8c","up":true},{"one":"a873e971042dfd5aaeb56d7d9089e35bc8dd410451a66740ebe7ea0752870120","other":"9fcdef2b818e8a4fcc07fedb706d9f88cc59703230af4f52043c84f9bd37cc57","up":true},{"one":"55cc9bd08390eec7bc050924e3011b251c6c4e088ac2adda27493064b75f0ab8","other":"a873e971042dfd5aaeb56d7d9089e35bc8dd410451a66740ebe7ea0752870120","up":true},{"one":"9fcdef2b818e8a4fcc07fedb706d9f88cc59703230af4f52043c84f9bd37cc57","other":"fc373cf4e892501073ed87b98231f0d001e6db70a1a08148035fbdfa2dffb3d3","up":true},{"one":"fc373cf4e892501073ed87b98231f0d001e6db70a1a08148035fbdfa2dffb3d3","other":"af99d46c187735ff037e1264f2dbca2985694d906de0e2c9e52d5513a6feb331","up":true},{"one":"a3790562c8209b7e96e7abef9ad28de2e470a01927234e4848966e0cd15652e9","other":"a873e971042dfd5aaeb56d7d9089e35bc8dd410451a66740ebe7ea0752870120","up":true},{"one":"9121c3fea7ccc99775fb5ade460f7a0cb76003231d13d44ed3e4f3cca1947227","other":"9fcdef2b818e8a4fcc07fedb706d9f88cc59703230af4f52043c84f9bd37cc57","up":true},{"one":"fb555885b96d5742a10e688bd7c1bb842f0434c319bf076d07f545e4c0550601","other":"fc373cf4e892501073ed87b98231f0d001e6db70a1a08148035fbdfa2dffb3d3","up":true},{"one":"a873e971042dfd5aaeb56d7d9089e35bc8dd410451a66740ebe7ea0752870120","other":"af99d46c187735ff037e1264f2dbca2985694d906de0e2c9e52d5513a6feb331","up":true},{"one":"9fcdef2b818e8a4fcc07fedb706d9f88cc59703230af4f52043c84f9bd37cc57","other":"8bf97ec29b6b3ca638ed82fa30d03d0d8309c9e8f1db2071f4a37de805327108","up":true},{"one":"af99d46c187735ff037e1264f2dbca2985694d906de0e2c9e52d5513a6feb331","other":"3c4287b7722f71eb03a3d54fb6b18aeb0bf8495cffbe8c103fe90229f08392ca","up":true},{"one":"91129c7135bff2b7cb1e1162bdd5399192790bdb4fc2eaa5b4cea53fe7a69d8c","other":"9121c3fea7ccc99775fb5ade460f7a0cb76003231d13d44ed3e4f3cca1947227","up":true},{"one":"f64d3106d2bb99e69e26c07de168f7fceaf85672859f7c1c038fd4dd6b6d7588","other":"fc373cf4e892501073ed87b98231f0d001e6db70a1a08148035fbdfa2dffb3d3","up":true},{"one":"a3790562c8209b7e96e7abef9ad28de2e470a01927234e4848966e0cd15652e9","other":"f64d3106d2bb99e69e26c07de168f7fceaf85672859f7c1c038fd4dd6b6d7588","up":true},{"one":"9121c3fea7ccc99775fb5ade460f7a0cb76003231d13d44ed3e4f3cca1947227","other":"3c4287b7722f71eb03a3d54fb6b18aeb0bf8495cffbe8c103fe90229f08392ca","up":true},{"one":"fb555885b96d5742a10e688bd7c1bb842f0434c319bf076d07f545e4c0550601","other":"3c4287b7722f71eb03a3d54fb6b18aeb0bf8495cffbe8c103fe90229f08392ca","up":true},{"one":"91129c7135bff2b7cb1e1162bdd5399192790bdb4fc2eaa5b4cea53fe7a69d8c","other":"9fcdef2b818e8a4fcc07fedb706d9f88cc59703230af4f52043c84f9bd37cc57","up":true},{"one":"af99d46c187735ff037e1264f2dbca2985694d906de0e2c9e52d5513a6feb331","other":"8bf97ec29b6b3ca638ed82fa30d03d0d8309c9e8f1db2071f4a37de805327108","up":true},{"one":"9fcdef2b818e8a4fcc07fedb706d9f88cc59703230af4f52043c84f9bd37cc57","other":"3c4287b7722f71eb03a3d54fb6b18aeb0bf8495cffbe8c103fe90229f08392ca","up":true},{"one":"a873e971042dfd5aaeb56d7d9089e35bc8dd410451a66740ebe7ea0752870120","other":"f64d3106d2bb99e69e26c07de168f7fceaf85672859f7c1c038fd4dd6b6d7588","up":true},{"one":"3c4287b7722f71eb03a3d54fb6b18aeb0bf8495cffbe8c103fe90229f08392ca","other":"6d8b6881d36bc5f4a4a82f5d707807af2eef2eaf4527eb6b16f62cd75a92bb8c","up":true},{"one":"84842bc52628c0bed88539ee40f211de307b3e728ab5c0ca78032b86028b49db","other":"8bf97ec29b6b3ca638ed82fa30d03d0d8309c9e8f1db2071f4a37de805327108","up":true},{"one":"55cc9bd08390eec7bc050924e3011b251c6c4e088ac2adda27493064b75f0ab8","other":"4ce65548fc8490c02e4c6c0549be7329a8a05c090d62870ad1075e52e204a3b5","up":true},{"one":"615c7fcf7a80635e42719c0ae1fb924ba703ee39e021898be56b81ef0f575f62","other":"6d8b6881d36bc5f4a4a82f5d707807af2eef2eaf4527eb6b16f62cd75a92bb8c","up":true},{"one":"a3790562c8209b7e96e7abef9ad28de2e470a01927234e4848966e0cd15652e9","other":"84842bc52628c0bed88539ee40f211de307b3e728ab5c0ca78032b86028b49db","up":true},{"one":"9121c3fea7ccc99775fb5ade460f7a0cb76003231d13d44ed3e4f3cca1947227","other":"af99d46c187735ff037e1264f2dbca2985694d906de0e2c9e52d5513a6feb331","up":true},{"one":"3c4287b7722f71eb03a3d54fb6b18aeb0bf8495cffbe8c103fe90229f08392ca","other":"55cc9bd08390eec7bc050924e3011b251c6c4e088ac2adda27493064b75f0ab8","up":true},{"one":"615c7fcf7a80635e42719c0ae1fb924ba703ee39e021898be56b81ef0f575f62","other":"55cc9bd08390eec7bc050924e3011b251c6c4e088ac2adda27493064b75f0ab8","up":true},{"one":"84842bc52628c0bed88539ee40f211de307b3e728ab5c0ca78032b86028b49db","other":"9121c3fea7ccc99775fb5ade460f7a0cb76003231d13d44ed3e4f3cca1947227","up":true},{"one":"91129c7135bff2b7cb1e1162bdd5399192790bdb4fc2eaa5b4cea53fe7a69d8c","other":"f64d3106d2bb99e69e26c07de168f7fceaf85672859f7c1c038fd4dd6b6d7588","up":true},{"one":"6d8b6881d36bc5f4a4a82f5d707807af2eef2eaf4527eb6b16f62cd75a92bb8c","other":"4ce65548fc8490c02e4c6c0549be7329a8a05c090d62870ad1075e52e204a3b5","up":true},{"one":"fc373cf4e892501073ed87b98231f0d001e6db70a1a08148035fbdfa2dffb3d3","other":"3c4287b7722f71eb03a3d54fb6b18aeb0bf8495cffbe8c103fe90229f08392ca","up":true},{"one":"3c4287b7722f71eb03a3d54fb6b18aeb0bf8495cffbe8c103fe90229f08392ca","other":"4ce65548fc8490c02e4c6c0549be7329a8a05c090d62870ad1075e52e204a3b5","up":true},{"one":"615c7fcf7a80635e42719c0ae1fb924ba703ee39e021898be56b81ef0f575f62","other":"4ce65548fc8490c02e4c6c0549be7329a8a05c090d62870ad1075e52e204a3b5","up":true},{"one":"84842bc52628c0bed88539ee40f211de307b3e728ab5c0ca78032b86028b49db","other":"9fcdef2b818e8a4fcc07fedb706d9f88cc59703230af4f52043c84f9bd37cc57","up":true},{"one":"91129c7135bff2b7cb1e1162bdd5399192790bdb4fc2eaa5b4cea53fe7a69d8c","other":"a3790562c8209b7e96e7abef9ad28de2e470a01927234e4848966e0cd15652e9","up":true},{"one":"8bf97ec29b6b3ca638ed82fa30d03d0d8309c9e8f1db2071f4a37de805327108","other":"91129c7135bff2b7cb1e1162bdd5399192790bdb4fc2eaa5b4cea53fe7a69d8c","up":true},{"one":"8bf97ec29b6b3ca638ed82fa30d03d0d8309c9e8f1db2071f4a37de805327108","other":"fb555885b96d5742a10e688bd7c1bb842f0434c319bf076d07f545e4c0550601","up":true},{"one":"84842bc52628c0bed88539ee40f211de307b3e728ab5c0ca78032b86028b49db","other":"fb555885b96d5742a10e688bd7c1bb842f0434c319bf076d07f545e4c0550601","up":true}]}
diff --git a/cmd/swarm/swarm-snapshot/verify.go b/cmd/swarm/swarm-snapshot/verify.go
new file mode 100644
index 0000000000..3312d7681f
--- /dev/null
+++ b/cmd/swarm/swarm-snapshot/verify.go
@@ -0,0 +1,85 @@
+// 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
+// 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 .
+
+package main
+
+import (
+ "context"
+ "errors"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/cmd/utils"
+ "github.com/ethereum/go-ethereum/node"
+ "github.com/ethereum/go-ethereum/p2p/simulations/adapters"
+ "github.com/ethereum/go-ethereum/swarm/network"
+ "github.com/ethereum/go-ethereum/swarm/network/simulation"
+ cli "gopkg.in/urfave/cli.v1"
+)
+
+func verify(ctx *cli.Context) error {
+ if len(ctx.Args()) < 1 {
+ return errors.New("argument should be the filename to verify or write-to")
+ }
+ filename, err := touchPath(ctx.Args()[0])
+ if err != nil {
+ return err
+ }
+ err = verifySnapshot(filename)
+ if err != nil {
+ utils.Fatalf("Simulation failed: %s", err)
+ }
+
+ return err
+}
+
+func verifySnapshot(filename string) error {
+ sim := simulation.New(map[string]simulation.ServiceFunc{
+ "bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) {
+ addr := network.NewAddr(ctx.Config.Node())
+
+ kp := network.NewKadParams()
+ kp.MinProxBinSize = testMinProxBinSize
+
+ kad := network.NewKademlia(addr.Over(), kp)
+ hp := network.NewHiveParams()
+ hp.KeepAliveInterval = time.Duration(200) * time.Millisecond
+ hp.Discovery = true //discovery
+
+ config := &network.BzzConfig{
+ OverlayAddr: addr.Over(),
+ UnderlayAddr: addr.Under(),
+ HiveParams: hp,
+ }
+ return network.NewBzz(config, kad, nil, nil, nil), nil, nil
+
+ },
+ })
+ defer sim.Close()
+ err := sim.UploadSnapshot(filename)
+ if err != nil {
+ utils.Fatalf("%v", err)
+ }
+
+ ctx, cancelSimRun := context.WithTimeout(context.Background(), 2*time.Minute)
+ defer cancelSimRun()
+
+ if _, err := sim.WaitTillHealthy(ctx, 2); err != nil {
+ utils.Fatalf("%v", err)
+ }
+
+ return nil
+}
diff --git a/cmd/swarm/swarm-snapshot/verify_test.go b/cmd/swarm/swarm-snapshot/verify_test.go
new file mode 100644
index 0000000000..45646be078
--- /dev/null
+++ b/cmd/swarm/swarm-snapshot/verify_test.go
@@ -0,0 +1,33 @@
+// 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
+// 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 .
+
+package main
+
+import (
+ "testing"
+)
+
+func TestSnapshotVerify(t *testing.T) {
+ snap := runSnapshot(t,
+ "v",
+ "testdata/snapshot.json",
+ )
+
+ snap.ExpectExit()
+ if snap.ExitStatus() != 0 {
+ t.Fatal("expected exit code 0")
+ }
+}
diff --git a/p2p/simulations/network.go b/p2p/simulations/network.go
index 86f7dc9bef..ffbc523f6d 100644
--- a/p2p/simulations/network.go
+++ b/p2p/simulations/network.go
@@ -80,6 +80,43 @@ func (net *Network) Events() *event.Feed {
return &net.events
}
+func triggerChecks(trigger chan enode.ID, net *Network, id enode.ID) error {
+ node := net.GetNode(id)
+ if node == nil {
+ return fmt.Errorf("unknown node: %s", id)
+ }
+ client, err := node.Client()
+ if err != nil {
+ return err
+ }
+ events := make(chan *p2p.PeerEvent)
+ sub, err := client.Subscribe(context.Background(), "admin", events, "peerEvents")
+ if err != nil {
+ return fmt.Errorf("error getting peer events for node %v: %s", id, err)
+ }
+ go func() {
+ defer sub.Unsubscribe()
+
+ tick := time.NewTicker(time.Second)
+ defer tick.Stop()
+
+ for {
+ select {
+ case <-events:
+ trigger <- id
+ case <-tick.C:
+ trigger <- id
+ case err := <-sub.Err():
+ if err != nil {
+ log.Error(fmt.Sprintf("error getting peer events for node %v", id), "err", err)
+ }
+ return
+ }
+ }
+ }()
+ return nil
+}
+
// NewNodeWithConfig adds a new node to the network with the given config,
// returning an error if a node with the same ID or name already exists
func (net *Network) NewNodeWithConfig(conf *adapters.NodeConfig) (*Node, error) {
diff --git a/p2p/simulations/simulation.go b/p2p/simulations/simulation.go
index ae62c42b9c..b7e6204e5d 100644
--- a/p2p/simulations/simulation.go
+++ b/p2p/simulations/simulation.go
@@ -49,9 +49,11 @@ func (s *Simulation) Run(ctx context.Context, step *Step) (result *StepResult) {
defer stop()
// perform the action
- if err := step.Action(ctx); err != nil {
- result.Error = err
- return
+ if step.Action != nil {
+ if err := step.Action(ctx); err != nil {
+ result.Error = err
+ return
+ }
}
// wait for all node expectations to either pass, error or timeout
diff --git a/swarm/network/kademlia.go b/swarm/network/kademlia.go
index ec53f70a35..1c0f6fec56 100644
--- a/swarm/network/kademlia.go
+++ b/swarm/network/kademlia.go
@@ -640,6 +640,8 @@ func (k *Kademlia) saturation() int {
})
// TODO evaluate whether this check cannot just as well be done within the eachbin
depth := depthForPot(k.conns, k.NeighbourhoodSize, k.base)
+
+ // if in the iterator above we iterated deeper than the neighbourhood depth - return depth
if depth < prev {
return depth
}
diff --git a/swarm/network/simulations/discovery/discovery_test.go b/swarm/network/simulations/discovery/discovery_test.go
index e5121c4775..75f34930df 100644
--- a/swarm/network/simulations/discovery/discovery_test.go
+++ b/swarm/network/simulations/discovery/discovery_test.go
@@ -23,11 +23,9 @@ import (
"flag"
"fmt"
"io/ioutil"
- "math/rand"
"os"
"path"
"strings"
- "sync"
"testing"
"time"
@@ -247,25 +245,14 @@ func discoverySimulation(nodes, conns int, adapter adapters.NodeAdapter) (*simul
action := func(ctx context.Context) error {
return nil
}
- wg := sync.WaitGroup{}
for i := range ids {
// collect the overlay addresses, to
addrs = append(addrs, ids[i].Bytes())
- for j := 0; j < conns; j++ {
- var k int
- if j == 0 {
- k = (i + 1) % len(ids)
- } else {
- k = rand.Intn(len(ids))
- }
- wg.Add(1)
- go func(i, k int) {
- defer wg.Done()
- net.Connect(ids[i], ids[k])
- }(i, k)
- }
}
- wg.Wait()
+ err := net.ConnectNodesChain(nil)
+ if err != nil {
+ return nil, err
+ }
log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
// construct the peer pot, so that kademlia health can be checked
ppmap := network.NewPeerPotMap(network.NewKadParams().NeighbourhoodSize, addrs)
@@ -457,23 +444,7 @@ func discoveryPersistenceSimulation(nodes, conns int, adapter adapters.NodeAdapt
return nil
}
- //connects in a chain
- wg := sync.WaitGroup{}
- //connects in a ring
- for i := range ids {
- for j := 1; j <= conns; j++ {
- k := (i + j) % len(ids)
- if k == i {
- k = (k + 1) % len(ids)
- }
- wg.Add(1)
- go func(i, k int) {
- defer wg.Done()
- net.Connect(ids[i], ids[k])
- }(i, k)
- }
- }
- wg.Wait()
+ net.ConnectNodesChain(nil)
log.Debug(fmt.Sprintf("nodes: %v", len(addrs)))
// construct the peer pot, so that kademlia health can be checked
check := func(ctx context.Context, id enode.ID) (bool, error) {