main.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  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. "fmt"
  20. "io/ioutil"
  21. "os"
  22. "runtime"
  23. "strconv"
  24. "github.com/ethereum/go-ethereum/accounts"
  25. "github.com/ethereum/go-ethereum/cmd/utils"
  26. "github.com/ethereum/go-ethereum/common"
  27. "github.com/ethereum/go-ethereum/console"
  28. "github.com/ethereum/go-ethereum/crypto"
  29. "github.com/ethereum/go-ethereum/ethclient"
  30. "github.com/ethereum/go-ethereum/internal/debug"
  31. "github.com/ethereum/go-ethereum/logger"
  32. "github.com/ethereum/go-ethereum/logger/glog"
  33. "github.com/ethereum/go-ethereum/node"
  34. "github.com/ethereum/go-ethereum/p2p"
  35. "github.com/ethereum/go-ethereum/p2p/discover"
  36. "github.com/ethereum/go-ethereum/swarm"
  37. bzzapi "github.com/ethereum/go-ethereum/swarm/api"
  38. "gopkg.in/urfave/cli.v1"
  39. )
  40. const clientIdentifier = "bzzd"
  41. var (
  42. gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
  43. app = utils.NewApp(gitCommit, "Ethereum Swarm server daemon")
  44. )
  45. var (
  46. ChequebookAddrFlag = cli.StringFlag{
  47. Name: "chequebook",
  48. Usage: "chequebook contract address",
  49. }
  50. SwarmAccountFlag = cli.StringFlag{
  51. Name: "bzzaccount",
  52. Usage: "Swarm account key file",
  53. }
  54. SwarmPortFlag = cli.StringFlag{
  55. Name: "bzzport",
  56. Usage: "Swarm local http api port",
  57. }
  58. SwarmConfigPathFlag = cli.StringFlag{
  59. Name: "bzzconfig",
  60. Usage: "Swarm config file path (datadir/bzz)",
  61. }
  62. SwarmSwapDisabled = cli.BoolFlag{
  63. Name: "bzznoswap",
  64. Usage: "Swarm SWAP disabled (default false)",
  65. }
  66. SwarmSyncDisabled = cli.BoolFlag{
  67. Name: "bzznosync",
  68. Usage: "Swarm Syncing disabled (default false)",
  69. }
  70. EthAPI = cli.StringFlag{
  71. Name: "ethapi",
  72. Usage: "URL of the Ethereum API provider",
  73. Value: node.DefaultIPCEndpoint("geth"),
  74. }
  75. )
  76. var defaultBootnodes = []string{}
  77. func init() {
  78. // Override flag defaults so bzzd can run alongside geth.
  79. utils.ListenPortFlag.Value = 30399
  80. utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"}
  81. // Set up the cli app.
  82. app.Commands = nil
  83. app.Action = bzzd
  84. app.Flags = []cli.Flag{
  85. utils.IdentityFlag,
  86. utils.DataDirFlag,
  87. utils.BootnodesFlag,
  88. utils.KeyStoreDirFlag,
  89. utils.ListenPortFlag,
  90. utils.NetrestrictFlag,
  91. utils.MaxPeersFlag,
  92. utils.NATFlag,
  93. utils.NodeKeyFileFlag,
  94. utils.NodeKeyHexFlag,
  95. utils.IPCDisabledFlag,
  96. utils.IPCApiFlag,
  97. utils.IPCPathFlag,
  98. // bzzd-specific flags
  99. EthAPI,
  100. SwarmConfigPathFlag,
  101. SwarmSwapDisabled,
  102. SwarmSyncDisabled,
  103. SwarmPortFlag,
  104. SwarmAccountFlag,
  105. ChequebookAddrFlag,
  106. }
  107. app.Flags = append(app.Flags, debug.Flags...)
  108. app.Before = func(ctx *cli.Context) error {
  109. runtime.GOMAXPROCS(runtime.NumCPU())
  110. return debug.Setup(ctx)
  111. }
  112. app.After = func(ctx *cli.Context) error {
  113. debug.Exit()
  114. return nil
  115. }
  116. }
  117. func main() {
  118. if err := app.Run(os.Args); err != nil {
  119. fmt.Fprintln(os.Stderr, err)
  120. os.Exit(1)
  121. }
  122. }
  123. func bzzd(ctx *cli.Context) error {
  124. stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
  125. registerBzzService(ctx, stack)
  126. utils.StartNode(stack)
  127. // Add bootnodes as initial peers.
  128. if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
  129. injectBootnodes(stack.Server(), ctx.GlobalStringSlice(utils.BootnodesFlag.Name))
  130. } else {
  131. injectBootnodes(stack.Server(), defaultBootnodes)
  132. }
  133. stack.Wait()
  134. return nil
  135. }
  136. func registerBzzService(ctx *cli.Context, stack *node.Node) {
  137. prvkey := getAccount(ctx, stack)
  138. chbookaddr := common.HexToAddress(ctx.GlobalString(ChequebookAddrFlag.Name))
  139. bzzdir := ctx.GlobalString(SwarmConfigPathFlag.Name)
  140. if bzzdir == "" {
  141. bzzdir = stack.InstanceDir()
  142. }
  143. bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey)
  144. if err != nil {
  145. utils.Fatalf("unable to configure swarm: %v", err)
  146. }
  147. bzzport := ctx.GlobalString(SwarmPortFlag.Name)
  148. if len(bzzport) > 0 {
  149. bzzconfig.Port = bzzport
  150. }
  151. swapEnabled := !ctx.GlobalBool(SwarmSwapDisabled.Name)
  152. syncEnabled := !ctx.GlobalBool(SwarmSyncDisabled.Name)
  153. ethapi := ctx.GlobalString(EthAPI.Name)
  154. if ethapi == "" {
  155. utils.Fatalf("Option %q must not be empty", EthAPI.Name)
  156. }
  157. boot := func(ctx *node.ServiceContext) (node.Service, error) {
  158. client, err := ethclient.Dial(ethapi)
  159. if err != nil {
  160. utils.Fatalf("Can't connect: %v", err)
  161. }
  162. return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled)
  163. }
  164. if err := stack.Register(boot); err != nil {
  165. utils.Fatalf("Failed to register the Swarm service: %v", err)
  166. }
  167. }
  168. func getAccount(ctx *cli.Context, stack *node.Node) *ecdsa.PrivateKey {
  169. keyid := ctx.GlobalString(SwarmAccountFlag.Name)
  170. if keyid == "" {
  171. utils.Fatalf("Option %q is required", SwarmAccountFlag.Name)
  172. }
  173. // Try to load the arg as a hex key file.
  174. if key, err := crypto.LoadECDSA(keyid); err == nil {
  175. glog.V(logger.Info).Infof("swarm account key loaded: %#x", crypto.PubkeyToAddress(key.PublicKey))
  176. return key
  177. }
  178. // Otherwise try getting it from the keystore.
  179. return decryptStoreAccount(stack.AccountManager(), keyid)
  180. }
  181. func decryptStoreAccount(accman *accounts.Manager, account string) *ecdsa.PrivateKey {
  182. var a accounts.Account
  183. var err error
  184. if common.IsHexAddress(account) {
  185. a, err = accman.Find(accounts.Account{Address: common.HexToAddress(account)})
  186. } else if ix, ixerr := strconv.Atoi(account); ixerr == nil {
  187. a, err = accman.AccountByIndex(ix)
  188. } else {
  189. utils.Fatalf("Can't find swarm account key %s", account)
  190. }
  191. if err != nil {
  192. utils.Fatalf("Can't find swarm account key: %v", err)
  193. }
  194. keyjson, err := ioutil.ReadFile(a.File)
  195. if err != nil {
  196. utils.Fatalf("Can't load swarm account key: %v", err)
  197. }
  198. for i := 1; i <= 3; i++ {
  199. passphrase := promptPassphrase(fmt.Sprintf("Unlocking swarm account %s [%d/3]", a.Address.Hex(), i))
  200. key, err := accounts.DecryptKey(keyjson, passphrase)
  201. if err == nil {
  202. return key.PrivateKey
  203. }
  204. }
  205. utils.Fatalf("Can't decrypt swarm account key")
  206. return nil
  207. }
  208. func promptPassphrase(prompt string) string {
  209. if prompt != "" {
  210. fmt.Println(prompt)
  211. }
  212. password, err := console.Stdin.PromptPassword("Passphrase: ")
  213. if err != nil {
  214. utils.Fatalf("Failed to read passphrase: %v", err)
  215. }
  216. return password
  217. }
  218. func injectBootnodes(srv *p2p.Server, nodes []string) {
  219. for _, url := range nodes {
  220. n, err := discover.ParseNode(url)
  221. if err != nil {
  222. glog.Errorf("invalid bootnode %q", err)
  223. }
  224. srv.AddPeer(n)
  225. }
  226. }