Skip to content
This repository was archived by the owner on Aug 2, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions cmd/swarm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,18 @@ var (
Usage: "Number of recent chunks cached in memory (default 5000)",
EnvVar: SWARM_ENV_STORE_CACHE_CAPACITY,
}
SwarmResourceRawFlag = cli.BoolFlag{
Name: "rawmru",
SwarmResourceMultihashFlag = cli.BoolTFlag{
Name: "multihash",
Usage: "Determines how to interpret data for a resource update. If not present, data will be interpreted as a multihash",
}
SwarmResourceNameFlag = cli.StringFlag{
Name: "name",
Usage: "User-defined name for the new resource",
}
SwarmResourceDataOnCreateFlag = cli.StringFlag{
Name: "data",
Usage: "Initializes the resource with the given hex-encoded data. Data must be prefixed by 0x",
}
)

//declare a few constant error messages, useful for later error check comparisons in test
Expand Down Expand Up @@ -245,9 +253,9 @@ func init() {
CustomHelpTemplate: helpTemplate,
Name: "create",
Usage: "creates a new Mutable Resource",
ArgsUsage: "<name> <frequency> [--rawmru] <0x Hex data>",
ArgsUsage: "<frequency>",
Description: "creates a new Mutable Resource",
Flags: []cli.Flag{SwarmResourceRawFlag},
Flags: []cli.Flag{SwarmResourceNameFlag, SwarmResourceDataOnCreateFlag, SwarmResourceMultihashFlag},
},
{
Action: resourceUpdate,
Expand All @@ -256,6 +264,7 @@ func init() {
Usage: "updates the content of an existing Mutable Resource",
ArgsUsage: "<Manifest Address or ENS domain> <0x Hex data>",
Description: "updates the content of an existing Mutable Resource",
Flags: []cli.Flag{SwarmResourceMultihashFlag},
},
{
Action: resourceInfo,
Expand Down
86 changes: 65 additions & 21 deletions cmd/swarm/mru.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,65 +30,109 @@ import (
"gopkg.in/urfave/cli.v1"
)

// swarm resource create <name> <frequency> [--rawmru] <0x Hexdata>
// swarm resource update <Manifest Address or ENS domain> <0x Hexdata>
func NewGenericSigner(ctx *cli.Context) mru.Signer {
return mru.NewGenericSigner(getClientAccount(ctx))
}

// swarm resource create <frequency> [--name <name>] [--data <0x Hexdata> [--multihash=false]]
// swarm resource update <Manifest Address or ENS domain> <0x Hexdata> [--multihash=false]
// swarm resource info <Manifest Address or ENS domain>

func resourceCreate(ctx *cli.Context) {
args := ctx.Args()

var (
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
client = swarm.NewClient(bzzapi)
rawResource = ctx.Bool(SwarmResourceRawFlag.Name)
multihash = ctx.Bool(SwarmResourceMultihashFlag.Name)
initialData = ctx.String(SwarmResourceDataOnCreateFlag.Name)
name = ctx.String(SwarmResourceNameFlag.Name)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we create a resource without a name?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Name is optional. If you don't put the name flag, then the resource name is an empty string.

)

if len(args) < 3 {
if len(args) < 1 {
fmt.Println("Incorrect number of arguments")
cli.ShowCommandHelpAndExit(ctx, "create", 1)
return
}
signer := mru.NewGenericSigner(getClientAccount(ctx))

name := args[0]
frequency, err := strconv.ParseUint(args[1], 10, 64)
signer := NewGenericSigner(ctx)
frequency, err := strconv.ParseUint(args[0], 10, 64)
if err != nil {
utils.Fatalf("Frequency formatting error: %s", err.Error())
fmt.Printf("Frequency formatting error: %s\n", err.Error())
cli.ShowCommandHelpAndExit(ctx, "create", 1)
return
}

data, err := hexutil.Decode(args[2])
newResourceRequest, err := mru.NewCreateRequest(&mru.ResourceMetadata{
Name: name,
Frequency: frequency,
Owner: signer.Address(),
})

if err != nil {
utils.Fatalf("Error parsing data: %s", err.Error())
return
utils.Fatalf("Error creating new resource request: %s", err)
}

if initialData != "" {
initialDataBytes, err := hexutil.Decode(initialData)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we allow...

  1. utf8 data?
  2. binary data? (| from stdin)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the most useful would be stdin. Noted here: #781

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So UTF8 and bin via stdin?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by UTF-8?. You already can use UTF8 or anything. That's application-specific encoding. All we are providing is to store a byte array. For simplicity, that byte array is hex-encoded in the --data parameter (for now).
Whether that hex-encoded byte array represents text in UTF-8 or whatever would be the application developer's choice.

The same applies for stdin-incoming data. Whether it is a byte stream coming from an UTF-8 encoder or not does not concern us.

Copy link
Contributor

@nolash nolash Jul 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I meant was that I'd like to see that adding an update was as easy as echo föö | swarm resource update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to do anything special to support UTF-8. It is already part of the output of echo, that's what I mean it is an application thing. Check this out:

$ echo "föö" |hexdump -C
00000000  66 c3 b6 c3 b6 0a                                 |f.....|

Three visual characters (+ newline) turned into 5 chars (+ newline, 0a), the ö taking two bytes each. Those 6 chars are the ones that would go into our stdin and be stored in the resource data.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine!

if err != nil {
fmt.Printf("Error parsing data: %s\n", err.Error())
cli.ShowCommandHelpAndExit(ctx, "create", 1)
return
}

newResourceRequest.SetData(initialDataBytes, multihash)
if err = newResourceRequest.Sign(signer); err != nil {
utils.Fatalf("Error signing resource update: %s", err.Error())
}
}
manifestAddress, err := client.CreateResource(name, frequency, 0, data, !rawResource, signer)

manifestAddress, err := client.CreateResource(newResourceRequest)
if err != nil {
utils.Fatalf("Error creating resource: %s", err.Error())
return
}
fmt.Println(manifestAddress) // output address to the user in a single line (useful for other commands to pick up)
fmt.Println(manifestAddress) // output manifest address to the user in a single line (useful for other commands to pick up)

}

func resourceUpdate(ctx *cli.Context) {
args := ctx.Args()

var (
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
client = swarm.NewClient(bzzapi)
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
client = swarm.NewClient(bzzapi)
multihash = ctx.Bool(SwarmResourceMultihashFlag.Name)
)

if len(args) < 2 {
fmt.Println("Incorrect number of arguments")
cli.ShowCommandHelpAndExit(ctx, "update", 1)
return
}
signer := mru.NewGenericSigner(getClientAccount(ctx))
manifestAddressOrDomain := args[1]
data, err := hexutil.Decode(args[2])
signer := NewGenericSigner(ctx)
manifestAddressOrDomain := args[0]
data, err := hexutil.Decode(args[1])
if err != nil {
utils.Fatalf("Error parsing data: %s", err.Error())
return
}
err = client.UpdateResource(manifestAddressOrDomain, data, signer)

// Retrieve resource status and metadata out of the manifest
updateRequest, err := client.GetResourceMetadata(manifestAddressOrDomain)
if err != nil {
utils.Fatalf("Error retrieving resource status: %s", err.Error())
}

// set the new data
updateRequest.SetData(data, multihash)

// sign update
if err = updateRequest.Sign(signer); err != nil {
utils.Fatalf("Error signing resource update: %s", err.Error())
}

// post update
err = client.UpdateResource(updateRequest)
if err != nil {
utils.Fatalf("Error updating resource: %s", err.Error())
return
Expand All @@ -112,7 +156,7 @@ func resourceInfo(ctx *cli.Context) {
utils.Fatalf("Error retrieving resource metadata: %s", err.Error())
return
}
encodedMetadata, err := mru.EncodeUpdateRequest(metadata)
encodedMetadata, err := metadata.MarshalJSON()
if err != nil {
utils.Fatalf("Error encoding metadata to JSON for display:%s", err)
}
Expand Down
27 changes: 7 additions & 20 deletions swarm/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ func (a *API) BuildDirectoryTree(ctx context.Context, mhash string, nameresolver
return addr, manifestEntryMap, nil
}

// Look up mutable resource updates at specific periods and versions
// ResourceLookup finds mutable resource updates at specific periods and versions
func (a *API) ResourceLookup(ctx context.Context, params *mru.LookupParams) (string, []byte, error) {
var err error
rsrc, err := a.resource.Load(params.RootAddr())
Expand All @@ -674,33 +674,20 @@ func (a *API) ResourceLookup(ctx context.Context, params *mru.LookupParams) (str
return rsrc.Name(), data, nil
}

func (a *API) ResourceCreate(ctx context.Context, mru *mru.Request) (storage.Address, error) {
err := a.resource.New(ctx, mru)
if err != nil {
return nil, err
}
return mru.RootAddr(), nil
// Create Mutable resource
func (a *API) ResourceCreate(ctx context.Context, request *mru.Request) error {
return a.resource.New(ctx, request)
}

// ResourceUpdateMultihash updates a Mutable Resource and marks the update's content to be of multihash type, which will be recognized upon retrieval.
// It will fail if the data is not a valid multihash.
// ResourceNewRequest creates a Request object to update a specific mutable resource
func (a *API) ResourceNewRequest(ctx context.Context, rootAddr storage.Address) (*mru.Request, error) {
return a.resource.NewUpdateRequest(ctx, rootAddr)
}

// ResourceUpdate updates a Mutable Resource with arbitrary data.
// Upon retrieval the update will be retrieved verbatim as bytes.
func (a *API) ResourceUpdate(ctx context.Context, rootAddr storage.Address, mru *mru.SignedResourceUpdate) (storage.Address, uint32, uint32, error) {
return a.resourceUpdate(ctx, rootAddr, mru)
}

func (a *API) resourceUpdate(ctx context.Context, rootAddr storage.Address, mru *mru.SignedResourceUpdate) (storage.Address, uint32, uint32, error) {
var updateAddr storage.Address
var err error
updateAddr, err = a.resource.Update(ctx, rootAddr, mru)
period, _ := a.resource.GetLastPeriod(rootAddr)
version, _ := a.resource.GetVersion(rootAddr)
return updateAddr, period, version, err
func (a *API) ResourceUpdate(ctx context.Context, request *mru.SignedResourceUpdate) (storage.Address, error) {
return a.resource.Update(ctx, request)
}

// ResourceHashSize returned the size of the digest produced by the Mutable Resource hashing function
Expand Down
48 changes: 13 additions & 35 deletions swarm/api/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -569,18 +569,8 @@ func (c *Client) MultipartUpload(hash string, uploader Uploader) (string, error)
// startTime=0 means "now"
// Returns the resulting Mutable Resource manifest address that you can use to include in an ENS Resolver (setContent)
// or reference future updates (Client.UpdateResource)
func (c *Client) CreateResource(name string, frequency, startTime uint64, data []byte, multihash bool, signer mru.Signer) (string, error) {
createRequest, err := mru.NewCreateRequest(name, frequency, startTime, signer.Address(), data, multihash)
if err != nil {
return "", err
}

createRequest.Sign(signer)
if err != nil {
return "", err
}

responseStream, err := c.updateResource(createRequest, "")
func (c *Client) CreateResource(request *mru.Request) (string, error) {
responseStream, err := c.updateResource(request)
if err != nil {
return "", err
}
Expand All @@ -591,38 +581,26 @@ func (c *Client) CreateResource(name string, frequency, startTime uint64, data [
return "", err
}

var manifestKey string
if err = json.Unmarshal(body, &manifestKey); err != nil {
var manifestAddress string
if err = json.Unmarshal(body, &manifestAddress); err != nil {
return "", err
}
return manifestKey, nil
return manifestAddress, nil
}

// UpdateResource allows you to send a new version of your content
// manifestAddressOrDomain is the address you obtained in CreateResource or an ENS domain whose Resolver
// points to that address
func (c *Client) UpdateResource(manifestAddressOrDomain string, data []byte, signer mru.Signer) error {
updateRequest, err := c.GetResourceMetadata(manifestAddressOrDomain)
if err != nil {
return err
}
updateRequest.SetData(data)
updateRequest.Sign(signer)
if err != nil {
return err
}

_, err = c.updateResource(updateRequest, manifestAddressOrDomain)
func (c *Client) UpdateResource(request *mru.Request) error {
_, err := c.updateResource(request)
return err
}

func (c *Client) updateResource(updateRequest *mru.Request, manifestAddressOrDomain string) (io.ReadCloser, error) {
body, err := mru.EncodeUpdateRequest(updateRequest)
func (c *Client) updateResource(request *mru.Request) (io.ReadCloser, error) {
body, err := request.MarshalJSON()
if err != nil {
return nil, err
}

req, err := http.NewRequest("POST", c.Gateway+"/bzz-resource:/"+manifestAddressOrDomain, bytes.NewBuffer(body))
req, err := http.NewRequest("POST", c.Gateway+"/bzz-resource:/", bytes.NewBuffer(body))
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -665,9 +643,9 @@ func (c *Client) GetResourceMetadata(manifestAddressOrDomain string) (*mru.Reque
return nil, err
}

metadata, err := mru.DecodeUpdateRequest(body)
if err != nil {
var metadata mru.Request
if err := metadata.UnmarshalJSON(body); err != nil {
return nil, err
}
return metadata, nil
return &metadata, nil
}
49 changes: 43 additions & 6 deletions swarm/api/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -394,12 +394,27 @@ func TestClientCreateResourceMultihash(t *testing.T) {
// our mutable resource "name"
resourceName := "foo.eth"

resourceManifestHash, err := client.CreateResource(resourceName, 13, srv.GetCurrentTime().Time, mh, true, signer)
createRequest, err := mru.NewCreateRequest(&mru.ResourceMetadata{
Name: resourceName,
Frequency: 13,
StartTime: srv.GetCurrentTime(),
Owner: signer.Address(),
})
if err != nil {
t.Fatal(err)
}
createRequest.SetData(mh, true)
if err := createRequest.Sign(signer); err != nil {
t.Fatalf("Error signing update: %s", err)
}

resourceManifestHash, err := client.CreateResource(createRequest)

if err != nil {
t.Fatalf("Error creating resource: %s", err)
}

correctManifestAddrHex := "19e911ba30608cc70af7cad3776501366a7ccf818025f2b3010794d9d66d7eb5"
correctManifestAddrHex := "6d3bc4664c97d8b821cb74bcae43f592494fb46d2d9cd31e69f3c7c802bbbd8e"
if resourceManifestHash != correctManifestAddrHex {
t.Fatalf("Response resource key mismatch, expected '%s', got '%s'", correctManifestAddrHex, resourceManifestHash)
}
Expand All @@ -419,6 +434,7 @@ func TestClientCreateResourceMultihash(t *testing.T) {

}

// TestClientCreateUpdateResource will check that mutable resources can be created and updated via the HTTP client.
func TestClientCreateUpdateResource(t *testing.T) {

signer, _ := newTestSigner()
Expand All @@ -433,12 +449,23 @@ func TestClientCreateUpdateResource(t *testing.T) {
// our mutable resource name
resourceName := "El Quijote"

resourceManifestHash, err := client.CreateResource(resourceName, 13, srv.GetCurrentTime().Time, databytes, false, signer)
createRequest, err := mru.NewCreateRequest(&mru.ResourceMetadata{
Name: resourceName,
Frequency: 13,
StartTime: srv.GetCurrentTime(),
Owner: signer.Address(),
})
if err != nil {
t.Fatalf("Error creating resource: %s", err)
t.Fatal(err)
}
createRequest.SetData(databytes, false)
if err := createRequest.Sign(signer); err != nil {
t.Fatalf("Error signing update: %s", err)
}

correctManifestAddrHex := "a09bdcc9e0e4762e01d3e0a69a00f673fa67e8ac17e647043ce07672424ba014"
resourceManifestHash, err := client.CreateResource(createRequest)

correctManifestAddrHex := "cc7904c17b49f9679e2d8006fe25e87e3f5c2072c2b49cab50f15e544471b30a"
if resourceManifestHash != correctManifestAddrHex {
t.Fatalf("Response resource key mismatch, expected '%s', got '%s'", correctManifestAddrHex, resourceManifestHash)
}
Expand All @@ -459,7 +486,17 @@ func TestClientCreateUpdateResource(t *testing.T) {
// define different data
databytes = []byte("... no ha mucho tiempo que vivía un hidalgo de los de lanza en astillero ...")

if err = client.UpdateResource(correctManifestAddrHex, databytes, signer); err != nil {
updateRequest, err := client.GetResourceMetadata(correctManifestAddrHex)
if err != nil {
t.Fatalf("Error retrieving update request template: %s", err)
}

updateRequest.SetData(databytes, false)
if err := updateRequest.Sign(signer); err != nil {
t.Fatalf("Error signing update: %s", err)
}

if err = client.UpdateResource(updateRequest); err != nil {
t.Fatalf("Error updating resource: %s", err)
}

Expand Down
Loading