diff --git a/empty.go b/empty.go index 31e9c73c..3da31fd4 100644 --- a/empty.go +++ b/empty.go @@ -37,7 +37,7 @@ func (e Empty) InsertOrdered(key []byte, value []byte, _ NodeFlushFn) error { return e.Insert(key, value, nil) } -func (Empty) Delete([]byte) error { +func (Empty) Delete([]byte, NodeResolverFn) error { return errors.New("cant delete an empty node") } diff --git a/empty_test.go b/empty_test.go index bcc8f0de..c0e60d90 100644 --- a/empty_test.go +++ b/empty_test.go @@ -37,7 +37,7 @@ func TestEmptyFuncs(t *testing.T) { if err == nil { t.Fatal("got nil error when inserting into empty") } - err = e.Delete(zeroKeyTest) + err = e.Delete(zeroKeyTest, nil) if err == nil { t.Fatal("got nil error when deleting from empty") } diff --git a/hashednode.go b/hashednode.go index abbca8d0..ffe7b757 100644 --- a/hashednode.go +++ b/hashednode.go @@ -43,7 +43,7 @@ func (*HashedNode) InsertOrdered([]byte, []byte, NodeFlushFn) error { return errInsertIntoHash } -func (*HashedNode) Delete([]byte) error { +func (*HashedNode) Delete([]byte, NodeResolverFn) error { return errors.New("cant delete a hashed node in-place") } diff --git a/hashednode_test.go b/hashednode_test.go index 43554500..7eb0a093 100644 --- a/hashednode_test.go +++ b/hashednode_test.go @@ -37,7 +37,7 @@ func TestHashedNodeFuncs(t *testing.T) { if err == nil { t.Fatal("got nil error when inserting into a hashed node") } - err = e.Delete(zeroKeyTest) + err = e.Delete(zeroKeyTest, nil) if err == nil { t.Fatal("got nil error when deleting from a hashed node") } diff --git a/stateless.go b/stateless.go index 20af277c..026b904b 100644 --- a/stateless.go +++ b/stateless.go @@ -288,7 +288,7 @@ func (*StatelessNode) InsertOrdered([]byte, []byte, NodeFlushFn) error { return errNotSupportedInStateless } -func (n *StatelessNode) Delete(key []byte) error { +func (n *StatelessNode) Delete(key []byte, resolver NodeResolverFn) error { // Case of an ext node if n.values != nil { var zero [32]byte @@ -303,7 +303,7 @@ func (n *StatelessNode) Delete(key []byte) error { child := n.children[nChild] var pre Fr CopyFr(&pre, child.hash) - if err := child.Delete(key); err != nil { + if err := child.Delete(key, resolver); err != nil { return err } diff --git a/stateless_test.go b/stateless_test.go index d22fc7e0..5dd2b0e3 100644 --- a/stateless_test.go +++ b/stateless_test.go @@ -88,12 +88,12 @@ func TestStatelessDelete(t *testing.T) { t.Fatal("second insert didn't update") } - root.Delete(oneKeyTest) + root.Delete(oneKeyTest, nil) rootRef := New() rootRef.Insert(zeroKeyTest, fourtyKeyTest, nil) rootRef.Insert(oneKeyTest, fourtyKeyTest, nil) - rootRef.Delete(oneKeyTest) + rootRef.Delete(oneKeyTest, nil) if !Equal(rootRef.ComputeCommitment(), root.commitment) { t.Fatal("error in delete", rootRef.ComputeCommitment(), root.hash) diff --git a/tree.go b/tree.go index 9f6a9d5c..32fdeff0 100644 --- a/tree.go +++ b/tree.go @@ -65,7 +65,7 @@ type VerkleNode interface { InsertOrdered([]byte, []byte, NodeFlushFn) error // Delete a leaf with the given key - Delete([]byte) error + Delete([]byte, NodeResolverFn) error // Get value at a given key Get([]byte, NodeResolverFn) ([]byte, error) @@ -549,7 +549,7 @@ func (n *InternalNode) InsertStemOrdered(key []byte, leaf *LeafNode, flush NodeF return nil } -func (n *InternalNode) Delete(key []byte) error { +func (n *InternalNode) Delete(key []byte, resolver NodeResolverFn) error { // Clear cached commitment on modification n.commitment = nil @@ -558,9 +558,24 @@ func (n *InternalNode) Delete(key []byte) error { case Empty: return errDeleteNonExistent case *HashedNode: - return errDeleteHash + if resolver == nil { + return errDeleteHash + } + comm := child.commitment.Bytes() + payload, err := resolver(comm[:]) + if err != nil { + return err + } + // deserialize the payload and set it as the child + c, err := ParseNode(payload, n.depth+1, comm[:]) + if err != nil { + return err + } + c.ComputeCommitment() + n.children[nChild] = c + return n.Delete(key, resolver) default: - return child.Delete(key) + return child.Delete(key, resolver) } } @@ -855,7 +870,7 @@ func (n *LeafNode) InsertOrdered(key []byte, value []byte, _ NodeFlushFn) error return n.Insert(key, value, nil) } -func (n *LeafNode) Delete(k []byte) error { +func (n *LeafNode) Delete(k []byte, _ NodeResolverFn) error { // Sanity check: ensure the key header is the same: if !equalPaths(k, n.stem) { return errDeleteNonExistent diff --git a/tree_test.go b/tree_test.go index 83271244..2e9fe9e0 100644 --- a/tree_test.go +++ b/tree_test.go @@ -329,7 +329,7 @@ func TestDelLeaf(t *testing.T) { hash := tree.ComputeCommitment() tree.Insert(key3, fourtyKeyTest, nil) - if err := tree.Delete(key3); err != nil { + if err := tree.Delete(key3, nil); err != nil { t.Error(err) } @@ -354,7 +354,7 @@ func TestDeleteNonExistent(t *testing.T) { tree := New() tree.Insert(key1, fourtyKeyTest, nil) tree.Insert(key2, fourtyKeyTest, nil) - if err := tree.Delete(key3); err != errDeleteNonExistent { + if err := tree.Delete(key3, nil); err != errDeleteNonExistent { t.Error("should fail to delete non-existent key") } } @@ -373,7 +373,7 @@ func TestDeletePrune(t *testing.T) { hash2 := tree.ComputeCommitment() tree.Insert(key4, fourtyKeyTest, nil) - if err := tree.Delete(key4); err != nil { + if err := tree.Delete(key4, nil); err != nil { t.Error(err) } postHash := tree.ComputeCommitment() @@ -388,7 +388,7 @@ func TestDeletePrune(t *testing.T) { t.Error("leaf hasnt been deleted") } - if err := tree.Delete(key3); err != nil { + if err := tree.Delete(key3, nil); err != nil { t.Error(err) } postHash = tree.ComputeCommitment() @@ -413,7 +413,7 @@ func TestDeleteHash(t *testing.T) { tree.InsertOrdered(key2, fourtyKeyTest, nil) tree.InsertOrdered(key3, fourtyKeyTest, nil) tree.ComputeCommitment() - if err := tree.Delete(key2); err != errDeleteHash { + if err := tree.Delete(key2, nil); err != errDeleteHash { t.Fatalf("did not report the correct error while deleting from a hash: %v", err) } } @@ -427,10 +427,43 @@ func TestDeleteUnequalPath(t *testing.T) { tree.Insert(key3, fourtyKeyTest, nil) tree.ComputeCommitment() - if err := tree.Delete(key2); err != errDeleteNonExistent { + if err := tree.Delete(key2, nil); err != errDeleteNonExistent { t.Fatalf("didn't catch the deletion of non-existing key, err =%v", err) } } +func TestDeleteResolve(t *testing.T) { + key1, _ := hex.DecodeString("0105000000000000000000000000000000000000000000000000000000000000") + key2, _ := hex.DecodeString("0107000000000000000000000000000000000000000000000000000000000000") + key3, _ := hex.DecodeString("0405000000000000000000000000000000000000000000000000000000000000") + tree := New() + var savedNodes []VerkleNode + saveNode := func(node VerkleNode) { + savedNodes = append(savedNodes, node) + } + tree.InsertOrdered(key1, fourtyKeyTest, saveNode) + tree.InsertOrdered(key2, fourtyKeyTest, saveNode) + tree.InsertOrdered(key3, fourtyKeyTest, saveNode) + tree.ComputeCommitment() + + var called bool + err := tree.Delete(key2, func(comm []byte) ([]byte, error) { + called = true + for _, node := range savedNodes { + c := node.ComputeCommitment().Bytes() + if bytes.Equal(comm, c[:]) { + return node.Serialize() + } + } + t.Fatal("could not find node") + return nil, fmt.Errorf("node not found") + }) + if !called { + t.Fatal("should have called the resolve function") + } + if err != nil { + t.Fatalf("error deleting key: %v", err) + } +} func TestConcurrentTrees(t *testing.T) { tree := New()