Skip to content

Commit

Permalink
Merge pull request ethereum#318 from ethersphere/mutableresources-max…
Browse files Browse the repository at this point in the history
…lookup

swarm/storage: Add max period traversal for lookup query
  • Loading branch information
nolash authored Mar 29, 2018
2 parents 4fb66e7 + 0d5b951 commit 539806d
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 48 deletions.
8 changes: 4 additions & 4 deletions swarm/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -581,17 +581,17 @@ func (self *Api) BuildDirectoryTree(mhash string, nameresolver bool) (key storag
}

// 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) {
func (self *Api) ResourceLookup(ctx context.Context, name string, period uint32, version uint32, maxLookup *storage.ResourceLookupParams) (storage.Key, []byte, error) {
var err error
if version != 0 {
if period == 0 {
return nil, nil, storage.NewResourceError(storage.ErrInvalidValue, "Period can't be 0")
}
_, err = self.resource.LookupVersionByName(ctx, name, period, version, true)
_, err = self.resource.LookupVersionByName(ctx, name, period, version, true, maxLookup)
} else if period != 0 {
_, err = self.resource.LookupHistoricalByName(ctx, name, period, true)
_, err = self.resource.LookupHistoricalByName(ctx, name, period, true, maxLookup)
} else {
_, err = self.resource.LookupLatestByName(ctx, name, true)
_, err = self.resource.LookupLatestByName(ctx, name, true, maxLookup)
}
if err != nil {
return nil, nil, err
Expand Down
7 changes: 4 additions & 3 deletions swarm/api/http/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,7 @@ func (s *Server) HandleGetResource(w http.ResponseWriter, r *Request) {
s.handleGetResource(w, r, r.uri.Addr)
}

// TODO: Enable pass maxPeriod parameter
func (s *Server) handleGetResource(w http.ResponseWriter, r *Request, name string) {
log.Debug("handle.get.resource", "ruid", r.ruid)
var params []string
Expand All @@ -428,7 +429,7 @@ func (s *Server) handleGetResource(w http.ResponseWriter, r *Request, name strin
log.Debug("handlegetdb", "name", name, "ruid", r.ruid)
switch len(params) {
case 0:
updateKey, data, err = s.api.ResourceLookup(r.Context(), name, 0, 0)
updateKey, data, err = s.api.ResourceLookup(r.Context(), name, 0, 0, nil)
case 2:
version, err = strconv.ParseUint(params[1], 10, 32)
if err != nil {
Expand All @@ -438,13 +439,13 @@ func (s *Server) handleGetResource(w http.ResponseWriter, r *Request, name strin
if err != nil {
break
}
updateKey, data, err = s.api.ResourceLookup(r.Context(), name, uint32(period), uint32(version))
updateKey, data, err = s.api.ResourceLookup(r.Context(), name, uint32(period), uint32(version), nil)
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))
updateKey, data, err = s.api.ResourceLookup(r.Context(), name, uint32(period), uint32(version), nil)
default:
Respond(w, r, "invalid mutable resource request", http.StatusBadRequest)
return
Expand Down
1 change: 1 addition & 0 deletions swarm/storage/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ const (
ErrNothingToReturn
ErrInvalidSignature
ErrNotSynced
ErrPeriodDepth
ErrCnt
)
4 changes: 4 additions & 0 deletions swarm/storage/ldbstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ func NewLDBStore(path string, hash SwarmHasher, capacity uint64, po func(Key) ui
return s, nil
}

func (self *LDBStore) SetTrusted() {
self.trusted = true
}

// NewMockDbStore creates a new instance of DbStore with
// mockStore set to a provided value. If mockStore argument is nil,
// this function behaves exactly as NewDbStore.
Expand Down
91 changes: 64 additions & 27 deletions swarm/storage/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,19 @@ func NewResourceError(code int, s string) error {
err: s,
}
switch code {
case ErrNotFound, ErrIO, ErrUnauthorized, ErrInvalidValue, ErrDataOverflow, ErrNothingToReturn, ErrInvalidSignature, ErrNotSynced:
case ErrNotFound, ErrIO, ErrUnauthorized, ErrInvalidValue, ErrDataOverflow, ErrNothingToReturn, ErrInvalidSignature, ErrNotSynced, ErrPeriodDepth:
r.code = code
}
return r
}

type Signature [signatureLength]byte

type ResourceLookupParams struct {
Limit bool
Max uint32
}

type SignFunc func(common.Hash) (Signature, error)

type nameHashFunc func(string) common.Hash
Expand Down Expand Up @@ -157,28 +162,40 @@ type headerGetter interface {
// TODO: Include modtime in chunk data + signature
type ResourceHandler struct {
ChunkStore
validator ResourceValidator
ethClient headerGetter
resources map[string]*resource
hashPool sync.Pool
resourceLock sync.RWMutex
nameHash nameHashFunc
storeTimeout time.Duration
validator ResourceValidator
ethClient headerGetter
resources map[string]*resource
hashPool sync.Pool
resourceLock sync.RWMutex
nameHash nameHashFunc
storeTimeout time.Duration
queryMaxPeriods *ResourceLookupParams
}

type ResourceHandlerParams struct {
Validator ResourceValidator
QueryMaxPeriods *ResourceLookupParams
}

// Create or open resource update chunk store
func NewResourceHandler(hasher SwarmHasher, chunkStore ChunkStore, ethClient headerGetter, validator ResourceValidator) (*ResourceHandler, error) {
func NewResourceHandler(hasher SwarmHasher, chunkStore ChunkStore, ethClient headerGetter, params *ResourceHandlerParams) (*ResourceHandler, error) {
if params.QueryMaxPeriods == nil {
params.QueryMaxPeriods = &ResourceLookupParams{
Limit: false,
}
}
rh := &ResourceHandler{
ChunkStore: chunkStore,
ethClient: ethClient,
resources: make(map[string]*resource),
validator: validator,
validator: params.Validator,
storeTimeout: defaultStoreTimeout,
hashPool: sync.Pool{
New: func() interface{} {
return MakeHashFunc(SHA3Hash)()
},
},
queryMaxPeriods: params.QueryMaxPeriods,
}

if rh.validator != nil {
Expand Down Expand Up @@ -320,17 +337,19 @@ func (self *ResourceHandler) NewResource(ctx context.Context, name string, frequ
// It is the callers responsibility to make sure that this chunk exists (if the resource
// update root data was retrieved externally, it typically doesn't)
//
//
func (self *ResourceHandler) LookupVersionByName(ctx context.Context, name string, period uint32, version uint32, refresh bool) (*resource, error) {
return self.LookupVersion(ctx, self.nameHash(name), name, period, version, refresh)
// If maxPeriod is -1, the default QueryMaxPeriod from ResourceHandlerParams will be used
// if maxPeriod is 0, there will be no limit on period hops
// if maxPeriod > 0, the given value will be the limit of period hops
func (self *ResourceHandler) LookupVersionByName(ctx context.Context, name string, period uint32, version uint32, refresh bool, maxLookup *ResourceLookupParams) (*resource, error) {
return self.LookupVersion(ctx, self.nameHash(name), name, period, version, refresh, maxLookup)
}

func (self *ResourceHandler) LookupVersion(ctx context.Context, nameHash common.Hash, name string, period uint32, version uint32, refresh bool) (*resource, error) {
func (self *ResourceHandler) LookupVersion(ctx context.Context, nameHash common.Hash, name string, period uint32, version uint32, refresh bool, maxLookup *ResourceLookupParams) (*resource, error) {
rsrc, err := self.loadResource(nameHash, name, refresh)
if err != nil {
return nil, err
}
return self.lookup(rsrc, period, version, refresh)
return self.lookup(rsrc, period, version, refresh, maxLookup)
}

// Retrieves the latest version of the resource update identified by `name`
Expand All @@ -341,16 +360,16 @@ func (self *ResourceHandler) LookupVersion(ctx context.Context, nameHash common.
// and returned.
//
// See also (*ResourceHandler).LookupVersion
func (self *ResourceHandler) LookupHistoricalByName(ctx context.Context, name string, period uint32, refresh bool) (*resource, error) {
return self.LookupHistorical(ctx, self.nameHash(name), name, period, refresh)
func (self *ResourceHandler) LookupHistoricalByName(ctx context.Context, name string, period uint32, refresh bool, maxLookup *ResourceLookupParams) (*resource, error) {
return self.LookupHistorical(ctx, self.nameHash(name), name, period, refresh, maxLookup)
}

func (self *ResourceHandler) LookupHistorical(ctx context.Context, nameHash common.Hash, name string, period uint32, refresh bool) (*resource, error) {
func (self *ResourceHandler) LookupHistorical(ctx context.Context, nameHash common.Hash, name string, period uint32, refresh bool, maxLookup *ResourceLookupParams) (*resource, error) {
rsrc, err := self.loadResource(nameHash, name, refresh)
if err != nil {
return nil, err
}
return self.lookup(rsrc, period, 0, refresh)
return self.lookup(rsrc, period, 0, refresh, maxLookup)
}

// Retrieves the latest version of the resource update identified by `name`
Expand All @@ -363,11 +382,11 @@ func (self *ResourceHandler) LookupHistorical(ctx context.Context, nameHash comm
// Version iteration is done as in (*ResourceHandler).LookupHistorical
//
// See also (*ResourceHandler).LookupHistorical
func (self *ResourceHandler) LookupLatestByName(ctx context.Context, name string, refresh bool) (*resource, error) {
return self.LookupLatest(ctx, self.nameHash(name), name, refresh)
func (self *ResourceHandler) LookupLatestByName(ctx context.Context, name string, refresh bool, maxLookup *ResourceLookupParams) (*resource, error) {
return self.LookupLatest(ctx, self.nameHash(name), name, refresh, maxLookup)
}

func (self *ResourceHandler) LookupLatest(ctx context.Context, nameHash common.Hash, name string, refresh bool) (*resource, error) {
func (self *ResourceHandler) LookupLatest(ctx context.Context, nameHash common.Hash, name string, refresh bool, maxLookup *ResourceLookupParams) (*resource, error) {

// get our blockheight at this time and the next block of the update period
rsrc, err := self.loadResource(nameHash, name, refresh)
Expand All @@ -379,11 +398,11 @@ func (self *ResourceHandler) LookupLatest(ctx context.Context, nameHash common.H
return nil, err
}
nextperiod := getNextPeriod(rsrc.startBlock, currentblock, rsrc.frequency)
return self.lookup(rsrc, nextperiod, 0, refresh)
return self.lookup(rsrc, nextperiod, 0, refresh, maxLookup)
}

// base code for public lookup methods
func (self *ResourceHandler) lookup(rsrc *resource, period uint32, version uint32, refresh bool) (*resource, error) {
func (self *ResourceHandler) lookup(rsrc *resource, period uint32, version uint32, refresh bool, maxLookup *ResourceLookupParams) (*resource, error) {

if period == 0 {
return nil, NewResourceError(ErrInvalidValue, "period must be >0")
Expand All @@ -398,7 +417,14 @@ func (self *ResourceHandler) lookup(rsrc *resource, period uint32, version uint3
version = 1
}

var hops uint32
if maxLookup == nil {
maxLookup = self.queryMaxPeriods
}
for period > 0 {
if maxLookup.Limit && hops > maxLookup.Max {
return nil, NewResourceError(ErrPeriodDepth, fmt.Sprintf("Lookup exceeded max period hops (%d)", maxLookup.Max))
}
key := self.resourceHash(period, version, rsrc.nameHash)
chunk, err := self.Get(key)
if err == nil {
Expand All @@ -421,6 +447,7 @@ func (self *ResourceHandler) lookup(rsrc *resource, period uint32, version uint3
}
log.Trace("rsrc update not found, checking previous period", "period", period, "key", key)
period--
hops++
}
return nil, NewResourceError(ErrNotFound, "no updates found")
}
Expand Down Expand Up @@ -865,12 +892,13 @@ func isMultihash(data []byte) int {
return cursor + inthashlength
}

// TODO: this should not be exposed, but swarm/testutil/http.go needs it
func NewTestResourceHandler(datadir string, ethClient headerGetter, validator ResourceValidator) (*ResourceHandler, error) {
// TODO: this should not be part of production code, but currently swarm/testutil/http.go needs it
func NewTestResourceHandler(datadir string, ethClient headerGetter, validator ResourceValidator, maxLimit *ResourceLookupParams) (*ResourceHandler, error) {
path := filepath.Join(datadir, DbDirName)
basekey := make([]byte, 32)
hasher := MakeHashFunc(SHA3Hash)
dbStore, err := NewLDBStore(path, hasher, singletonSwarmDbCapacity, func(k Key) (ret uint8) { return uint8(Proximity(basekey[:], k[:])) })
dbStore.SetTrusted()
if err != nil {
return nil, err
}
Expand All @@ -879,5 +907,14 @@ func NewTestResourceHandler(datadir string, ethClient headerGetter, validator Re
DbStore: dbStore,
}
resourceChunkStore := NewResourceChunkStore(localStore, nil)
return NewResourceHandler(hasher, resourceChunkStore, ethClient, validator)
if maxLimit == nil {
maxLimit = &ResourceLookupParams{
Limit: false,
}
}
params := &ResourceHandlerParams{
Validator: validator,
QueryMaxPeriods: maxLimit,
}
return NewResourceHandler(hasher, resourceChunkStore, ethClient, params)
}
23 changes: 13 additions & 10 deletions swarm/storage/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,8 @@ var (

func init() {
var err error
verbose := flag.Bool("v", false, "verbose")
flag.Parse()
if *verbose {
log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true)))))
}
log.Root().SetHandler(log.CallerFileHandler(log.LvlFilterHandler(log.Lvl(*loglevel), log.StreamHandler(os.Stderr, log.TerminalFormat(true)))))
safeName, err = ToSafeName(domainName)
if err != nil {
panic(err)
Expand Down Expand Up @@ -222,8 +219,14 @@ func TestResourceHandler(t *testing.T) {
// it will match on second iteration startblocknumber + (resourceFrequency * 3)
fwdBlocks(int(resourceFrequency*2)-1, backend)

rh2, err := NewTestResourceHandler(datadir, rh.ethClient, nil)
_, err = rh2.LookupLatestByName(ctx, safeName, true)
lookupParams := &ResourceLookupParams{
Limit: false,
}
rh2, err := NewTestResourceHandler(datadir, rh.ethClient, nil, lookupParams)
if err != nil {
t.Fatal(err)
}
_, err = rh2.LookupLatestByName(ctx, safeName, true, nil)
if err != nil {
t.Fatal(err)
}
Expand All @@ -241,7 +244,7 @@ func TestResourceHandler(t *testing.T) {
log.Debug("Latest lookup", "period", rh2.resources[safeName].lastPeriod, "version", rh2.resources[safeName].version, "data", rh2.resources[safeName].data)

// specific block, latest version
rsrc, err := rh2.LookupHistoricalByName(ctx, safeName, 3, true)
rsrc, err := rh2.LookupHistoricalByName(ctx, safeName, 3, true, lookupParams)
if err != nil {
t.Fatal(err)
}
Expand All @@ -252,7 +255,7 @@ func TestResourceHandler(t *testing.T) {
log.Debug("Historical lookup", "period", rh2.resources[safeName].lastPeriod, "version", rh2.resources[safeName].version, "data", rh2.resources[safeName].data)

// specific block, specific version
rsrc, err = rh2.LookupVersionByName(ctx, safeName, 3, 1, true)
rsrc, err = rh2.LookupVersionByName(ctx, safeName, 3, 1, true, lookupParams)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -350,7 +353,7 @@ func TestResourceMultihash(t *testing.T) {
rh.Close()

// test with signed data
rh2, err := NewTestResourceHandler(datadir, rh.ethClient, validator)
rh2, err := NewTestResourceHandler(datadir, rh.ethClient, validator, nil)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -481,7 +484,7 @@ func setupTest(backend headerGetter, validator ResourceValidator) (rh *ResourceH
os.RemoveAll(datadir)
}

rh, err = NewTestResourceHandler(datadir, backend, validator)
rh, err = NewTestResourceHandler(datadir, backend, validator, &ResourceLookupParams{Limit: false})
return rh, datadir, signer, cleanF, nil
}

Expand Down
8 changes: 5 additions & 3 deletions swarm/swarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,11 +198,13 @@ func NewSwarm(ctx *node.ServiceContext, backend chequebook.Backend, config *api.
var resourceHandler *storage.ResourceHandler
// if use resource updates
if self.config.ResourceEnabled && resolver != nil {
resourceValidator := storage.NewENSValidator(config.EnsRoot, resolver, storage.NewGenericResourceSigner(self.privateKey))
resolver.SetNameHash(resourceValidator.NameHash)
resourceparams := &storage.ResourceHandlerParams{
Validator: storage.NewENSValidator(config.EnsRoot, resolver, storage.NewGenericResourceSigner(self.privateKey)),
}
resolver.SetNameHash(resourceparams.Validator.NameHash)
hashfunc := storage.MakeHashFunc(storage.SHA3Hash)
chunkStore := storage.NewResourceChunkStore(self.lstore, func(*storage.Chunk) error { return nil })
resourceHandler, err = storage.NewResourceHandler(hashfunc, chunkStore, resolver, resourceValidator)
resourceHandler, err = storage.NewResourceHandler(hashfunc, chunkStore, resolver, resourceparams)
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion swarm/testutil/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ func NewTestSwarmServer(t *testing.T) *TestSwarmServer {
t.Fatal(err)
}

rh, err := storage.NewTestResourceHandler(resourceDir, &fakeBackend{}, nil)
rh, err := storage.NewTestResourceHandler(resourceDir, &fakeBackend{}, nil, &storage.ResourceLookupParams{Limit: false})
if err != nil {
t.Fatal(err)
}
Expand Down

0 comments on commit 539806d

Please sign in to comment.