-
Notifications
You must be signed in to change notification settings - Fork 26
[WIP] DB backend hooks #37
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
Changes from all commits
134c6d7
47f3999
48ecb95
11afb2e
a9f5710
e486e31
be7fbbb
4248857
41310aa
2cf8bf2
7ee58c0
26b661b
1626e77
c0c0c49
ac93761
328d7dd
e7496e3
c0f86c3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| package merkletree | ||
|
|
||
| import ( | ||
| "errors" | ||
|
|
||
| "github.com/coniks-sys/coniks-go/crypto" | ||
| "github.com/coniks-sys/coniks-go/storage/kv" | ||
| "github.com/coniks-sys/coniks-go/utils" | ||
| ) | ||
|
|
||
| var ( | ||
| ErrorBadTreeNonce = errors.New("[merkletree] Bad tree nonce") | ||
| ) | ||
|
|
||
| func NewMerkleTreeFromKV(db kv.DB, epoch uint64) (*MerkleTree, error) { | ||
| nonceKey := append([]byte{TreeNonceIdentifier}, util.ULongToBytes(epoch)...) | ||
| val, err := db.Get(nonceKey) | ||
| var nonce []byte | ||
| if err != nil { | ||
| return nil, err | ||
| } else if len(val) != crypto.HashSizeByte { | ||
| return nil, ErrorBadTreeNonce | ||
| } else { | ||
| nonce = val | ||
| } | ||
| m := new(MerkleTree) | ||
| m.nonce = nonce | ||
| root, err := m.reconstructTree(db, nil, epoch, []bool{}) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| m.root = root.(*interiorNode) | ||
| m.hash = m.root.Hash(m) | ||
| return m, nil | ||
| } | ||
|
|
||
| func (m *MerkleTree) reconstructTree(db kv.DB, parent MerkleNode, epoch uint64, prefixBits []bool) (MerkleNode, error) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason why |
||
| n, err := loadNode(db, epoch, prefixBits) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| if _, ok := n.(*emptyNode); ok { | ||
| n.(*emptyNode).parent = parent | ||
| return n, nil | ||
| } | ||
| if _, ok := n.(*userLeafNode); ok { | ||
| n.(*userLeafNode).parent = parent | ||
| return n, nil | ||
| } | ||
| n.(*interiorNode).parent = parent | ||
| n.(*interiorNode).leftChild, err = m.reconstructTree(db, n, epoch, append(prefixBits, false)) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| n.(*interiorNode).rightChild, err = m.reconstructTree(db, n, epoch, append(prefixBits, true)) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return n, nil | ||
| } | ||
|
|
||
| func ReconstructBranch(db kv.DB, epoch uint64, index []byte) (*MerkleTree, error) { | ||
| indexBits := util.ToBits(index) | ||
| m := new(MerkleTree) | ||
| root, err := loadNode(db, epoch, []bool{}) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| m.root = root.(*interiorNode) | ||
| var parent = root | ||
| loadingLoop: | ||
| for depth := 1; depth < len(indexBits); depth++ { | ||
| n, err := loadNode(db, epoch, indexBits[:depth]) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| if indexBits[depth-1] { | ||
| parent.(*interiorNode).rightChild = n | ||
| } else { | ||
| parent.(*interiorNode).leftChild = n | ||
| } | ||
| switch n.(type) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this switch needs a default case to throw an error in case we encounter some malformed data. |
||
| case *userLeafNode: | ||
| n.(*userLeafNode).parent = parent | ||
| break loadingLoop | ||
| case *emptyNode: | ||
| n.(*emptyNode).parent = parent | ||
| break loadingLoop | ||
| case *interiorNode: | ||
| n.(*interiorNode).parent = parent | ||
| } | ||
| parent = n | ||
| } | ||
|
|
||
| return m, nil | ||
| } | ||
|
|
||
| func (m *MerkleTree) StoreToKV(epoch uint64, wb kv.Batch) { | ||
| // store tree nodes | ||
| m.root.storeToKV(epoch, []bool{}, wb) | ||
| // store tree nonce | ||
| wb.Put(append([]byte{TreeNonceIdentifier}, util.ULongToBytes(epoch)...), m.nonce) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| package merkletree | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "testing" | ||
|
|
||
| "github.com/coniks-sys/coniks-go/storage/kv" | ||
| "github.com/coniks-sys/coniks-go/utils" | ||
| ) | ||
|
|
||
| func TestTreeStore(t *testing.T) { | ||
| util.WithDB(func(db kv.DB) { | ||
| key1 := "key1" | ||
| val1 := []byte("value1") | ||
| key2 := "key2" | ||
| val2 := []byte("value2") | ||
|
|
||
| m1, err := NewMerkleTree() | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| index1 := vrfPrivKey1.Compute([]byte(key1)) | ||
| if err := m1.Set(index1, key1, val1); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| index2 := vrfPrivKey1.Compute([]byte(key2)) | ||
| if err := m1.Set(index2, key2, val2); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| m1.recomputeHash() | ||
|
|
||
| wb := db.NewBatch() | ||
| m1.StoreToKV(1, wb) | ||
| err = db.Write(wb) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason why |
||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| m2, err := NewMerkleTreeFromKV(db, 1) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| if !bytes.Equal(m2.nonce, m1.nonce) || | ||
| !bytes.Equal(m2.root.Hash(m1), m1.hash) { | ||
| t.Fatal("Bad tree construction") | ||
| } | ||
|
|
||
| ap := m2.Get(index1) | ||
| if ap.Leaf.Value() == nil { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the only condition that indicates that
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same goes for L58, L105, and L120. |
||
| t.Error("Cannot find key:", key1) | ||
| return | ||
| } | ||
| if !bytes.Equal(ap.Leaf.Value(), val1) { | ||
| t.Error(key1, "value mismatch") | ||
| } | ||
|
|
||
| ap = m2.Get(index2) | ||
| if ap.Leaf.Value() == nil { | ||
| t.Error("Cannot find key:", key2) | ||
| return | ||
| } | ||
| if !bytes.Equal(ap.Leaf.Value(), val2) { | ||
| t.Error(key2, "value mismatch") | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| func TestReconstructBranch(t *testing.T) { | ||
| util.WithDB(func(db kv.DB) { | ||
| key1 := "key1" | ||
| val1 := []byte("value1") | ||
| key2 := "key3" | ||
| val2 := []byte("value2") | ||
|
|
||
| m1, err := NewMerkleTree() | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| index1 := vrfPrivKey1.Compute([]byte(key1)) | ||
| if err := m1.Set(index1, key1, val1); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| index2 := vrfPrivKey1.Compute([]byte(key2)) | ||
| if err := m1.Set(index2, key2, val2); err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| m1.recomputeHash() | ||
|
|
||
| wb := db.NewBatch() | ||
| m1.StoreToKV(1, wb) | ||
| err = db.Write(wb) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as L34. |
||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
|
|
||
| m2_1, err := ReconstructBranch(db, 1, index1) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| ap := m2_1.Get(index1) | ||
| if ap.Leaf.Value() == nil { | ||
| t.Error("Cannot find key:", key1) | ||
| return | ||
| } | ||
| if !bytes.Equal(ap.Leaf.Value(), val1) { | ||
| t.Error(key1, "value mismatch", | ||
| "want", val1, | ||
| "get", ap.Leaf.Value()) | ||
| } | ||
|
|
||
| m2_2, err := ReconstructBranch(db, 1, index2) | ||
| if err != nil { | ||
| t.Fatal(err) | ||
| } | ||
| ap = m2_2.Get(index2) | ||
| if ap.Leaf.Value() == nil { | ||
| t.Error("Cannot find key:", key2) | ||
| return | ||
| } | ||
| if !bytes.Equal(ap.Leaf.Value(), val2) { | ||
| t.Error(key2, "value mismatch") | ||
| } | ||
| }) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ package merkletree | |
|
|
||
| import ( | ||
| "github.com/coniks-sys/coniks-go/crypto" | ||
| "github.com/coniks-sys/coniks-go/storage/kv" | ||
| "github.com/coniks-sys/coniks-go/utils" | ||
| ) | ||
|
|
||
|
|
@@ -70,6 +71,7 @@ type MerkleNode interface { | |
| isEmpty() bool | ||
| Hash(*MerkleTree) []byte | ||
| Clone(*interiorNode) MerkleNode | ||
| storeToKV(epoch uint64, prefixBits []bool, wb kv.Batch) | ||
| } | ||
|
|
||
| var _ MerkleNode = (*userLeafNode)(nil) | ||
|
|
@@ -147,11 +149,11 @@ func (n *emptyNode) Clone(parent *interiorNode) MerkleNode { | |
| } | ||
| } | ||
|
|
||
| func (n *userLeafNode) isEmpty() bool { | ||
| func (n *interiorNode) isEmpty() bool { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason the node types were flipped between here and L156? |
||
| return false | ||
| } | ||
|
|
||
| func (n *interiorNode) isEmpty() bool { | ||
| func (n *userLeafNode) isEmpty() bool { | ||
| return false | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this is duplicate (see L27)