diff --git a/swarm/shed/db.go b/swarm/shed/db.go index 6f6b164501..8c11bf48b6 100644 --- a/swarm/shed/db.go +++ b/swarm/shed/db.go @@ -123,6 +123,17 @@ func (db *DB) Get(key []byte) (value []byte, err error) { return value, nil } +// Has wraps LevelDB Has method to increment metrics counter. +func (db *DB) Has(key []byte) (yes bool, err error) { + yes, err = db.ldb.Has(key, nil) + if err != nil { + metrics.GetOrRegisterCounter("DB.hasFail", nil).Inc(1) + return false, err + } + metrics.GetOrRegisterCounter("DB.has", nil).Inc(1) + return yes, nil +} + // Delete wraps LevelDB Delete method to increment metrics counter. func (db *DB) Delete(key []byte) (err error) { err = db.ldb.Delete(key, nil) diff --git a/swarm/shed/index.go b/swarm/shed/index.go index d02bf1a004..6be018d20a 100644 --- a/swarm/shed/index.go +++ b/swarm/shed/index.go @@ -145,6 +145,17 @@ func (f Index) Get(keyFields Item) (out Item, err error) { return out.Merge(keyFields), nil } +// Has accepts key fields represented as Item to check +// if there this Item's encoded key is stored in +// the index. +func (f Index) Has(keyFields Item) (bool, error) { + key, err := f.encodeKeyFunc(keyFields) + if err != nil { + return false, err + } + return f.db.Has(key) +} + // Put accepts Item to encode information from it // and save it to the database. func (f Index) Put(i Item) (err error) { diff --git a/swarm/shed/index_test.go b/swarm/shed/index_test.go index 489f001bd6..de181fa41a 100644 --- a/swarm/shed/index_test.go +++ b/swarm/shed/index_test.go @@ -49,7 +49,7 @@ var retrievalIndexFuncs = IndexFuncs{ }, } -// TestIndex validates put, get and delete functions of the Index implementation. +// TestIndex validates put, get, has and delete functions of the Index implementation. func TestIndex(t *testing.T) { db, cleanupFunc := newTestDB(t) defer cleanupFunc() @@ -177,6 +177,41 @@ func TestIndex(t *testing.T) { checkItem(t, got, want) }) + t.Run("has", func(t *testing.T) { + want := Item{ + Address: []byte("has-hash"), + Data: []byte("DATA"), + StoreTimestamp: time.Now().UTC().UnixNano(), + } + + dontWant := Item{ + Address: []byte("do-not-has-hash"), + Data: []byte("DATA"), + StoreTimestamp: time.Now().UTC().UnixNano(), + } + + err := index.Put(want) + if err != nil { + t.Fatal(err) + } + + has, err := index.Has(want) + if err != nil { + t.Fatal(err) + } + if !has { + t.Error("item is not found") + } + + has, err = index.Has(dontWant) + if err != nil { + t.Fatal(err) + } + if has { + t.Error("unwanted item is found") + } + }) + t.Run("delete", func(t *testing.T) { want := Item{ Address: []byte("delete-hash"), diff --git a/swarm/storage/localstore/mode_has.go b/swarm/storage/localstore/mode_has.go new file mode 100644 index 0000000000..90feaceef1 --- /dev/null +++ b/swarm/storage/localstore/mode_has.go @@ -0,0 +1,39 @@ +// 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 . + +package localstore + +import ( + "github.com/ethereum/go-ethereum/swarm/chunk" +) + +// Hasser provides Has method to retrieve Chunks +// from database. +type Hasser struct { + db *DB +} + +// NewHasser returns a new Hasser on database. +func (db *DB) NewHasser() *Hasser { + return &Hasser{ + db: db, + } +} + +// Has returns true if the chunk is stored in database. +func (h *Hasser) Has(addr chunk.Address) (bool, error) { + return h.db.retrievalDataIndex.Has(addressToItem(addr)) +} diff --git a/swarm/storage/localstore/mode_has_test.go b/swarm/storage/localstore/mode_has_test.go new file mode 100644 index 0000000000..332616ca21 --- /dev/null +++ b/swarm/storage/localstore/mode_has_test.go @@ -0,0 +1,55 @@ +// 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 . + +package localstore + +import ( + "testing" +) + +// TestHas validates that Hasser is returning true for +// the stored chunk and false for one that is not stored. +func TestHas(t *testing.T) { + db, cleanupFunc := newTestDB(t, nil) + defer cleanupFunc() + + chunk := generateTestRandomChunk() + + err := db.NewPutter(ModePutUpload).Put(chunk) + if err != nil { + t.Fatal(err) + } + + hasser := db.NewHasser() + + has, err := hasser.Has(chunk.Address()) + if err != nil { + t.Fatal(err) + } + if !has { + t.Error("chunk not found") + } + + missingChunk := generateTestRandomChunk() + + has, err = hasser.Has(missingChunk.Address()) + if err != nil { + t.Fatal(err) + } + if has { + t.Error("unexpected chunk is found") + } +}