main.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. // Copyright 2016 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. package main
  17. import (
  18. "crypto/ecdsa"
  19. "encoding/hex"
  20. "fmt"
  21. "io/ioutil"
  22. "os"
  23. "os/signal"
  24. "runtime"
  25. "sort"
  26. "strconv"
  27. "strings"
  28. "syscall"
  29. "github.com/ethereum/go-ethereum/accounts"
  30. "github.com/ethereum/go-ethereum/accounts/keystore"
  31. "github.com/ethereum/go-ethereum/cmd/utils"
  32. "github.com/ethereum/go-ethereum/common"
  33. "github.com/ethereum/go-ethereum/console"
  34. "github.com/ethereum/go-ethereum/crypto"
  35. "github.com/ethereum/go-ethereum/internal/debug"
  36. "github.com/ethereum/go-ethereum/log"
  37. "github.com/ethereum/go-ethereum/node"
  38. "github.com/ethereum/go-ethereum/p2p/enode"
  39. "github.com/ethereum/go-ethereum/swarm"
  40. bzzapi "github.com/ethereum/go-ethereum/swarm/api"
  41. swarmmetrics "github.com/ethereum/go-ethereum/swarm/metrics"
  42. "github.com/ethereum/go-ethereum/swarm/tracing"
  43. sv "github.com/ethereum/go-ethereum/swarm/version"
  44. "gopkg.in/urfave/cli.v1"
  45. )
  46. const clientIdentifier = "swarm"
  47. const helpTemplate = `NAME:
  48. {{.HelpName}} - {{.Usage}}
  49. USAGE:
  50. {{if .UsageText}}{{.UsageText}}{{else}}{{.HelpName}}{{if .VisibleFlags}} [command options]{{end}} {{if .ArgsUsage}}{{.ArgsUsage}}{{else}}[arguments...]{{end}}{{end}}{{if .Category}}
  51. CATEGORY:
  52. {{.Category}}{{end}}{{if .Description}}
  53. DESCRIPTION:
  54. {{.Description}}{{end}}{{if .VisibleFlags}}
  55. OPTIONS:
  56. {{range .VisibleFlags}}{{.}}
  57. {{end}}{{end}}
  58. `
  59. // Git SHA1 commit hash of the release (set via linker flags)
  60. // this variable will be assigned if corresponding parameter is passed with install, but not with test
  61. // e.g.: go install -ldflags "-X main.gitCommit=ed1312d01b19e04ef578946226e5d8069d5dfd5a" ./cmd/swarm
  62. var gitCommit string
  63. //declare a few constant error messages, useful for later error check comparisons in test
  64. var (
  65. SWARM_ERR_NO_BZZACCOUNT = "bzzaccount option is required but not set; check your config file, command line or environment variables"
  66. SWARM_ERR_SWAP_SET_NO_API = "SWAP is enabled but --swap-api is not set"
  67. )
  68. // this help command gets added to any subcommand that does not define it explicitly
  69. var defaultSubcommandHelp = cli.Command{
  70. Action: func(ctx *cli.Context) { cli.ShowCommandHelpAndExit(ctx, "", 1) },
  71. CustomHelpTemplate: helpTemplate,
  72. Name: "help",
  73. Usage: "shows this help",
  74. Hidden: true,
  75. }
  76. var defaultNodeConfig = node.DefaultConfig
  77. // This init function sets defaults so cmd/swarm can run alongside geth.
  78. func init() {
  79. sv.GitCommit = gitCommit
  80. defaultNodeConfig.Name = clientIdentifier
  81. defaultNodeConfig.Version = sv.VersionWithCommit(gitCommit)
  82. defaultNodeConfig.P2P.ListenAddr = ":30399"
  83. defaultNodeConfig.IPCPath = "bzzd.ipc"
  84. // Set flag defaults for --help display.
  85. utils.ListenPortFlag.Value = 30399
  86. }
  87. var app = utils.NewApp("", "Ethereum Swarm")
  88. // This init function creates the cli.App.
  89. func init() {
  90. app.Action = bzzd
  91. app.Version = sv.ArchiveVersion(gitCommit)
  92. app.Copyright = "Copyright 2013-2016 The go-ethereum Authors"
  93. app.Commands = []cli.Command{
  94. {
  95. Action: version,
  96. CustomHelpTemplate: helpTemplate,
  97. Name: "version",
  98. Usage: "Print version numbers",
  99. Description: "The output of this command is supposed to be machine-readable",
  100. },
  101. {
  102. Action: keys,
  103. CustomHelpTemplate: helpTemplate,
  104. Name: "print-keys",
  105. Flags: []cli.Flag{SwarmCompressedFlag},
  106. Usage: "Print public key information",
  107. Description: "The output of this command is supposed to be machine-readable",
  108. },
  109. // See upload.go
  110. upCommand,
  111. // See access.go
  112. accessCommand,
  113. // See feeds.go
  114. feedCommand,
  115. // See list.go
  116. listCommand,
  117. // See hash.go
  118. hashCommand,
  119. // See download.go
  120. downloadCommand,
  121. // See manifest.go
  122. manifestCommand,
  123. // See fs.go
  124. fsCommand,
  125. // See db.go
  126. dbCommand,
  127. // See config.go
  128. DumpConfigCommand,
  129. }
  130. // append a hidden help subcommand to all commands that have subcommands
  131. // if a help command was already defined above, that one will take precedence.
  132. addDefaultHelpSubcommands(app.Commands)
  133. sort.Sort(cli.CommandsByName(app.Commands))
  134. app.Flags = []cli.Flag{
  135. utils.IdentityFlag,
  136. utils.DataDirFlag,
  137. utils.BootnodesFlag,
  138. utils.KeyStoreDirFlag,
  139. utils.ListenPortFlag,
  140. utils.DiscoveryV5Flag,
  141. utils.NetrestrictFlag,
  142. utils.NodeKeyFileFlag,
  143. utils.NodeKeyHexFlag,
  144. utils.MaxPeersFlag,
  145. utils.NATFlag,
  146. utils.IPCDisabledFlag,
  147. utils.IPCPathFlag,
  148. utils.PasswordFileFlag,
  149. // bzzd-specific flags
  150. CorsStringFlag,
  151. EnsAPIFlag,
  152. SwarmTomlConfigPathFlag,
  153. SwarmSwapEnabledFlag,
  154. SwarmSwapAPIFlag,
  155. SwarmSyncDisabledFlag,
  156. SwarmSyncUpdateDelay,
  157. SwarmMaxStreamPeerServersFlag,
  158. SwarmLightNodeEnabled,
  159. SwarmDeliverySkipCheckFlag,
  160. SwarmListenAddrFlag,
  161. SwarmPortFlag,
  162. SwarmAccountFlag,
  163. SwarmNetworkIdFlag,
  164. ChequebookAddrFlag,
  165. // upload flags
  166. SwarmApiFlag,
  167. SwarmRecursiveFlag,
  168. SwarmWantManifestFlag,
  169. SwarmUploadDefaultPath,
  170. SwarmUpFromStdinFlag,
  171. SwarmUploadMimeType,
  172. // bootnode mode
  173. SwarmBootnodeModeFlag,
  174. // storage flags
  175. SwarmStorePath,
  176. SwarmStoreCapacity,
  177. SwarmStoreCacheCapacity,
  178. }
  179. rpcFlags := []cli.Flag{
  180. utils.WSEnabledFlag,
  181. utils.WSListenAddrFlag,
  182. utils.WSPortFlag,
  183. utils.WSApiFlag,
  184. utils.WSAllowedOriginsFlag,
  185. }
  186. app.Flags = append(app.Flags, rpcFlags...)
  187. app.Flags = append(app.Flags, debug.Flags...)
  188. app.Flags = append(app.Flags, swarmmetrics.Flags...)
  189. app.Flags = append(app.Flags, tracing.Flags...)
  190. app.Before = func(ctx *cli.Context) error {
  191. runtime.GOMAXPROCS(runtime.NumCPU())
  192. if err := debug.Setup(ctx, ""); err != nil {
  193. return err
  194. }
  195. swarmmetrics.Setup(ctx)
  196. tracing.Setup(ctx)
  197. return nil
  198. }
  199. app.After = func(ctx *cli.Context) error {
  200. debug.Exit()
  201. return nil
  202. }
  203. }
  204. func main() {
  205. if err := app.Run(os.Args); err != nil {
  206. fmt.Fprintln(os.Stderr, err)
  207. os.Exit(1)
  208. }
  209. }
  210. func keys(ctx *cli.Context) error {
  211. privateKey := getPrivKey(ctx)
  212. pubkey := crypto.FromECDSAPub(&privateKey.PublicKey)
  213. pubkeyhex := hex.EncodeToString(pubkey)
  214. pubCompressed := hex.EncodeToString(crypto.CompressPubkey(&privateKey.PublicKey))
  215. bzzkey := crypto.Keccak256Hash(pubkey).Hex()
  216. if !ctx.Bool(SwarmCompressedFlag.Name) {
  217. fmt.Println(fmt.Sprintf("bzzkey=%s", bzzkey[2:]))
  218. fmt.Println(fmt.Sprintf("publicKey=%s", pubkeyhex))
  219. }
  220. fmt.Println(fmt.Sprintf("publicKeyCompressed=%s", pubCompressed))
  221. return nil
  222. }
  223. func version(ctx *cli.Context) error {
  224. fmt.Println(strings.Title(clientIdentifier))
  225. fmt.Println("Version:", sv.VersionWithMeta)
  226. if gitCommit != "" {
  227. fmt.Println("Git Commit:", gitCommit)
  228. }
  229. fmt.Println("Go Version:", runtime.Version())
  230. fmt.Println("OS:", runtime.GOOS)
  231. return nil
  232. }
  233. func bzzd(ctx *cli.Context) error {
  234. //build a valid bzzapi.Config from all available sources:
  235. //default config, file config, command line and env vars
  236. bzzconfig, err := buildConfig(ctx)
  237. if err != nil {
  238. utils.Fatalf("unable to configure swarm: %v", err)
  239. }
  240. cfg := defaultNodeConfig
  241. //pss operates on ws
  242. cfg.WSModules = append(cfg.WSModules, "pss")
  243. //geth only supports --datadir via command line
  244. //in order to be consistent within swarm, if we pass --datadir via environment variable
  245. //or via config file, we get the same directory for geth and swarm
  246. if _, err := os.Stat(bzzconfig.Path); err == nil {
  247. cfg.DataDir = bzzconfig.Path
  248. }
  249. //optionally set the bootnodes before configuring the node
  250. setSwarmBootstrapNodes(ctx, &cfg)
  251. //setup the ethereum node
  252. utils.SetNodeConfig(ctx, &cfg)
  253. //always disable discovery from p2p package - swarm discovery is done with the `hive` protocol
  254. cfg.P2P.NoDiscovery = true
  255. stack, err := node.New(&cfg)
  256. if err != nil {
  257. utils.Fatalf("can't create node: %v", err)
  258. }
  259. defer stack.Close()
  260. //a few steps need to be done after the config phase is completed,
  261. //due to overriding behavior
  262. initSwarmNode(bzzconfig, stack, ctx)
  263. //register BZZ as node.Service in the ethereum node
  264. registerBzzService(bzzconfig, stack)
  265. //start the node
  266. utils.StartNode(stack)
  267. go func() {
  268. sigc := make(chan os.Signal, 1)
  269. signal.Notify(sigc, syscall.SIGTERM)
  270. defer signal.Stop(sigc)
  271. <-sigc
  272. log.Info("Got sigterm, shutting swarm down...")
  273. stack.Stop()
  274. }()
  275. // add swarm bootnodes, because swarm doesn't use p2p package's discovery discv5
  276. go func() {
  277. s := stack.Server()
  278. for _, n := range cfg.P2P.BootstrapNodes {
  279. s.AddPeer(n)
  280. }
  281. }()
  282. stack.Wait()
  283. return nil
  284. }
  285. func registerBzzService(bzzconfig *bzzapi.Config, stack *node.Node) {
  286. //define the swarm service boot function
  287. boot := func(_ *node.ServiceContext) (node.Service, error) {
  288. // In production, mockStore must be always nil.
  289. return swarm.NewSwarm(bzzconfig, nil)
  290. }
  291. //register within the ethereum node
  292. if err := stack.Register(boot); err != nil {
  293. utils.Fatalf("Failed to register the Swarm service: %v", err)
  294. }
  295. }
  296. func getAccount(bzzaccount string, ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
  297. //an account is mandatory
  298. if bzzaccount == "" {
  299. utils.Fatalf(SWARM_ERR_NO_BZZACCOUNT)
  300. }
  301. // Try to load the arg as a hex key file.
  302. if key, err := crypto.LoadECDSA(bzzaccount); err == nil {
  303. log.Info("Swarm account key loaded", "address", crypto.PubkeyToAddress(key.PublicKey))
  304. return key
  305. }
  306. // Otherwise try getting it from the keystore.
  307. am := stack.AccountManager()
  308. ks := am.Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
  309. return decryptStoreAccount(ks, bzzaccount, utils.MakePasswordList(ctx))
  310. }
  311. // getPrivKey returns the private key of the specified bzzaccount
  312. // Used only by client commands, such as `feed`
  313. func getPrivKey(ctx *cli.Context) *ecdsa.PrivateKey {
  314. // booting up the swarm node just as we do in bzzd action
  315. bzzconfig, err := buildConfig(ctx)
  316. if err != nil {
  317. utils.Fatalf("unable to configure swarm: %v", err)
  318. }
  319. cfg := defaultNodeConfig
  320. if _, err := os.Stat(bzzconfig.Path); err == nil {
  321. cfg.DataDir = bzzconfig.Path
  322. }
  323. utils.SetNodeConfig(ctx, &cfg)
  324. stack, err := node.New(&cfg)
  325. if err != nil {
  326. utils.Fatalf("can't create node: %v", err)
  327. }
  328. defer stack.Close()
  329. return getAccount(bzzconfig.BzzAccount, ctx, stack)
  330. }
  331. func decryptStoreAccount(ks *keystore.KeyStore, account string, passwords []string) *ecdsa.PrivateKey {
  332. var a accounts.Account
  333. var err error
  334. if common.IsHexAddress(account) {
  335. a, err = ks.Find(accounts.Account{Address: common.HexToAddress(account)})
  336. } else if ix, ixerr := strconv.Atoi(account); ixerr == nil && ix > 0 {
  337. if accounts := ks.Accounts(); len(accounts) > ix {
  338. a = accounts[ix]
  339. } else {
  340. err = fmt.Errorf("index %d higher than number of accounts %d", ix, len(accounts))
  341. }
  342. } else {
  343. utils.Fatalf("Can't find swarm account key %s", account)
  344. }
  345. if err != nil {
  346. utils.Fatalf("Can't find swarm account key: %v - Is the provided bzzaccount(%s) from the right datadir/Path?", err, account)
  347. }
  348. keyjson, err := ioutil.ReadFile(a.URL.Path)
  349. if err != nil {
  350. utils.Fatalf("Can't load swarm account key: %v", err)
  351. }
  352. for i := 0; i < 3; i++ {
  353. password := getPassPhrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i+1), i, passwords)
  354. key, err := keystore.DecryptKey(keyjson, password)
  355. if err == nil {
  356. return key.PrivateKey
  357. }
  358. }
  359. utils.Fatalf("Can't decrypt swarm account key")
  360. return nil
  361. }
  362. // getPassPhrase retrieves the password associated with bzz account, either by fetching
  363. // from a list of pre-loaded passwords, or by requesting it interactively from user.
  364. func getPassPhrase(prompt string, i int, passwords []string) string {
  365. // non-interactive
  366. if len(passwords) > 0 {
  367. if i < len(passwords) {
  368. return passwords[i]
  369. }
  370. return passwords[len(passwords)-1]
  371. }
  372. // fallback to interactive mode
  373. if prompt != "" {
  374. fmt.Println(prompt)
  375. }
  376. password, err := console.Stdin.PromptPassword("Passphrase: ")
  377. if err != nil {
  378. utils.Fatalf("Failed to read passphrase: %v", err)
  379. }
  380. return password
  381. }
  382. // addDefaultHelpSubcommand scans through defined CLI commands and adds
  383. // a basic help subcommand to each
  384. // if a help command is already defined, it will take precedence over the default.
  385. func addDefaultHelpSubcommands(commands []cli.Command) {
  386. for i := range commands {
  387. cmd := &commands[i]
  388. if cmd.Subcommands != nil {
  389. cmd.Subcommands = append(cmd.Subcommands, defaultSubcommandHelp)
  390. addDefaultHelpSubcommands(cmd.Subcommands)
  391. }
  392. }
  393. }
  394. func setSwarmBootstrapNodes(ctx *cli.Context, cfg *node.Config) {
  395. if ctx.GlobalIsSet(utils.BootnodesFlag.Name) || ctx.GlobalIsSet(utils.BootnodesV4Flag.Name) {
  396. return
  397. }
  398. cfg.P2P.BootstrapNodes = []*enode.Node{}
  399. for _, url := range SwarmBootnodes {
  400. node, err := enode.ParseV4(url)
  401. if err != nil {
  402. log.Error("Bootstrap URL invalid", "enode", url, "err", err)
  403. }
  404. cfg.P2P.BootstrapNodes = append(cfg.P2P.BootstrapNodes, node)
  405. }
  406. log.Debug("added default swarm bootnodes", "length", len(cfg.P2P.BootstrapNodes))
  407. }