|
| 1 | +// Copyright 2018 The go-ethereum Authors |
| 2 | +// This file is part of go-ethereum. |
| 3 | +// |
| 4 | +// go-ethereum is free software: you can redistribute it and/or modify |
| 5 | +// it under the terms of the GNU General Public License as published by |
| 6 | +// the Free Software Foundation, either version 3 of the License, or |
| 7 | +// (at your option) any later version. |
| 8 | +// |
| 9 | +// go-ethereum is distributed in the hope that it will be useful, |
| 10 | +// but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | +// GNU General Public License for more details. |
| 13 | +// |
| 14 | +// You should have received a copy of the GNU General Public License |
| 15 | +// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>. |
| 16 | + |
| 17 | +package main |
| 18 | + |
| 19 | +import ( |
| 20 | + "context" |
| 21 | + "encoding/json" |
| 22 | + "errors" |
| 23 | + "fmt" |
| 24 | + "io/ioutil" |
| 25 | + "os" |
| 26 | + "path" |
| 27 | + "path/filepath" |
| 28 | + "strings" |
| 29 | + "sync" |
| 30 | + "time" |
| 31 | + |
| 32 | + "github.com/ethereum/go-ethereum/log" |
| 33 | + "github.com/ethereum/go-ethereum/node" |
| 34 | + "github.com/ethereum/go-ethereum/p2p/simulations" |
| 35 | + "github.com/ethereum/go-ethereum/p2p/simulations/adapters" |
| 36 | + "github.com/ethereum/go-ethereum/swarm/network" |
| 37 | + "github.com/ethereum/go-ethereum/swarm/network/simulation" |
| 38 | + cli "gopkg.in/urfave/cli.v1" |
| 39 | +) |
| 40 | + |
| 41 | +// create is used as the entry function for "create" app command. |
| 42 | +func create(ctx *cli.Context) error { |
| 43 | + log.PrintOrigins(true) |
| 44 | + log.Root().SetHandler(log.LvlFilterHandler(log.Lvl(ctx.Int("verbosity")), log.StreamHandler(os.Stdout, log.TerminalFormat(true)))) |
| 45 | + |
| 46 | + if len(ctx.Args()) < 1 { |
| 47 | + return errors.New("argument should be the filename to verify or write-to") |
| 48 | + } |
| 49 | + filename, err := touchPath(ctx.Args()[0]) |
| 50 | + if err != nil { |
| 51 | + return err |
| 52 | + } |
| 53 | + return createSnapshot(filename, ctx.Int("nodes"), strings.Split(ctx.String("services"), ",")) |
| 54 | +} |
| 55 | + |
| 56 | +// createSnapshot creates a new snapshot on filesystem with provided filename, |
| 57 | +// number of nodes and service names. |
| 58 | +func createSnapshot(filename string, nodes int, services []string) (err error) { |
| 59 | + log.Debug("create snapshot", "filename", filename, "nodes", nodes, "services", services) |
| 60 | + |
| 61 | + sim := simulation.New(map[string]simulation.ServiceFunc{ |
| 62 | + "bzz": func(ctx *adapters.ServiceContext, b *sync.Map) (node.Service, func(), error) { |
| 63 | + addr := network.NewAddr(ctx.Config.Node()) |
| 64 | + kad := network.NewKademlia(addr.Over(), network.NewKadParams()) |
| 65 | + hp := network.NewHiveParams() |
| 66 | + hp.KeepAliveInterval = time.Duration(200) * time.Millisecond |
| 67 | + hp.Discovery = true // discovery must be enabled when creating a snapshot |
| 68 | + |
| 69 | + config := &network.BzzConfig{ |
| 70 | + OverlayAddr: addr.Over(), |
| 71 | + UnderlayAddr: addr.Under(), |
| 72 | + HiveParams: hp, |
| 73 | + } |
| 74 | + return network.NewBzz(config, kad, nil, nil, nil), nil, nil |
| 75 | + }, |
| 76 | + }) |
| 77 | + defer sim.Close() |
| 78 | + |
| 79 | + _, err = sim.AddNodes(nodes) |
| 80 | + if err != nil { |
| 81 | + return fmt.Errorf("add nodes: %v", err) |
| 82 | + } |
| 83 | + |
| 84 | + err = sim.Net.ConnectNodesRing(nil) |
| 85 | + if err != nil { |
| 86 | + return fmt.Errorf("connect nodes: %v", err) |
| 87 | + } |
| 88 | + |
| 89 | + ctx, cancelSimRun := context.WithTimeout(context.Background(), 2*time.Minute) |
| 90 | + defer cancelSimRun() |
| 91 | + if _, err := sim.WaitTillHealthy(ctx); err != nil { |
| 92 | + return fmt.Errorf("wait for healthy kademlia: %v", err) |
| 93 | + } |
| 94 | + |
| 95 | + var snap *simulations.Snapshot |
| 96 | + if len(services) > 0 { |
| 97 | + // If service names are provided, include them in the snapshot. |
| 98 | + // But, check if "bzz" service is not among them to remove it |
| 99 | + // form the snapshot as it exists on snapshot creation. |
| 100 | + var removeServices []string |
| 101 | + var wantBzz bool |
| 102 | + for _, s := range services { |
| 103 | + if s == "bzz" { |
| 104 | + wantBzz = true |
| 105 | + break |
| 106 | + } |
| 107 | + } |
| 108 | + if !wantBzz { |
| 109 | + removeServices = []string{"bzz"} |
| 110 | + } |
| 111 | + snap, err = sim.Net.SnapshotWithServices(services, removeServices) |
| 112 | + } else { |
| 113 | + snap, err = sim.Net.Snapshot() |
| 114 | + } |
| 115 | + if err != nil { |
| 116 | + return fmt.Errorf("create snapshot: %v", err) |
| 117 | + } |
| 118 | + jsonsnapshot, err := json.Marshal(snap) |
| 119 | + if err != nil { |
| 120 | + return fmt.Errorf("json encode snapshot: %v", err) |
| 121 | + } |
| 122 | + return ioutil.WriteFile(filename, jsonsnapshot, 0666) |
| 123 | +} |
| 124 | + |
| 125 | +// touchPath creates an empty file and all subdirectories |
| 126 | +// that are missing. |
| 127 | +func touchPath(filename string) (string, error) { |
| 128 | + if path.IsAbs(filename) { |
| 129 | + if _, err := os.Stat(filename); err == nil { |
| 130 | + // path exists, overwrite |
| 131 | + return filename, nil |
| 132 | + } |
| 133 | + } |
| 134 | + |
| 135 | + d, f := path.Split(filename) |
| 136 | + dir, err := filepath.Abs(filepath.Dir(os.Args[0])) |
| 137 | + if err != nil { |
| 138 | + return "", err |
| 139 | + } |
| 140 | + |
| 141 | + _, err = os.Stat(path.Join(dir, filename)) |
| 142 | + if err == nil { |
| 143 | + // path exists, overwrite |
| 144 | + return filename, nil |
| 145 | + } |
| 146 | + |
| 147 | + dirPath := path.Join(dir, d) |
| 148 | + filePath := path.Join(dirPath, f) |
| 149 | + if d != "" { |
| 150 | + err = os.MkdirAll(dirPath, os.ModeDir) |
| 151 | + if err != nil { |
| 152 | + return "", err |
| 153 | + } |
| 154 | + } |
| 155 | + |
| 156 | + return filePath, nil |
| 157 | +} |
0 commit comments