Skip to content
This repository was archived by the owner on Aug 2, 2021. It is now read-only.

Commit 85e2318

Browse files
authored
Merge pull request #204 from ethersphere/swarm-mutableresources-apinew
HTTP API for mutable resources
2 parents f68e836 + ef5eeb6 commit 85e2318

File tree

16 files changed

+712
-301
lines changed

16 files changed

+712
-301
lines changed

cmd/swarm/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ func registerBzzService(bzzconfig *bzzapi.Config, ctx *cli.Context, stack *node.
560560
}
561561

562562
// In production, mockStore must be always nil.
563-
return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, bzzconfig.SwapEnabled, bzzconfig.SyncEnabled, bzzconfig.Cors, bzzconfig.PssEnabled, nil)
563+
return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, nil)
564564
}
565565
//register within the ethereum node
566566
if err := stack.Register(boot); err != nil {

swarm/api/api.go

Lines changed: 55 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package api
1818

1919
import (
20+
"context"
2021
"fmt"
2122
"io"
2223
"net/http"
@@ -46,15 +47,17 @@ on top of the dpa
4647
it is the public interface of the dpa which is included in the ethereum stack
4748
*/
4849
type Api struct {
49-
dpa *storage.DPA
50-
dns Resolver
50+
resource *storage.ResourceHandler
51+
dpa *storage.DPA
52+
dns Resolver
5153
}
5254

5355
//the api constructor initialises
54-
func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) {
56+
func NewApi(dpa *storage.DPA, dns Resolver, resourceHandler *storage.ResourceHandler) (self *Api) {
5557
self = &Api{
56-
dpa: dpa,
57-
dns: dns,
58+
dpa: dpa,
59+
dns: dns,
60+
resource: resourceHandler,
5861
}
5962
return
6063
}
@@ -361,3 +364,50 @@ func (self *Api) BuildDirectoryTree(mhash string, nameresolver bool) (key storag
361364
}
362365
return key, manifestEntryMap, nil
363366
}
367+
368+
// Look up mutable resource updates at specific periods and versions
369+
func (self *Api) ResourceLookup(ctx context.Context, name string, period uint32, version uint32) (storage.Key, []byte, error) {
370+
var err error
371+
if version != 0 {
372+
if period == 0 {
373+
currentblocknumber, err := self.resource.GetBlock(ctx)
374+
if err != nil {
375+
return nil, nil, fmt.Errorf("Could not determine latest block: %v", err)
376+
}
377+
period = self.resource.BlockToPeriod(name, currentblocknumber)
378+
}
379+
_, err = self.resource.LookupVersionByName(ctx, name, period, version, true)
380+
} else if period != 0 {
381+
_, err = self.resource.LookupHistoricalByName(ctx, name, period, true)
382+
} else {
383+
_, err = self.resource.LookupLatestByName(ctx, name, true)
384+
}
385+
if err != nil {
386+
return nil, nil, err
387+
}
388+
return self.resource.GetContent(name)
389+
}
390+
391+
func (self *Api) ResourceCreate(ctx context.Context, name string, frequency uint64) (storage.Key, error) {
392+
rsrc, err := self.resource.NewResource(ctx, name, frequency)
393+
if err != nil {
394+
return nil, err
395+
}
396+
h := rsrc.NameHash()
397+
return storage.Key(h[:]), nil
398+
}
399+
400+
func (self *Api) ResourceUpdate(ctx context.Context, name string, data []byte) (storage.Key, uint32, uint32, error) {
401+
key, err := self.resource.Update(ctx, name, data)
402+
period, _ := self.resource.GetLastPeriod(name)
403+
version, _ := self.resource.GetVersion(name)
404+
return key, period, version, err
405+
}
406+
407+
func (self *Api) ResourceHashSize() int {
408+
return self.resource.HashSize()
409+
}
410+
411+
func (self *Api) ResourceIsValidated() bool {
412+
return self.resource.IsValidated()
413+
}

swarm/api/api_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ func testApi(t *testing.T, f func(*Api)) {
4040
if err != nil {
4141
return
4242
}
43-
api := NewApi(dpa, nil)
43+
api := NewApi(dpa, nil, nil)
4444
dpa.Start()
4545
f(api)
4646
dpa.Stop()

swarm/api/config.go

Lines changed: 43 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -46,22 +46,24 @@ type Config struct {
4646
*network.HiveParams
4747
Swap *swap.SwapParams
4848
//*network.SyncParams
49-
Contract common.Address
50-
EnsRoot common.Address
51-
EnsApi string
52-
Path string
53-
ListenAddr string
54-
Port string
55-
PublicKey string
56-
BzzKey string
57-
NetworkId uint64
58-
SwapEnabled bool
59-
SyncEnabled bool
60-
PssEnabled bool
61-
SwapApi string
62-
Cors string
63-
BzzAccount string
64-
BootNodes string
49+
Contract common.Address
50+
EnsRoot common.Address
51+
EnsApi string
52+
Path string
53+
ListenAddr string
54+
Port string
55+
PublicKey string
56+
BzzKey string
57+
NetworkId uint64
58+
SwapEnabled bool
59+
SyncEnabled bool
60+
PssEnabled bool
61+
ResourceEnabled bool
62+
SwapApi string
63+
Cors string
64+
BzzAccount string
65+
BootNodes string
66+
privateKey *ecdsa.PrivateKey
6567
}
6668

6769
//create a default config with all parameters to set to defaults
@@ -72,18 +74,19 @@ func NewConfig() (self *Config) {
7274
ChunkerParams: storage.NewChunkerParams(),
7375
HiveParams: network.NewHiveParams(),
7476
//SyncParams: network.NewDefaultSyncParams(),
75-
Swap: swap.NewDefaultSwapParams(),
76-
ListenAddr: DefaultHTTPListenAddr,
77-
Port: DefaultHTTPPort,
78-
Path: node.DefaultDataDir(),
79-
EnsApi: node.DefaultIPCEndpoint("geth"),
80-
EnsRoot: ens.TestNetAddress,
81-
NetworkId: network.NetworkID,
82-
SwapEnabled: false,
83-
SyncEnabled: true,
84-
PssEnabled: true,
85-
SwapApi: "",
86-
BootNodes: "",
77+
Swap: swap.NewDefaultSwapParams(),
78+
ListenAddr: DefaultHTTPListenAddr,
79+
Port: DefaultHTTPPort,
80+
Path: node.DefaultDataDir(),
81+
EnsApi: node.DefaultIPCEndpoint("geth"),
82+
EnsRoot: ens.TestNetAddress,
83+
NetworkId: network.NetworkID,
84+
SwapEnabled: false,
85+
SyncEnabled: true,
86+
PssEnabled: true,
87+
ResourceEnabled: true,
88+
SwapApi: "",
89+
BootNodes: "",
8790
}
8891

8992
return
@@ -108,8 +111,17 @@ func (self *Config) Init(prvKey *ecdsa.PrivateKey) {
108111
self.PublicKey = pubkeyhex
109112
self.BzzKey = keyhex
110113

111-
self.Swap.Init(self.Contract, prvKey)
112-
//self.SyncParams.Init(self.Path)
113-
//self.HiveParams.Init(self.Path)
114+
if self.SwapEnabled {
115+
self.Swap.Init(self.Contract, prvKey)
116+
}
117+
self.privateKey = prvKey
114118
self.StoreParams.Init(self.Path)
115119
}
120+
121+
func (self *Config) ShiftPrivateKey() (privKey *ecdsa.PrivateKey) {
122+
if self.privateKey != nil {
123+
privKey = self.privateKey
124+
self.privateKey = nil
125+
}
126+
return privKey
127+
}

swarm/api/config_test.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,9 @@ func TestConfig(t *testing.T) {
4949
if one.PublicKey == "" {
5050
t.Fatal("Expected PublicKey to be set")
5151
}
52-
53-
//the Init function should append subdirs to the given path
54-
if one.Swap.PayProfile.Beneficiary == (common.Address{}) {
52+
if one.Swap.PayProfile.Beneficiary == (common.Address{}) && one.SwapEnabled {
5553
t.Fatal("Failed to correctly initialize SwapParams")
5654
}
57-
58-
if one.HiveParams.MaxPeersPerRequest != 5 {
59-
t.Fatal("Failed to correctly initialize HiveParams")
60-
}
61-
6255
if one.StoreParams.ChunkDbPath == one.Path {
6356
t.Fatal("Failed to correctly initialize StoreParams")
6457
}

swarm/api/http/server.go

Lines changed: 99 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ package http
2121

2222
import (
2323
"archive/tar"
24+
"bytes"
2425
"encoding/json"
2526
"errors"
2627
"fmt"
@@ -290,6 +291,95 @@ func (s *Server) HandleDelete(w http.ResponseWriter, r *Request) {
290291
fmt.Fprint(w, newKey)
291292
}
292293

294+
func (s *Server) HandlePostResource(w http.ResponseWriter, r *Request) {
295+
var outdata string
296+
if r.uri.Path != "" {
297+
frequency, err := strconv.ParseUint(r.uri.Path, 10, 64)
298+
if err != nil {
299+
s.BadRequest(w, r, fmt.Sprintf("Cannot parse frequency parameter: %v", err))
300+
return
301+
}
302+
key, err := s.api.ResourceCreate(r.Context(), r.uri.Addr, frequency)
303+
if err != nil {
304+
s.Error(w, r, fmt.Errorf("Resource creation failed: %v", err))
305+
return
306+
}
307+
outdata = key.Hex()
308+
}
309+
310+
data, err := ioutil.ReadAll(r.Body)
311+
if err != nil {
312+
s.Error(w, r, err)
313+
return
314+
}
315+
_, _, _, err = s.api.ResourceUpdate(r.Context(), r.uri.Addr, data)
316+
if err != nil {
317+
s.Error(w, r, fmt.Errorf("Update resource failed: %v", err))
318+
return
319+
}
320+
321+
if outdata != "" {
322+
w.Header().Add("Content-type", "text/plain")
323+
w.WriteHeader(http.StatusOK)
324+
fmt.Fprint(w, outdata)
325+
return
326+
}
327+
w.WriteHeader(http.StatusOK)
328+
}
329+
330+
// Retrieve mutable resource updates:
331+
// bzz-resource://<id> - get latest update
332+
// bzz-resource://<id>/<n> - get latest update on period n
333+
// bzz-resource://<id>/<n>/<m> - get update version m of period n
334+
// <id> = ens name or hash
335+
func (s *Server) HandleGetResource(w http.ResponseWriter, r *Request) {
336+
s.handleGetResource(w, r, r.uri.Addr)
337+
}
338+
339+
func (s *Server) handleGetResource(w http.ResponseWriter, r *Request, name string) {
340+
var params []string
341+
if len(r.uri.Path) > 0 {
342+
params = strings.Split(r.uri.Path, "/")
343+
}
344+
var updateKey storage.Key
345+
var period uint64
346+
var version uint64
347+
var data []byte
348+
var err error
349+
now := time.Now()
350+
log.Debug("handlegetdb", "name", name)
351+
switch len(params) {
352+
case 0:
353+
updateKey, data, err = s.api.ResourceLookup(r.Context(), name, 0, 0)
354+
case 2:
355+
version, err = strconv.ParseUint(params[1], 10, 32)
356+
if err != nil {
357+
break
358+
}
359+
period, err = strconv.ParseUint(params[0], 10, 32)
360+
if err != nil {
361+
break
362+
}
363+
updateKey, data, err = s.api.ResourceLookup(r.Context(), name, uint32(period), uint32(version))
364+
case 1:
365+
period, err = strconv.ParseUint(params[0], 10, 32)
366+
if err != nil {
367+
break
368+
}
369+
updateKey, data, err = s.api.ResourceLookup(r.Context(), name, uint32(period), uint32(version))
370+
default:
371+
s.BadRequest(w, r, "Invalid mutable resource request")
372+
return
373+
}
374+
if err != nil {
375+
s.Error(w, r, fmt.Errorf("Mutable resource lookup failed: %v", err))
376+
return
377+
}
378+
log.Debug("Found update", "key", updateKey)
379+
w.Header().Set("Content-Type", "application/octet-stream")
380+
http.ServeContent(w, &r.Request, "", now, bytes.NewReader(data))
381+
}
382+
293383
// HandleGet handles a GET request to
294384
// - bzz-raw://<key> and responds with the raw content stored at the
295385
// given storage key
@@ -335,7 +425,7 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *Request) {
335425
return api.SkipManifest
336426
})
337427
if entry == nil {
338-
s.NotFound(w, r, fmt.Errorf("Manifest entry could not be loaded"))
428+
s.NotFound(w, r, errors.New("Manifest entry could not be loaded"))
339429
return
340430
}
341431
key = storage.Key(common.Hex2Bytes(entry.Hash))
@@ -357,7 +447,6 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *Request) {
357447
contentType = typ
358448
}
359449
w.Header().Set("Content-Type", contentType)
360-
361450
http.ServeContent(w, &r.Request, "", time.Now(), reader)
362451
case r.uri.Hash():
363452
w.Header().Set("Content-Type", "text/plain")
@@ -604,6 +693,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
604693
case "POST":
605694
if uri.Raw() || uri.DeprecatedRaw() {
606695
s.HandlePostRaw(w, req)
696+
} else if uri.Resource() {
697+
s.HandlePostResource(w, req)
607698
} else {
608699
s.HandlePostFiles(w, req)
609700
}
@@ -629,6 +720,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
629720
s.HandleDelete(w, req)
630721

631722
case "GET":
723+
724+
if uri.Resource() {
725+
s.HandleGetResource(w, req)
726+
return
727+
}
728+
632729
if uri.Raw() || uri.Hash() || uri.DeprecatedRaw() {
633730
s.HandleGet(w, req)
634731
return

0 commit comments

Comments
 (0)