Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option for datastore read rehashing #2904

Merged
merged 5 commits into from
Jun 27, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions blocks/blockstore/blockstore.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ var log = logging.Logger("blockstore")
// BlockPrefix namespaces blockstore datastores
var BlockPrefix = ds.NewKey("blocks")

var ValueTypeMismatch = errors.New("The retrieved value is not a Block")
var ValueTypeMismatch = errors.New("the retrieved value is not a Block")
var ErrHashMismatch = errors.New("block in storage has different hash than requested")

var ErrNotFound = errors.New("blockstore: block not found")

Expand Down Expand Up @@ -71,6 +72,12 @@ type blockstore struct {
lk sync.RWMutex
gcreq int32
gcreqlk sync.Mutex

rehash bool
}

func (bs *blockstore) RuntimeHashing(enabled bool) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • this is not a clear name, because there is runtime hashing regardless
  • use: HashOnRead

bs.rehash = enabled
}

func (bs *blockstore) Get(k key.Key) (blocks.Block, error) {
Expand All @@ -90,7 +97,16 @@ func (bs *blockstore) Get(k key.Key) (blocks.Block, error) {
return nil, ValueTypeMismatch
}

return blocks.NewBlockWithHash(bdata, mh.Multihash(k))
if bs.rehash {
rb := blocks.NewBlock(bdata)
if rb.Key() != k {
return nil, ErrHashMismatch
} else {
return rb, nil
}
} else {
return blocks.NewBlockWithHash(bdata, mh.Multihash(k))
}
}

func (bs *blockstore) Put(block blocks.Block) error {
Expand Down
16 changes: 16 additions & 0 deletions blocks/blockstore/blockstore_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,22 @@ func TestPutThenGetBlock(t *testing.T) {
}
}

func TestRuntimeHashing(t *testing.T) {
bs := NewBlockstore(ds_sync.MutexWrap(ds.NewMapDatastore()))
bl := blocks.NewBlock([]byte("some data"))
blBad, err := blocks.NewBlockWithHash([]byte("some other data"), bl.Key().ToMultihash())
if err != nil {
t.Fatal("Debug is enabled")
}

bs.Put(blBad)
bs.RuntimeHashing(true)

if _, err := bs.Get(bl.Key()); err != ErrHashMismatch {
t.Fatalf("Expected '%v' got '%v'\n", ErrHashMismatch, err)
}
}

func newBlockStoreWithKeys(t *testing.T, d ds.Datastore, N int) (Blockstore, []key.Key) {
if d == nil {
d = ds.NewMapDatastore()
Expand Down
16 changes: 11 additions & 5 deletions core/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -130,16 +130,22 @@ func setupNode(ctx context.Context, n *IpfsNode, cfg *BuildCfg) error {
}

var err error
n.Blockstore, err = bstore.WriteCached(bstore.NewBlockstore(n.Repo.Datastore()), kSizeBlockstoreWriteCache)
bs := bstore.NewBlockstore(n.Repo.Datastore())
n.Blockstore, err = bstore.WriteCached(bs, kSizeBlockstoreWriteCache)
if err != nil {
return err
}

rcfg, err := n.Repo.Config()
if err != nil {
return err
}

if rcfg.Datastore.HashOnRead {
bs.RuntimeHashing(true)
}

if cfg.Online {
rcfg, err := n.Repo.Config()
if err != nil {
return err
}
do := setupDiscoveryOption(rcfg.Discovery)
if err := n.startOnlineServices(ctx, cfg.Routing, cfg.Host, do); err != nil {
return err
Expand Down
5 changes: 3 additions & 2 deletions repo/config/datastore.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@ type Datastore struct {
StorageGCWatermark int64 // in percentage to multiply on StorageMax
GCPeriod string // in ns, us, ms, s, m, h

Params *json.RawMessage
NoSync bool
Params *json.RawMessage
NoSync bool
HashOnRead bool
}

func (d *Datastore) ParamData() []byte {
Expand Down
1 change: 1 addition & 0 deletions repo/config/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ func datastoreConfig() (Datastore, error) {
StorageMax: "10GB",
StorageGCWatermark: 90, // 90%
GCPeriod: "1h",
HashOnRead: false,
}, nil
}

Expand Down
37 changes: 37 additions & 0 deletions test/sharness/t0084-repo-read-rehash.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/bin/sh
#
# Copyright (c) Jakub Sztandera
# MIT Licensed; see the LICENSE file in this repository.
#

test_description="Test ipfs blockstore repo read check."

. lib/test-lib.sh

rm -rf "$IPF_PATH/*"

test_init_ipfs


H_BLOCK1=$(echo "Block 1" | ipfs add -q)
H_BLOCK2=$(echo "Block 2" | ipfs add -q)

BS_BLOCK1="1220f18e/1220f18e07ebc69997909358f28b9d2c327eb032b0afab6bbc7fd7f399a7b7590be4.data"
BS_BLOCK2="1220dc58/1220dc582e51f1f98b1f2d1c1baaa9f7b11602239ed42fbdf8f52d67e63cc03df12a.data"


test_expect_success 'blocks are swapped' '
ipfs cat $H_BLOCK2 > noswap &&
cp -f "$IPFS_PATH/blocks/$BS_BLOCK1" "$IPFS_PATH/blocks/$BS_BLOCK2" &&
ipfs cat $H_BLOCK2 > swap &&
test_must_fail test_cmp noswap swap
'

ipfs config --bool Datastore.HashOnRead true

test_expect_success 'getting modified block fails' '
(test_must_fail ipfs cat $H_BLOCK2 2> err_msg) &&
grep "block in storage has different hash than requested" err_msg
'

test_done