main.go 14 KB

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