feeds.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  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. // Command feed allows the user to create and update signed Swarm feeds
  17. package main
  18. import (
  19. "fmt"
  20. "strings"
  21. "github.com/ethereum/go-ethereum/common"
  22. "github.com/ethereum/go-ethereum/common/hexutil"
  23. "github.com/ethereum/go-ethereum/crypto"
  24. "github.com/ethereum/go-ethereum/cmd/utils"
  25. swarm "github.com/ethereum/go-ethereum/swarm/api/client"
  26. "github.com/ethereum/go-ethereum/swarm/storage/feed"
  27. "gopkg.in/urfave/cli.v1"
  28. )
  29. var feedCommand = cli.Command{
  30. CustomHelpTemplate: helpTemplate,
  31. Name: "feed",
  32. Usage: "(Advanced) Create and update Swarm Feeds",
  33. ArgsUsage: "<create|update|info>",
  34. Description: "Works with Swarm Feeds",
  35. Subcommands: []cli.Command{
  36. {
  37. Action: feedCreateManifest,
  38. CustomHelpTemplate: helpTemplate,
  39. Name: "create",
  40. Usage: "creates and publishes a new feed manifest",
  41. Description: `creates and publishes a new feed manifest pointing to a specified user's updates about a particular topic.
  42. The feed topic can be built in the following ways:
  43. * use --topic to set the topic to an arbitrary binary hex string.
  44. * use --name to set the topic to a human-readable name.
  45. For example --name could be set to "profile-picture", meaning this feed allows to get this user's current profile picture.
  46. * use both --topic and --name to create named subtopics.
  47. For example, --topic could be set to an Ethereum contract address and --name could be set to "comments", meaning
  48. this feed tracks a discussion about that contract.
  49. The --user flag allows to have this manifest refer to a user other than yourself. If not specified,
  50. it will then default to your local account (--bzzaccount)`,
  51. Flags: []cli.Flag{SwarmFeedNameFlag, SwarmFeedTopicFlag, SwarmFeedUserFlag},
  52. },
  53. {
  54. Action: feedUpdate,
  55. CustomHelpTemplate: helpTemplate,
  56. Name: "update",
  57. Usage: "updates the content of an existing Swarm Feed",
  58. ArgsUsage: "<0x Hex data>",
  59. Description: `publishes a new update on the specified topic
  60. The feed topic can be built in the following ways:
  61. * use --topic to set the topic to an arbitrary binary hex string.
  62. * use --name to set the topic to a human-readable name.
  63. For example --name could be set to "profile-picture", meaning this feed allows to get this user's current profile picture.
  64. * use both --topic and --name to create named subtopics.
  65. For example, --topic could be set to an Ethereum contract address and --name could be set to "comments", meaning
  66. this feed tracks a discussion about that contract.
  67. If you have a manifest, you can specify it with --manifest to refer to the feed,
  68. instead of using --topic / --name
  69. `,
  70. Flags: []cli.Flag{SwarmFeedManifestFlag, SwarmFeedNameFlag, SwarmFeedTopicFlag},
  71. },
  72. {
  73. Action: feedInfo,
  74. CustomHelpTemplate: helpTemplate,
  75. Name: "info",
  76. Usage: "obtains information about an existing Swarm feed",
  77. Description: `obtains information about an existing Swarm feed
  78. The topic can be specified directly with the --topic flag as an hex string
  79. If no topic is specified, the default topic (zero) will be used
  80. The --name flag can be used to specify subtopics with a specific name.
  81. The --user flag allows to refer to a user other than yourself. If not specified,
  82. it will then default to your local account (--bzzaccount)
  83. If you have a manifest, you can specify it with --manifest instead of --topic / --name / ---user
  84. to refer to the feed`,
  85. Flags: []cli.Flag{SwarmFeedManifestFlag, SwarmFeedNameFlag, SwarmFeedTopicFlag, SwarmFeedUserFlag},
  86. },
  87. },
  88. }
  89. func NewGenericSigner(ctx *cli.Context) feed.Signer {
  90. return feed.NewGenericSigner(getPrivKey(ctx))
  91. }
  92. func getTopic(ctx *cli.Context) (topic feed.Topic) {
  93. var name = ctx.String(SwarmFeedNameFlag.Name)
  94. var relatedTopic = ctx.String(SwarmFeedTopicFlag.Name)
  95. var relatedTopicBytes []byte
  96. var err error
  97. if relatedTopic != "" {
  98. relatedTopicBytes, err = hexutil.Decode(relatedTopic)
  99. if err != nil {
  100. utils.Fatalf("Error parsing topic: %s", err)
  101. }
  102. }
  103. topic, err = feed.NewTopic(name, relatedTopicBytes)
  104. if err != nil {
  105. utils.Fatalf("Error parsing topic: %s", err)
  106. }
  107. return topic
  108. }
  109. // swarm feed create <frequency> [--name <name>] [--data <0x Hexdata> [--multihash=false]]
  110. // swarm feed update <Manifest Address or ENS domain> <0x Hexdata> [--multihash=false]
  111. // swarm feed info <Manifest Address or ENS domain>
  112. func feedCreateManifest(ctx *cli.Context) {
  113. var (
  114. bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  115. client = swarm.NewClient(bzzapi)
  116. )
  117. newFeedUpdateRequest := feed.NewFirstRequest(getTopic(ctx))
  118. newFeedUpdateRequest.Feed.User = feedGetUser(ctx)
  119. manifestAddress, err := client.CreateFeedWithManifest(newFeedUpdateRequest)
  120. if err != nil {
  121. utils.Fatalf("Error creating feed manifest: %s", err.Error())
  122. return
  123. }
  124. fmt.Println(manifestAddress) // output manifest address to the user in a single line (useful for other commands to pick up)
  125. }
  126. func feedUpdate(ctx *cli.Context) {
  127. args := ctx.Args()
  128. var (
  129. bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  130. client = swarm.NewClient(bzzapi)
  131. manifestAddressOrDomain = ctx.String(SwarmFeedManifestFlag.Name)
  132. )
  133. if len(args) < 1 {
  134. fmt.Println("Incorrect number of arguments")
  135. cli.ShowCommandHelpAndExit(ctx, "update", 1)
  136. return
  137. }
  138. signer := NewGenericSigner(ctx)
  139. data, err := hexutil.Decode(args[0])
  140. if err != nil {
  141. utils.Fatalf("Error parsing data: %s", err.Error())
  142. return
  143. }
  144. var updateRequest *feed.Request
  145. var query *feed.Query
  146. if manifestAddressOrDomain == "" {
  147. query = new(feed.Query)
  148. query.User = signer.Address()
  149. query.Topic = getTopic(ctx)
  150. }
  151. // Retrieve a feed update request
  152. updateRequest, err = client.GetFeedRequest(query, manifestAddressOrDomain)
  153. if err != nil {
  154. utils.Fatalf("Error retrieving feed status: %s", err.Error())
  155. }
  156. // Check that the provided signer matches the request to sign
  157. if updateRequest.User != signer.Address() {
  158. utils.Fatalf("Signer address does not match the update request")
  159. }
  160. // set the new data
  161. updateRequest.SetData(data)
  162. // sign update
  163. if err = updateRequest.Sign(signer); err != nil {
  164. utils.Fatalf("Error signing feed update: %s", err.Error())
  165. }
  166. // post update
  167. err = client.UpdateFeed(updateRequest)
  168. if err != nil {
  169. utils.Fatalf("Error updating feed: %s", err.Error())
  170. return
  171. }
  172. }
  173. func feedInfo(ctx *cli.Context) {
  174. var (
  175. bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
  176. client = swarm.NewClient(bzzapi)
  177. manifestAddressOrDomain = ctx.String(SwarmFeedManifestFlag.Name)
  178. )
  179. var query *feed.Query
  180. if manifestAddressOrDomain == "" {
  181. query = new(feed.Query)
  182. query.Topic = getTopic(ctx)
  183. query.User = feedGetUser(ctx)
  184. }
  185. metadata, err := client.GetFeedRequest(query, manifestAddressOrDomain)
  186. if err != nil {
  187. utils.Fatalf("Error retrieving feed metadata: %s", err.Error())
  188. return
  189. }
  190. encodedMetadata, err := metadata.MarshalJSON()
  191. if err != nil {
  192. utils.Fatalf("Error encoding metadata to JSON for display:%s", err)
  193. }
  194. fmt.Println(string(encodedMetadata))
  195. }
  196. func feedGetUser(ctx *cli.Context) common.Address {
  197. var user = ctx.String(SwarmFeedUserFlag.Name)
  198. if user != "" {
  199. return common.HexToAddress(user)
  200. }
  201. pk := getPrivKey(ctx)
  202. if pk == nil {
  203. utils.Fatalf("Cannot read private key. Must specify --user or --bzzaccount")
  204. }
  205. return crypto.PubkeyToAddress(pk.PublicKey)
  206. }