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 tracing to Trees and Deep Subtrees #18

Merged
merged 64 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
0d0fa65
Add go fuzz tests
Manav-Aggarwal Nov 21, 2022
66328cd
Add membership proof for existing keys
Manav-Aggarwal Nov 21, 2022
f77bbcc
Build tree after adding membership proof
Manav-Aggarwal Nov 21, 2022
0391a5b
Make batch add fuzz tests work
Manav-Aggarwal Nov 21, 2022
837ad95
Do not commit version twice for dst
Manav-Aggarwal Nov 21, 2022
a4496b6
Save version out of dst.Set condition
Manav-Aggarwal Nov 21, 2022
05c45a5
Set rootHash to workingHash
Manav-Aggarwal Nov 21, 2022
4a3ddf1
Fix edge cases
Manav-Aggarwal Nov 22, 2022
b7e049c
Refactor DST Non-Existence Proof
Manav-Aggarwal Nov 22, 2022
2b3e980
Change cacheSize
Manav-Aggarwal Nov 22, 2022
5b8f0dd
Add test data and sibling nodes for each node in path
Manav-Aggarwal Nov 23, 2022
de2fbe6
Fix GetSiblingNodes
Manav-Aggarwal Nov 23, 2022
d416ad6
Add more test data
Manav-Aggarwal Nov 23, 2022
81fd580
testing: fuzz: failing test case
tzdybal Nov 23, 2022
6c5e4f3
Use set for keys
Manav-Aggarwal Nov 23, 2022
e478f31
Add more test data
Manav-Aggarwal Nov 23, 2022
ae86a4c
Refactor existence proofs code
Manav-Aggarwal Nov 23, 2022
cec8884
Add required info for remove operation
Manav-Aggarwal Nov 23, 2022
0dd98e2
Add children of siblings as well
Manav-Aggarwal Nov 24, 2022
154ed5d
Remove debug code
Manav-Aggarwal Nov 24, 2022
76a375f
Add testdata that breaks current code
Manav-Aggarwal Nov 24, 2022
55b482f
Fix bug
Manav-Aggarwal Nov 24, 2022
8af01e1
Add failing testcase
Manav-Aggarwal Nov 24, 2022
7078d03
Add breaking testcase
Manav-Aggarwal Nov 25, 2022
30906fb
IAVL with tracing
Manav-Aggarwal Nov 25, 2022
aa5dc48
Fuzz tests pass
Manav-Aggarwal Nov 25, 2022
07eade4
Refactor tracing code
Manav-Aggarwal Nov 25, 2022
a8a10d2
Remove redundant code
Manav-Aggarwal Nov 25, 2022
fa061e3
Remove working hash in calls to AddExistenceProof
Manav-Aggarwal Nov 25, 2022
bb51e4a
Clean up flag
Manav-Aggarwal Nov 25, 2022
9a9adb0
Make build tree a private method
Manav-Aggarwal Nov 25, 2022
252d0ff
Add back whitespace in node.go
Manav-Aggarwal Nov 25, 2022
e0343e3
Add new ci for fuzz
Manav-Aggarwal Nov 25, 2022
f1e4545
Refactor more
Manav-Aggarwal Nov 25, 2022
f9a41f5
Refactor out getKey method
Manav-Aggarwal Nov 25, 2022
52629c3
Change name to reapInclusionProofs
Manav-Aggarwal Nov 25, 2022
add675c
Refactor set and remove in DST
Manav-Aggarwal Nov 25, 2022
7b1767c
Factor out add existence proofs from Remove DST for consistency with Set
Manav-Aggarwal Nov 25, 2022
4ec78b8
Refactor into testContext
Manav-Aggarwal Nov 25, 2022
e13a09a
Clean up setInDST
Manav-Aggarwal Nov 27, 2022
4f96374
Add method for get
Manav-Aggarwal Nov 27, 2022
5eee6e6
Export methods
Manav-Aggarwal Nov 27, 2022
d84e54f
Add witness data to deep subtree
Manav-Aggarwal Nov 27, 2022
86b941c
Verify operation in witness data
Manav-Aggarwal Nov 27, 2022
4eee605
Refactor and verify operation for get and remove
Manav-Aggarwal Nov 27, 2022
cf0ba69
Add set witness data
Manav-Aggarwal Nov 28, 2022
5cebe57
Add tracing to tree
Manav-Aggarwal Nov 29, 2022
c43cce7
add getter for witness data
Manav-Aggarwal Nov 29, 2022
7b466bc
Merge branch 'deepsubtrees' into manav/new_iavl_design
Manav-Aggarwal Nov 29, 2022
a141084
Verify existence proofs in dst
Manav-Aggarwal Nov 29, 2022
17c3e7c
Cleanup
Manav-Aggarwal Nov 29, 2022
d6b8119
Reset witness data on tracing enabled
Manav-Aggarwal Dec 1, 2022
12d28ac
Add node to keysAccessed even not in cache
Manav-Aggarwal Dec 1, 2022
4727534
Add initial root hash
Manav-Aggarwal Dec 1, 2022
f0a2d28
Refactor GetInitialRootHash
Manav-Aggarwal Dec 1, 2022
b174a3f
Modify GetInitialRootHash
Manav-Aggarwal Dec 1, 2022
f388340
Add test data
Manav-Aggarwal Dec 1, 2022
803cbb6
Add get to dst tests: fails right now
Manav-Aggarwal Dec 1, 2022
5ae6efb
Refactor and add tracing for root key as well
Manav-Aggarwal Dec 1, 2022
6c51f9f
Add docs
Manav-Aggarwal Dec 1, 2022
0bbb18f
Add more docs
Manav-Aggarwal Dec 1, 2022
9fb2a6b
Update comments
Manav-Aggarwal Dec 1, 2022
0ea5fe0
Update log message
Manav-Aggarwal Dec 6, 2022
5fc90f8
allocate length
Manav-Aggarwal Dec 6, 2022
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
205 changes: 122 additions & 83 deletions deepsubtree.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"fmt"
"strings"

"github.com/chrispappas/golang-generics-set/set"
ics23 "github.com/confio/ics23/go"
dbm "github.com/tendermint/tm-db"
)
Expand All @@ -21,14 +20,15 @@ const (
// a subset of nodes of an IAVL tree
type DeepSubTree struct {
*MutableTree
// witnessData WitnessData
// counter int
initialRootHash []byte // Initial Root Hash when Deep Subtree is initialized for an already existing tree
witnessData []WitnessData // Represents a trace operation along with inclusion proofs required for said operation
operationCounter int // Keeps track of which operation in the witness data list the Deep Subtree is on
}

// NewDeepSubTree returns a new deep subtree with the specified cache size, datastore, and version.
func NewDeepSubTree(db dbm.DB, cacheSize int, skipFastStorageUpgrade bool, version int64) *DeepSubTree {
ndb := newNodeDB(db, cacheSize, nil)
head := &ImmutableTree{ndb: ndb, version: version}
head := &ImmutableTree{ndb: ndb, version: version, skipFastStorageUpgrade: skipFastStorageUpgrade}
Manav-Aggarwal marked this conversation as resolved.
Show resolved Hide resolved
mutableTree := &MutableTree{
ImmutableTree: head,
lastSaved: head.clone(),
Expand All @@ -40,7 +40,27 @@ func NewDeepSubTree(db dbm.DB, cacheSize int, skipFastStorageUpgrade bool, versi
ndb: ndb,
skipFastStorageUpgrade: skipFastStorageUpgrade,
}
return &DeepSubTree{MutableTree: mutableTree}
return &DeepSubTree{MutableTree: mutableTree, initialRootHash: nil, witnessData: nil, operationCounter: 0}
}

// Setter for witness data. Also, resets the operation counter back to 0.
func (dst *DeepSubTree) SetWitnessData(witnessData []WitnessData) {
dst.witnessData = witnessData
dst.operationCounter = 0
}

// Returns the initial root hash if it is initialized and Deep Subtree root is nil.
// Otherwise, returns the Deep Subtree working hash is considered the initial root hash.
func (dst *DeepSubTree) GetInitialRootHash() ([]byte, error) {
if dst.root == nil && dst.initialRootHash != nil {
return dst.initialRootHash, nil
}
return dst.WorkingHash()
}

// Setter for initial root hash
func (dst *DeepSubTree) SetInitialRootHash(initialRootHash []byte) {
dst.initialRootHash = initialRootHash
}

func (node *Node) updateInnerNodeKey() {
Expand All @@ -63,7 +83,7 @@ func (dst *DeepSubTree) buildTree(rootHash []byte) error {
if !bytes.Equal(workingHash, rootHash) {
if dst.root != nil {
return fmt.Errorf(
"deep Subtree rootHash: %s does not match expected rootHash: %s",
"deep subtree rootHash: %s does not match expected rootHash: %s",
workingHash,
rootHash,
)
Expand Down Expand Up @@ -119,10 +139,58 @@ func (dst *DeepSubTree) linkNode(node *Node) error {
return nil
}

// Set sets a key in the working tree with the given value.
// Assumption: Node with given key already exists and is a leaf node.
// Modified version of set taken from mutable_tree.go
// Verifies the given operation matches up with the witness data.
// Also, verifies and adds existence proofs related to the operation.
func (dst *DeepSubTree) verifyOperationAndProofs(operation Operation, key []byte, value []byte) error {
if dst.witnessData == nil {
return errors.New("witness data in deep subtree is nil")
}
if dst.operationCounter >= len(dst.witnessData) {
return fmt.Errorf(
"operation counter in witness data: %d should be less than length of witness data: %d",
dst.operationCounter,
len(dst.witnessData),
)
}
traceOp := dst.witnessData[dst.operationCounter]
if traceOp.Operation != operation || !bytes.Equal(traceOp.Key, key) || !bytes.Equal(traceOp.Value, value) {
return fmt.Errorf(
"traceOp in witnessData (%s, %s, %s) does not match up with executed operation (%s, %s, %s)",
traceOp.Operation, string(traceOp.Key), string(traceOp.Value),
operation, string(key), string(value),
)
}
rootHash, err := dst.GetInitialRootHash()
if err != nil {
return err
}

// Verify proofs against current rootHash
for _, proof := range traceOp.Proofs {
err := proof.Verify(ics23.IavlSpec, rootHash, proof.Key, proof.Value)
if err != nil {
return err
}
}
err = dst.AddExistenceProofs(traceOp.Proofs, rootHash)
if err != nil {
return err
}
dst.operationCounter++
return nil
}

// Verifies the Set operation with witness data and perform the given write operation
func (dst *DeepSubTree) Set(key []byte, value []byte) (updated bool, err error) {
err = dst.verifyOperationAndProofs("write", key, value)
if err != nil {
return false, err
}
return dst.set(key, value)
}

// Sets a key in the working tree with the given value.
func (dst *DeepSubTree) set(key []byte, value []byte) (updated bool, err error) {
if value == nil {
return updated, fmt.Errorf("attempt to store nil value at key '%s'", key)
}
Expand All @@ -132,7 +200,6 @@ func (dst *DeepSubTree) Set(key []byte, value []byte) (updated bool, err error)
return updated, nil
}

// TODO: verify operation is on top, look at the witness data and add the relevant existence proofs
dst.root, updated, err = dst.recursiveSet(dst.root, key, value)
if err != nil {
return updated, err
Expand Down Expand Up @@ -224,9 +291,37 @@ func (dst *DeepSubTree) recursiveSet(node *Node, key []byte, value []byte) (
return newNode, updated, err
}

// Remove tries to remove a key from the tree and if removed, returns its
// value, nodes orphaned and 'true'.
// Verifies the Get operation with witness data and perform the given read operation
func (dst *DeepSubTree) Get(key []byte) (value []byte, err error) {
err = dst.verifyOperationAndProofs("read", key, nil)
if err != nil {
return nil, err
}
return dst.get(key)
}

// Get returns the value of the specified key if it exists, or nil otherwise.
// The returned value must not be modified, since it may point to data stored within IAVL.
func (dst *DeepSubTree) get(key []byte) ([]byte, error) {
if dst.root == nil {
return nil, nil
}

return dst.ImmutableTree.Get(key)
}

// Verifies the Remove operation with witness data and perform the given delete operation
func (dst *DeepSubTree) Remove(key []byte) (value []byte, removed bool, err error) {
err = dst.verifyOperationAndProofs("delete", key, nil)
if err != nil {
return nil, false, err
}
return dst.remove(key)
}

// Remove tries to remove a key from the tree and if removed, returns its
// value, and 'true'.
func (dst *DeepSubTree) remove(key []byte) (value []byte, removed bool, err error) {
if dst.root == nil {
return nil, false, nil
}
Expand Down Expand Up @@ -338,59 +433,6 @@ func (dst *DeepSubTree) recursiveRemove(node *Node, key []byte) (newHash []byte,
return nil, nil, nil, fmt.Errorf("node with key: %s not found", key)
}

func (tree *MutableTree) getExistenceProofsNeededForSet(key []byte, value []byte) ([]*ics23.ExistenceProof, error) {
_, err := tree.Set(key, value)

if err != nil {
return nil, err
}

keysAccessed := tree.ndb.keysAccessed.Values()
tree.ndb.keysAccessed = make(set.Set[string])

tree.Rollback()

return tree.reapInclusionProofs(keysAccessed)
}

func (tree *MutableTree) getExistenceProofsNeededForRemove(key []byte) ([]*ics23.ExistenceProof, error) {
ics23proof, err := tree.GetMembershipProof(key)
if err != nil {
return nil, err
}

_, _, err = tree.Remove(key)
if err != nil {
return nil, err
}

keysAccessed := tree.ndb.keysAccessed.Values()
tree.ndb.keysAccessed = make(set.Set[string])

tree.Rollback()

keysAccessed = append(keysAccessed, string(key))

existenceProofs, err := tree.reapInclusionProofs(keysAccessed)
if err != nil {
return nil, err
}
existenceProofs = append(existenceProofs, ics23proof.GetExist())
return existenceProofs, nil
}

func (tree *MutableTree) reapInclusionProofs(keysAccessed []string) ([]*ics23.ExistenceProof, error) {
existenceProofs := make([]*ics23.ExistenceProof, 0)
for _, key := range keysAccessed {
ics23proof, err := tree.GetMembershipProof([]byte(key))
if err != nil {
return nil, err
}
existenceProofs = append(existenceProofs, ics23proof.GetExist())
}
return existenceProofs, nil
}

func recomputeHash(node *Node) error {
if node.leftHash == nil && node.leftNode != nil {
leftHash, err := node.leftNode._hash()
Expand Down Expand Up @@ -502,18 +544,24 @@ func (dst *DeepSubTree) AddExistenceProofs(existenceProofs []*ics23.ExistencePro
return err
}
}
if rootHash == nil {
workingHash, err := dst.WorkingHash()
if err != nil {
return err
}
rootHash = workingHash
err := dst.buildTree(rootHash)
if err != nil {
return err
}
return nil
}

err := dst.buildTree(rootHash)
func (dst *DeepSubTree) saveNodeIfNeeded(node *Node) error {
has, err := dst.ndb.Has(node.hash)
if err != nil {
return err
}
if !has {
err = dst.ndb.SaveNode(node)
if err != nil {
return err
}
}
return nil
}

Expand All @@ -522,7 +570,7 @@ func (dst *DeepSubTree) addExistenceProof(proof *ics23.ExistenceProof) error {
if err != nil {
return err
}
err = dst.ndb.SaveNode(leaf)
err = dst.saveNodeIfNeeded(leaf)
if err != nil {
return err
}
Expand All @@ -535,16 +583,7 @@ func (dst *DeepSubTree) addExistenceProof(proof *ics23.ExistenceProof) error {
}
prevHash = inner.hash

has, err := dst.ndb.Has(inner.hash)
if err != nil {
return err
}
if !has {
err = dst.ndb.SaveNode(inner)
if err != nil {
return err
}
}
dst.saveNodeIfNeeded(inner)
}
return nil
}
Expand Down
Loading