Skip to content

Commit

Permalink
Merge pull request ethereum#204 from ethersphere/swarm-mutableresourc…
Browse files Browse the repository at this point in the history
…es-apinew

HTTP API for mutable resources
  • Loading branch information
nolash authored Jan 25, 2018
2 parents f68e836 + ef5eeb6 commit 85e2318
Show file tree
Hide file tree
Showing 16 changed files with 712 additions and 301 deletions.
2 changes: 1 addition & 1 deletion cmd/swarm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ func registerBzzService(bzzconfig *bzzapi.Config, ctx *cli.Context, stack *node.
}

// In production, mockStore must be always nil.
return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, bzzconfig.SwapEnabled, bzzconfig.SyncEnabled, bzzconfig.Cors, bzzconfig.PssEnabled, nil)
return swarm.NewSwarm(ctx, swapClient, ensClient, bzzconfig, nil)
}
//register within the ethereum node
if err := stack.Register(boot); err != nil {
Expand Down
60 changes: 55 additions & 5 deletions swarm/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package api

import (
"context"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -46,15 +47,17 @@ on top of the dpa
it is the public interface of the dpa which is included in the ethereum stack
*/
type Api struct {
dpa *storage.DPA
dns Resolver
resource *storage.ResourceHandler
dpa *storage.DPA
dns Resolver
}

//the api constructor initialises
func NewApi(dpa *storage.DPA, dns Resolver) (self *Api) {
func NewApi(dpa *storage.DPA, dns Resolver, resourceHandler *storage.ResourceHandler) (self *Api) {
self = &Api{
dpa: dpa,
dns: dns,
dpa: dpa,
dns: dns,
resource: resourceHandler,
}
return
}
Expand Down Expand Up @@ -361,3 +364,50 @@ func (self *Api) BuildDirectoryTree(mhash string, nameresolver bool) (key storag
}
return key, manifestEntryMap, nil
}

// Look up mutable resource updates at specific periods and versions
func (self *Api) ResourceLookup(ctx context.Context, name string, period uint32, version uint32) (storage.Key, []byte, error) {
var err error
if version != 0 {
if period == 0 {
currentblocknumber, err := self.resource.GetBlock(ctx)
if err != nil {
return nil, nil, fmt.Errorf("Could not determine latest block: %v", err)
}
period = self.resource.BlockToPeriod(name, currentblocknumber)
}
_, err = self.resource.LookupVersionByName(ctx, name, period, version, true)
} else if period != 0 {
_, err = self.resource.LookupHistoricalByName(ctx, name, period, true)
} else {
_, err = self.resource.LookupLatestByName(ctx, name, true)
}
if err != nil {
return nil, nil, err
}
return self.resource.GetContent(name)
}

func (self *Api) ResourceCreate(ctx context.Context, name string, frequency uint64) (storage.Key, error) {
rsrc, err := self.resource.NewResource(ctx, name, frequency)
if err != nil {
return nil, err
}
h := rsrc.NameHash()
return storage.Key(h[:]), nil
}

func (self *Api) ResourceUpdate(ctx context.Context, name string, data []byte) (storage.Key, uint32, uint32, error) {
key, err := self.resource.Update(ctx, name, data)
period, _ := self.resource.GetLastPeriod(name)
version, _ := self.resource.GetVersion(name)
return key, period, version, err
}

func (self *Api) ResourceHashSize() int {
return self.resource.HashSize()
}

func (self *Api) ResourceIsValidated() bool {
return self.resource.IsValidated()
}
2 changes: 1 addition & 1 deletion swarm/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ func testApi(t *testing.T, f func(*Api)) {
if err != nil {
return
}
api := NewApi(dpa, nil)
api := NewApi(dpa, nil, nil)
dpa.Start()
f(api)
dpa.Stop()
Expand Down
74 changes: 43 additions & 31 deletions swarm/api/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,22 +46,24 @@ type Config struct {
*network.HiveParams
Swap *swap.SwapParams
//*network.SyncParams
Contract common.Address
EnsRoot common.Address
EnsApi string
Path string
ListenAddr string
Port string
PublicKey string
BzzKey string
NetworkId uint64
SwapEnabled bool
SyncEnabled bool
PssEnabled bool
SwapApi string
Cors string
BzzAccount string
BootNodes string
Contract common.Address
EnsRoot common.Address
EnsApi string
Path string
ListenAddr string
Port string
PublicKey string
BzzKey string
NetworkId uint64
SwapEnabled bool
SyncEnabled bool
PssEnabled bool
ResourceEnabled bool
SwapApi string
Cors string
BzzAccount string
BootNodes string
privateKey *ecdsa.PrivateKey
}

//create a default config with all parameters to set to defaults
Expand All @@ -72,18 +74,19 @@ func NewConfig() (self *Config) {
ChunkerParams: storage.NewChunkerParams(),
HiveParams: network.NewHiveParams(),
//SyncParams: network.NewDefaultSyncParams(),
Swap: swap.NewDefaultSwapParams(),
ListenAddr: DefaultHTTPListenAddr,
Port: DefaultHTTPPort,
Path: node.DefaultDataDir(),
EnsApi: node.DefaultIPCEndpoint("geth"),
EnsRoot: ens.TestNetAddress,
NetworkId: network.NetworkID,
SwapEnabled: false,
SyncEnabled: true,
PssEnabled: true,
SwapApi: "",
BootNodes: "",
Swap: swap.NewDefaultSwapParams(),
ListenAddr: DefaultHTTPListenAddr,
Port: DefaultHTTPPort,
Path: node.DefaultDataDir(),
EnsApi: node.DefaultIPCEndpoint("geth"),
EnsRoot: ens.TestNetAddress,
NetworkId: network.NetworkID,
SwapEnabled: false,
SyncEnabled: true,
PssEnabled: true,
ResourceEnabled: true,
SwapApi: "",
BootNodes: "",
}

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

self.Swap.Init(self.Contract, prvKey)
//self.SyncParams.Init(self.Path)
//self.HiveParams.Init(self.Path)
if self.SwapEnabled {
self.Swap.Init(self.Contract, prvKey)
}
self.privateKey = prvKey
self.StoreParams.Init(self.Path)
}

func (self *Config) ShiftPrivateKey() (privKey *ecdsa.PrivateKey) {
if self.privateKey != nil {
privKey = self.privateKey
self.privateKey = nil
}
return privKey
}
9 changes: 1 addition & 8 deletions swarm/api/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,9 @@ func TestConfig(t *testing.T) {
if one.PublicKey == "" {
t.Fatal("Expected PublicKey to be set")
}

//the Init function should append subdirs to the given path
if one.Swap.PayProfile.Beneficiary == (common.Address{}) {
if one.Swap.PayProfile.Beneficiary == (common.Address{}) && one.SwapEnabled {
t.Fatal("Failed to correctly initialize SwapParams")
}

if one.HiveParams.MaxPeersPerRequest != 5 {
t.Fatal("Failed to correctly initialize HiveParams")
}

if one.StoreParams.ChunkDbPath == one.Path {
t.Fatal("Failed to correctly initialize StoreParams")
}
Expand Down
101 changes: 99 additions & 2 deletions swarm/api/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package http

import (
"archive/tar"
"bytes"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -290,6 +291,95 @@ func (s *Server) HandleDelete(w http.ResponseWriter, r *Request) {
fmt.Fprint(w, newKey)
}

func (s *Server) HandlePostResource(w http.ResponseWriter, r *Request) {
var outdata string
if r.uri.Path != "" {
frequency, err := strconv.ParseUint(r.uri.Path, 10, 64)
if err != nil {
s.BadRequest(w, r, fmt.Sprintf("Cannot parse frequency parameter: %v", err))
return
}
key, err := s.api.ResourceCreate(r.Context(), r.uri.Addr, frequency)
if err != nil {
s.Error(w, r, fmt.Errorf("Resource creation failed: %v", err))
return
}
outdata = key.Hex()
}

data, err := ioutil.ReadAll(r.Body)
if err != nil {
s.Error(w, r, err)
return
}
_, _, _, err = s.api.ResourceUpdate(r.Context(), r.uri.Addr, data)
if err != nil {
s.Error(w, r, fmt.Errorf("Update resource failed: %v", err))
return
}

if outdata != "" {
w.Header().Add("Content-type", "text/plain")
w.WriteHeader(http.StatusOK)
fmt.Fprint(w, outdata)
return
}
w.WriteHeader(http.StatusOK)
}

// Retrieve mutable resource updates:
// bzz-resource://<id> - get latest update
// bzz-resource://<id>/<n> - get latest update on period n
// bzz-resource://<id>/<n>/<m> - get update version m of period n
// <id> = ens name or hash
func (s *Server) HandleGetResource(w http.ResponseWriter, r *Request) {
s.handleGetResource(w, r, r.uri.Addr)
}

func (s *Server) handleGetResource(w http.ResponseWriter, r *Request, name string) {
var params []string
if len(r.uri.Path) > 0 {
params = strings.Split(r.uri.Path, "/")
}
var updateKey storage.Key
var period uint64
var version uint64
var data []byte
var err error
now := time.Now()
log.Debug("handlegetdb", "name", name)
switch len(params) {
case 0:
updateKey, data, err = s.api.ResourceLookup(r.Context(), name, 0, 0)
case 2:
version, err = strconv.ParseUint(params[1], 10, 32)
if err != nil {
break
}
period, err = strconv.ParseUint(params[0], 10, 32)
if err != nil {
break
}
updateKey, data, err = s.api.ResourceLookup(r.Context(), name, uint32(period), uint32(version))
case 1:
period, err = strconv.ParseUint(params[0], 10, 32)
if err != nil {
break
}
updateKey, data, err = s.api.ResourceLookup(r.Context(), name, uint32(period), uint32(version))
default:
s.BadRequest(w, r, "Invalid mutable resource request")
return
}
if err != nil {
s.Error(w, r, fmt.Errorf("Mutable resource lookup failed: %v", err))
return
}
log.Debug("Found update", "key", updateKey)
w.Header().Set("Content-Type", "application/octet-stream")
http.ServeContent(w, &r.Request, "", now, bytes.NewReader(data))
}

// HandleGet handles a GET request to
// - bzz-raw://<key> and responds with the raw content stored at the
// given storage key
Expand Down Expand Up @@ -335,7 +425,7 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *Request) {
return api.SkipManifest
})
if entry == nil {
s.NotFound(w, r, fmt.Errorf("Manifest entry could not be loaded"))
s.NotFound(w, r, errors.New("Manifest entry could not be loaded"))
return
}
key = storage.Key(common.Hex2Bytes(entry.Hash))
Expand All @@ -357,7 +447,6 @@ func (s *Server) HandleGet(w http.ResponseWriter, r *Request) {
contentType = typ
}
w.Header().Set("Content-Type", contentType)

http.ServeContent(w, &r.Request, "", time.Now(), reader)
case r.uri.Hash():
w.Header().Set("Content-Type", "text/plain")
Expand Down Expand Up @@ -604,6 +693,8 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case "POST":
if uri.Raw() || uri.DeprecatedRaw() {
s.HandlePostRaw(w, req)
} else if uri.Resource() {
s.HandlePostResource(w, req)
} else {
s.HandlePostFiles(w, req)
}
Expand All @@ -629,6 +720,12 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
s.HandleDelete(w, req)

case "GET":

if uri.Resource() {
s.HandleGetResource(w, req)
return
}

if uri.Raw() || uri.Hash() || uri.DeprecatedRaw() {
s.HandleGet(w, req)
return
Expand Down
Loading

0 comments on commit 85e2318

Please sign in to comment.