Skip to content

Commit

Permalink
swarm: Debug API and HasChunks() API endpoint (ethereum#18980)
Browse files Browse the repository at this point in the history
  • Loading branch information
holisticode authored and nonsense committed Feb 7, 2019
1 parent 33d0a0e commit 41597c2
Show file tree
Hide file tree
Showing 12 changed files with 145 additions and 37 deletions.
2 changes: 1 addition & 1 deletion cmd/swarm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,5 +468,5 @@ func setSwarmBootstrapNodes(ctx *cli.Context, cfg *node.Config) {
}
cfg.P2P.BootstrapNodes = append(cfg.P2P.BootstrapNodes, node)
}
log.Debug("added default swarm bootnodes", "length", len(cfg.P2P.BootstrapNodes))

}
58 changes: 58 additions & 0 deletions swarm/api/inspector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package api

import (
"context"

"github.com/ethereum/go-ethereum/swarm/network"
"github.com/ethereum/go-ethereum/swarm/storage"
)

type Inspector struct {
api *API
hive *network.Hive
netStore *storage.NetStore
}

func NewInspector(api *API, hive *network.Hive, netStore *storage.NetStore) *Inspector {
return &Inspector{api, hive, netStore}
}

// Hive prints the kademlia table
func (inspector *Inspector) Hive() string {
return inspector.hive.String()
}

type HasInfo struct {
Addr string `json:"address"`
Has bool `json:"has"`
}

// Has checks whether each chunk address is present in the underlying datastore,
// the bool in the returned structs indicates if the underlying datastore has
// the chunk stored with the given address (true), or not (false)
func (inspector *Inspector) Has(chunkAddresses []storage.Address) []HasInfo {
results := make([]HasInfo, 0)
for _, addr := range chunkAddresses {
res := HasInfo{}
res.Addr = addr.String()
res.Has = inspector.netStore.Has(context.Background(), addr)
results = append(results, res)
}
return results
}
34 changes: 0 additions & 34 deletions swarm/api/testapi.go

This file was deleted.

5 changes: 5 additions & 0 deletions swarm/network/stream/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ func newRoundRobinStore(stores ...storage.ChunkStore) *roundRobinStore {
}
}

// not used in this context, only to fulfill ChunkStore interface
func (rrs *roundRobinStore) Has(ctx context.Context, addr storage.Address) bool {
panic("RoundRobinStor doesn't support HasChunk")
}

func (rrs *roundRobinStore) Get(ctx context.Context, addr storage.Address) (storage.Chunk, error) {
return nil, errors.New("get not well defined on round robin store")
}
Expand Down
9 changes: 9 additions & 0 deletions swarm/storage/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,15 @@ func (m *MapChunkStore) Get(_ context.Context, ref Address) (Chunk, error) {
return chunk, nil
}

// Need to implement Has from SyncChunkStore
func (m *MapChunkStore) Has(ctx context.Context, ref Address) bool {
m.mu.RLock()
defer m.mu.RUnlock()

_, has := m.chunks[ref.Hex()]
return has
}

func (m *MapChunkStore) Close() {
}

Expand Down
12 changes: 12 additions & 0 deletions swarm/storage/ldbstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -969,6 +969,18 @@ func (s *LDBStore) Get(_ context.Context, addr Address) (chunk Chunk, err error)
return s.get(addr)
}

// Has queries the underlying DB if a chunk with the given address is stored
// Returns true if the chunk is found, false if not
func (s *LDBStore) Has(_ context.Context, addr Address) bool {
s.lock.RLock()
defer s.lock.RUnlock()

ikey := getIndexKey(addr)
_, err := s.db.Get(ikey)

return err == nil
}

// TODO: To conform with other private methods of this object indices should not be updated
func (s *LDBStore) get(addr Address) (chunk *chunk, err error) {
if s.closed {
Expand Down
7 changes: 7 additions & 0 deletions swarm/storage/localstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,13 @@ func (ls *LocalStore) Put(ctx context.Context, chunk Chunk) error {
return err
}

// Has queries the underlying DbStore if a chunk with the given address
// is being stored there.
// Returns true if it is stored, false if not
func (ls *LocalStore) Has(ctx context.Context, addr Address) bool {
return ls.DbStore.Has(ctx, addr)
}

// Get(chunk *Chunk) looks up a chunk in the local stores
// This method is blocking until the chunk is retrieved
// so additional timeout may be needed to wrap this call if
Expand Down
33 changes: 33 additions & 0 deletions swarm/storage/localstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,36 @@ func setupLocalStore(t *testing.T, ldbCap int) (ls *LocalStore, cleanup func())

return store, cleanup
}

func TestHas(t *testing.T) {
ldbCap := defaultGCRatio
store, cleanup := setupLocalStore(t, ldbCap)
defer cleanup()

nonStoredAddr := GenerateRandomChunk(128).Address()

has := store.Has(context.Background(), nonStoredAddr)
if has {
t.Fatal("Expected Has() to return false, but returned true!")
}

storeChunks := GenerateRandomChunks(128, 3)
for _, ch := range storeChunks {
err := store.Put(context.Background(), ch)
if err != nil {
t.Fatalf("Expected store to store chunk, but it failed: %v", err)
}

has := store.Has(context.Background(), ch.Address())
if !has {
t.Fatal("Expected Has() to return true, but returned false!")
}
}

//let's be paranoic and test again that the non-existent chunk returns false
has = store.Has(context.Background(), nonStoredAddr)
if has {
t.Fatal("Expected Has() to return false, but returned true!")
}

}
5 changes: 5 additions & 0 deletions swarm/storage/memstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ func NewMemStore(params *StoreParams, _ *LDBStore) (m *MemStore) {
}
}

// Has needed to implement SyncChunkStore
func (m *MemStore) Has(_ context.Context, addr Address) bool {
return m.cache.Contains(addr)
}

func (m *MemStore) Get(_ context.Context, addr Address) (Chunk, error) {
if m.disabled {
return nil, ErrChunkNotFound
Expand Down
7 changes: 7 additions & 0 deletions swarm/storage/netstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ func (n *NetStore) get(ctx context.Context, ref Address) (Chunk, func(context.Co
return chunk, nil, nil
}

// Has is the storage layer entry point to query the underlying
// database to return if it has a chunk or not.
// Called from the DebugAPI
func (n *NetStore) Has(ctx context.Context, ref Address) bool {
return n.store.Has(ctx, ref)
}

// getOrCreateFetcher attempts at retrieving an existing fetchers
// if none exists, creates one and saves it in the fetchers cache
// caller must hold the lock
Expand Down
8 changes: 7 additions & 1 deletion swarm/storage/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ func (v *ContentAddressValidator) Validate(chunk Chunk) bool {
type ChunkStore interface {
Put(ctx context.Context, ch Chunk) (err error)
Get(rctx context.Context, ref Address) (ch Chunk, err error)
Has(rctx context.Context, ref Address) bool
Close()
}

Expand All @@ -314,7 +315,12 @@ func (f *FakeChunkStore) Put(_ context.Context, ch Chunk) error {
return nil
}

// Gut doesn't store anything it is just here to implement ChunkStore
// Has doesn't do anything it is just here to implement ChunkStore
func (f *FakeChunkStore) Has(_ context.Context, ref Address) bool {
panic("FakeChunkStore doesn't support HasChunk")
}

// Get doesn't store anything it is just here to implement ChunkStore
func (f *FakeChunkStore) Get(_ context.Context, ref Address) (Chunk, error) {
panic("FakeChunkStore doesn't support Get")
}
Expand Down
2 changes: 1 addition & 1 deletion swarm/swarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,7 @@ func (self *Swarm) APIs() []rpc.API {
{
Namespace: "bzz",
Version: "3.0",
Service: api.NewControl(self.api, self.bzz.Hive),
Service: api.NewInspector(self.api, self.bzz.Hive, self.netStore),
Public: false,
},
{
Expand Down

0 comments on commit 41597c2

Please sign in to comment.