diff --git a/addbatch_test.go b/addbatch_test.go index 579c613..bbbb62f 100644 --- a/addbatch_test.go +++ b/addbatch_test.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "fmt" "math/big" + "os" "runtime" "sort" "testing" @@ -660,6 +661,7 @@ func benchAdd(t *testing.T, ks, vs [][]byte) { c := qt.New(t) dbDir := t.TempDir() + defer os.RemoveAll(dbDir) //nolint:errcheck // storage, err := pebble.NewPebbleStorage(dbDir, false) storage, err := leveldb.NewLevelDbStorage(dbDir, false) c.Assert(err, qt.IsNil) @@ -681,6 +683,8 @@ func benchAddBatch(t *testing.T, ks, vs [][]byte) { c := qt.New(t) dbDir := t.TempDir() + defer os.RemoveAll(dbDir) //nolint:errcheck + // storage, err := pebble.NewPebbleStorage(dbDir, false) storage, err := leveldb.NewLevelDbStorage(dbDir, false) c.Assert(err, qt.IsNil) tree, err := NewTree(storage, 140, HashFunctionBlake2b) diff --git a/tree.go b/tree.go index d2be1ab..c19d231 100644 --- a/tree.go +++ b/tree.go @@ -278,23 +278,24 @@ func (t *Tree) down(newKey, currKey []byte, siblings [][]byte, switch currValue[0] { case PrefixValueEmpty: // empty - // TODO WIP WARNING should not be reached, as the 'if' above should avoid - // reaching this point - // return currKey, empty, siblings, nil - panic("should not be reached, as the 'if' above should avoid reaching this point") // TMP + fmt.Printf("newKey: %s, currKey: %s, currLvl: %d, currValue: %s\n", + hex.EncodeToString(newKey), hex.EncodeToString(currKey), + currLvl, hex.EncodeToString(currValue)) + panic("This point should not be reached, as the 'if' above" + + " should avoid reaching this point. This panic is temporary" + + " for reporting purposes, will be deleted in future versions." + + " Please paste this log (including the previous lines) in a" + + " new issue: https://github.com/arnaucube/arbo/issues/new") // TMP case PrefixValueLeaf: // leaf - if bytes.Equal(newKey, currKey) { - // TODO move this error msg to const & add test that - // checks that adding a repeated key this error is - // returned - return nil, nil, nil, ErrKeyAlreadyExists - } - if !bytes.Equal(currValue, emptyValue) { if getLeaf { return currKey, currValue, siblings, nil } oldLeafKey, _ := ReadLeafValue(currValue) + if bytes.Equal(newKey, oldLeafKey) { + return nil, nil, nil, ErrKeyAlreadyExists + } + oldLeafKeyFull := make([]byte, t.hashFunction.Len()) copy(oldLeafKeyFull[:], oldLeafKey) @@ -385,6 +386,12 @@ func (t *Tree) newLeafValue(k, v []byte) ([]byte, []byte, error) { return newLeafValue(t.hashFunction, k, v) } +// newLeafValue takes a key & value from a leaf, and computes the leaf hash, +// which is used as the leaf key. And the value is the concatenation of the +// inputed key & value. The output of this function is used as key-value to +// store the leaf in the DB. +// [ 1 byte | 1 byte | N bytes | M bytes ] +// [ type of node | length of key | key | value ] func newLeafValue(hashFunc HashFunction, k, v []byte) ([]byte, []byte, error) { leafKey, err := hashFunc.Hash(k, v, []byte{1}) if err != nil { @@ -418,6 +425,12 @@ func (t *Tree) newIntermediate(l, r []byte) ([]byte, []byte, error) { return newIntermediate(t.hashFunction, l, r) } +// newIntermediate takes the left & right keys of a intermediate node, and +// computes its hash. Returns the hash of the node, which is the node key, and a +// byte array that contains the value (which contains the left & right child +// keys) to store in the DB. +// [ 1 byte | 1 byte | N bytes | N bytes ] +// [ type of node | length of key | left key | right key ] func newIntermediate(hashFunc HashFunction, l, r []byte) ([]byte, []byte, error) { b := make([]byte, PrefixValueLen+hashFunc.Len()*2) b[0] = 2 diff --git a/tree_test.go b/tree_test.go index 273b15b..6ecf112 100644 --- a/tree_test.go +++ b/tree_test.go @@ -141,12 +141,11 @@ func TestAddRepeatedIndex(t *testing.T) { bLen := tree.HashFunction().Len() k := BigIntToBytes(bLen, big.NewInt(int64(3))) v := BigIntToBytes(bLen, big.NewInt(int64(12))) - if err := tree.Add(k, v); err != nil { - t.Fatal(err) - } + err = tree.Add(k, v) - c.Assert(err, qt.Not(qt.IsNil)) - c.Check(err, qt.Equals, ErrMaxVirtualLevel) + c.Assert(err, qt.IsNil) + err = tree.Add(k, v) // repeating same key-value + c.Check(err, qt.Equals, ErrKeyAlreadyExists) } func TestUpdate(t *testing.T) { diff --git a/vt.go b/vt.go index 5d13f95..7ea8900 100644 --- a/vt.go +++ b/vt.go @@ -245,6 +245,7 @@ func (n *node) getNodesAtLevel(currLvl, l int) ([]*node, error) { return nodes, nil } +// upFromNodes builds the tree from the bottom to up func upFromNodes(ns []*node) (*node, error) { if len(ns) == 1 { return ns[0], nil @@ -267,6 +268,7 @@ func upFromNodes(ns []*node) (*node, error) { return upFromNodes(res) } +// add adds a key&value as a leaf in the VirtualTree func (t *vt) add(fromLvl int, k, v []byte) error { leaf := newLeafNode(t.params, k, v) if t.root == nil { @@ -282,7 +284,8 @@ func (t *vt) add(fromLvl int, k, v []byte) error { } // computeHashes should be called after all the vt.add is used, once all the -// leafs are in the tree +// leafs are in the tree. Computes the hashes of the tree, parallelizing in the +// available CPUs. func (t *vt) computeHashes() ([][2][]byte, error) { var err error @@ -519,7 +522,8 @@ func flp2(n int) int { return res } -// returns an array of key-values to store in the db +// computeHashes computes the hashes under the node from which is called the +// method. Returns an array of key-values to store in the db func (n *node) computeHashes(currLvl, maxLvl int, p *params, pairs [][2][]byte) ( [][2][]byte, error) { if n == nil || currLvl >= maxLvl {