main.go 13 KB

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