Skip to content

MerkleDB Adjust New View function(s) #1927

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

Merged
merged 18 commits into from
Aug 27, 2023
Merged
Show file tree
Hide file tree
Changes from 4 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
84 changes: 54 additions & 30 deletions x/merkledb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ const (
)

var (
_ TrieView = (*merkleDB)(nil)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

No longer needed.

_ MerkleDB = (*merkleDB)(nil)

codec = newCodec()
Expand All @@ -50,9 +49,15 @@ var (
hadCleanShutdown = []byte{1}
didNotHaveCleanShutdown = []byte{0}

errSameRoot = errors.New("start and end root are the same")
errSameRoot = errors.New("start and end root are the same")
errNoNewRoot = errors.New("there was no updated root in change list")
)

type ChangeOp struct {
Value []byte
Delete bool
}

type ChangeProofer interface {
// GetChangeProof returns a proof for a subset of the key/value changes in key range
// [start, end] that occurred between [startRootID] and [endRootID].
Expand Down Expand Up @@ -255,7 +260,7 @@ func (db *merkleDB) rebuild(ctx context.Context) error {

for it.Next() {
if len(currentOps) >= viewSizeLimit {
view, err := db.newUntrackedView(currentOps)
view, err := db.newUntrackedView(currentOps, false)
if err != nil {
return err
}
Expand All @@ -282,7 +287,7 @@ func (db *merkleDB) rebuild(ctx context.Context) error {
if err := it.Error(); err != nil {
return err
}
view, err := db.newUntrackedView(currentOps)
view, err := db.newUntrackedView(currentOps, false)
if err != nil {
return err
}
Expand All @@ -308,7 +313,7 @@ func (db *merkleDB) CommitChangeProof(ctx context.Context, proof *ChangeProof) e
}
}

view, err := db.newUntrackedView(ops)
view, err := db.newUntrackedView(ops, true)
if err != nil {
return err
}
Expand Down Expand Up @@ -349,7 +354,7 @@ func (db *merkleDB) CommitRangeProof(ctx context.Context, start maybe.Maybe[[]by
}

// Don't need to lock [view] because nobody else has a reference to it.
view, err := db.newUntrackedView(ops)
view, err := db.newUntrackedView(ops, true)
if err != nil {
return err
}
Expand Down Expand Up @@ -504,7 +509,7 @@ func (db *merkleDB) getProof(ctx context.Context, key []byte) (*Proof, error) {
return nil, database.ErrClosed
}

view, err := db.newUntrackedView(nil)
view, err := db.newUntrackedView(nil, false)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -655,15 +660,47 @@ func (db *merkleDB) GetChangeProof(
return result, nil
}

// NewView returns a new view on top of this trie.
// NewViewFromBatchOps returns a new view on top of this trie.
// Changes made to the view will only be reflected in the original trie if Commit is called.
// if copyBytes is true, code will duplicate any passed []byte so that editing in calling code is safe
// Assumes [db.commitLock] and [db.lock] aren't held.
func (db *merkleDB) NewViewFromBatchOps(_ context.Context, batchOps []database.BatchOp, copyBytes bool) (TrieView, error) {
return db.newView(func() (*trieView, error) {
return newTrieViewFromBatchOps(db, db, db.root.clone(), batchOps, copyBytes)
})
}

// Returns a new view that isn't tracked in [db.childViews].
// For internal use only, namely in methods that create short-lived views.
// Assumes [db.lock] isn't held and [db.commitLock] is read locked.
func (db *merkleDB) newUntrackedView(batchOps []database.BatchOp, copyBytes bool) (*trieView, error) {
return newTrieViewFromBatchOps(db, db, db.root.clone(), batchOps, copyBytes)
}

// NewViewFromMap returns a new view on top of this trie.
// Changes made to the view will only be reflected in the original trie if Commit is called.
// if copyBytes is true, code will duplicate any passed []byte so that editing in calling code is safe
// Assumes [db.commitLock] and [db.lock] aren't held.
func (db *merkleDB) NewView(_ context.Context, batchOps []database.BatchOp) (TrieView, error) {
func (db *merkleDB) NewViewFromMap(_ context.Context, changes map[string]ChangeOp, copyBytes bool) (TrieView, error) {
return db.newView(func() (*trieView, error) {
return newTrieViewFromMap(db, db, db.root.clone(), changes, copyBytes)
})
}

// newView returns a new view on top of this trie that has been added to childViews
// Wrapper function for NewViewFromMap and NewViewFromBatchOps
// Changes made to the view will only be reflected in the original trie if Commit is called.
// Assumes [db.commitLock] and [db.lock] aren't held.
func (db *merkleDB) newView(createView func() (*trieView, error)) (TrieView, error) {
// ensure the db doesn't change while creating the new view
db.commitLock.RLock()
defer db.commitLock.RUnlock()

newView, err := db.newUntrackedView(batchOps)
if db.closed {
return nil, database.ErrClosed
}

newView, err := createView()
if err != nil {
return nil, err
}
Expand All @@ -676,21 +713,6 @@ func (db *merkleDB) NewView(_ context.Context, batchOps []database.BatchOp) (Tri
return newView, nil
}

// Returns a new view that isn't tracked in [db.childViews].
// For internal use only, namely in methods that create short-lived views.
// Assumes [db.lock] isn't held and [db.commitLock] is read locked.
func (db *merkleDB) newUntrackedView(batchOps []database.BatchOp) (*trieView, error) {
if db.closed {
return nil, database.ErrClosed
}
Comment on lines -683 to -685
Copy link
Contributor

Choose a reason for hiding this comment

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

Kind of weird that this was removed - but because this is an internal function it makes sense that this should never really happen anyways

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we always check it before we call this function, so it couldn't really happen as you said.


newView, err := newTrieView(db, db, db.root.clone(), batchOps)
if err != nil {
return nil, err
}
return newView, nil
}

func (db *merkleDB) Has(k []byte) (bool, error) {
db.lock.RLock()
defer db.lock.RUnlock()
Expand Down Expand Up @@ -825,7 +847,8 @@ func (db *merkleDB) PutContext(ctx context.Context, k, v []byte) error {
Key: k,
Value: v,
},
})
},
true)
if err != nil {
return err
}
Expand All @@ -849,7 +872,7 @@ func (db *merkleDB) DeleteContext(ctx context.Context, key []byte) error {
Key: key,
Delete: true,
},
})
}, false)
if err != nil {
return err
}
Expand All @@ -864,7 +887,7 @@ func (db *merkleDB) commitBatch(ops []database.BatchOp) error {
return database.ErrClosed
}

view, err := db.newUntrackedView(ops)
view, err := db.newUntrackedView(ops, true)
if err != nil {
return err
}
Expand Down Expand Up @@ -1075,7 +1098,7 @@ func (db *merkleDB) VerifyChangeProof(
}

// Don't need to lock [view] because nobody else has a reference to it.
view, err := db.newUntrackedView(ops)
view, err := db.newUntrackedView(ops, true)
if err != nil {
return err
}
Expand Down Expand Up @@ -1184,7 +1207,8 @@ func (db *merkleDB) getHistoricalViewForRange(

// looking for the trie's current root id, so return the trie unmodified
if currentRootID == rootID {
return newTrieView(db, db, db.root.clone(), nil)
// create an empty trie
return newTrieViewFromBatchOps(db, db, db.root.clone(), nil, false)
}

changeHistory, err := db.history.getChangesToGetToRoot(rootID, start, end)
Expand Down
38 changes: 19 additions & 19 deletions x/merkledb/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ func Test_MerkleDB_DB_Load_Root_From_DB(t *testing.T) {
k := []byte(strconv.Itoa(i))
ops = append(ops, database.BatchOp{Key: k, Value: hashing.ComputeHash256(k)})
}
view, err := db.NewView(context.Background(), ops)
view, err := db.NewViewFromBatchOps(context.Background(), ops, true)
require.NoError(err)
require.NoError(view.CommitToDB(context.Background()))

Expand Down Expand Up @@ -171,7 +171,7 @@ func Test_MerkleDB_DB_Rebuild(t *testing.T) {
k := []byte(strconv.Itoa(i))
ops = append(ops, database.BatchOp{Key: k, Value: hashing.ComputeHash256(k)})
}
view, err := db.NewView(context.Background(), ops)
view, err := db.NewViewFromBatchOps(context.Background(), ops, true)
require.NoError(err)
require.NoError(view.CommitToDB(context.Background()))

Expand Down Expand Up @@ -246,12 +246,12 @@ func Test_MerkleDB_Invalidate_Siblings_On_Commit(t *testing.T) {
require.NoError(err)
require.NotNil(dbTrie)

viewToCommit, err := dbTrie.NewView(context.Background(), []database.BatchOp{{Key: []byte{0}, Value: []byte{0}}})
viewToCommit, err := dbTrie.NewViewFromBatchOps(context.Background(), []database.BatchOp{{Key: []byte{0}, Value: []byte{0}}}, true)
require.NoError(err)

sibling1, err := dbTrie.NewView(context.Background(), nil)
sibling1, err := dbTrie.NewViewFromBatchOps(context.Background(), nil, true)
require.NoError(err)
sibling2, err := dbTrie.NewView(context.Background(), nil)
sibling2, err := dbTrie.NewViewFromBatchOps(context.Background(), nil, true)
require.NoError(err)

require.False(sibling1.(*trieView).isInvalid())
Expand Down Expand Up @@ -456,7 +456,7 @@ func TestDatabaseNewUntrackedView(t *testing.T) {
require.NoError(err)

// Create a new untracked view.
view, err := db.newUntrackedView([]database.BatchOp{{Key: []byte{1}, Value: []byte{1}}})
view, err := db.newUntrackedView([]database.BatchOp{{Key: []byte{1}, Value: []byte{1}}}, true)
require.NoError(err)
require.Empty(db.childViews)

Expand All @@ -468,14 +468,14 @@ func TestDatabaseNewUntrackedView(t *testing.T) {
}

// Test that tracked views are persisted to [db.childViews].
func TestDatabaseNewViewTracked(t *testing.T) {
func TestDatabaseNewViewFromBatchOpsTracked(t *testing.T) {
require := require.New(t)

db, err := getBasicDB()
require.NoError(err)

// Create a new tracked view.
view, err := db.NewView(context.Background(), []database.BatchOp{{Key: []byte{1}, Value: []byte{1}}})
view, err := db.NewViewFromBatchOps(context.Background(), []database.BatchOp{{Key: []byte{1}, Value: []byte{1}}}, true)
require.NoError(err)
require.Len(db.childViews, 1)

Expand All @@ -499,7 +499,7 @@ func TestDatabaseCommitChanges(t *testing.T) {
require.Equal(dbRoot, db.getMerkleRoot()) // Root didn't change

// Committing an invalid view should fail.
invalidView, err := db.NewView(context.Background(), nil)
invalidView, err := db.NewViewFromBatchOps(context.Background(), nil, true)
require.NoError(err)
invalidView.(*trieView).invalidate()
err = invalidView.CommitToDB(context.Background())
Expand All @@ -510,24 +510,24 @@ func TestDatabaseCommitChanges(t *testing.T) {
require.NoError(db.Put([]byte{2}, []byte{2}))

// Make a view and insert/delete a key-value pair.
view1Intf, err := db.NewView(context.Background(), []database.BatchOp{
view1Intf, err := db.NewViewFromBatchOps(context.Background(), []database.BatchOp{
{Key: []byte{3}, Value: []byte{3}},
{Key: []byte{1}, Delete: true},
})
}, true)
require.NoError(err)
require.IsType(&trieView{}, view1Intf)
view1 := view1Intf.(*trieView)
view1Root, err := view1.GetMerkleRoot(context.Background())
require.NoError(err)

// Make a second view
view2Intf, err := db.NewView(context.Background(), nil)
view2Intf, err := db.NewViewFromBatchOps(context.Background(), nil, true)
require.NoError(err)
require.IsType(&trieView{}, view2Intf)
view2 := view2Intf.(*trieView)

// Make a view atop a view
view3Intf, err := view1.NewView(context.Background(), nil)
view3Intf, err := view1.NewViewFromBatchOps(context.Background(), nil, true)
require.NoError(err)
require.IsType(&trieView{}, view3Intf)
view3 := view3Intf.(*trieView)
Expand Down Expand Up @@ -577,17 +577,17 @@ func TestDatabaseInvalidateChildrenExcept(t *testing.T) {
require.NoError(err)

// Create children
view1Intf, err := db.NewView(context.Background(), nil)
view1Intf, err := db.NewViewFromBatchOps(context.Background(), nil, true)
require.NoError(err)
require.IsType(&trieView{}, view1Intf)
view1 := view1Intf.(*trieView)

view2Intf, err := db.NewView(context.Background(), nil)
view2Intf, err := db.NewViewFromBatchOps(context.Background(), nil, true)
require.NoError(err)
require.IsType(&trieView{}, view2Intf)
view2 := view2Intf.(*trieView)

view3Intf, err := db.NewView(context.Background(), nil)
view3Intf, err := db.NewViewFromBatchOps(context.Background(), nil, true)
require.NoError(err)
require.IsType(&trieView{}, view3Intf)
view3 := view3Intf.(*trieView)
Expand Down Expand Up @@ -668,15 +668,15 @@ func Test_MerkleDB_Random_Insert_Ordering(t *testing.T) {
}
db, err := getBasicDB()
require.NoError(err)
result, err := db.NewView(context.Background(), ops)
result, err := db.NewViewFromBatchOps(context.Background(), ops, true)
require.NoError(err)
primaryRoot, err := result.GetMerkleRoot(context.Background())
require.NoError(err)
for shuffleIndex := 0; shuffleIndex < 3; shuffleIndex++ {
r.Shuffle(totalState, func(i, j int) {
ops[i], ops[j] = ops[j], ops[i]
})
result, err := db.NewView(context.Background(), ops)
result, err := db.NewViewFromBatchOps(context.Background(), ops, true)
require.NoError(err)
newRoot, err := result.GetMerkleRoot(context.Background())
require.NoError(err)
Expand Down Expand Up @@ -855,7 +855,7 @@ func runRandDBTest(require *require.Assertions, r *rand.Rand, rt randTest) {
for key, value := range values {
ops = append(ops, database.BatchOp{Key: key.Serialize().Value, Value: value})
}
newView, err := dbTrie.NewView(context.Background(), ops)
newView, err := dbTrie.NewViewFromBatchOps(context.Background(), ops, true)
require.NoError(err)

calculatedRoot, err := newView.GetMerkleRoot(context.Background())
Expand Down
27 changes: 21 additions & 6 deletions x/merkledb/mock_db.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion x/merkledb/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -882,5 +882,5 @@ func getStandaloneTrieView(ctx context.Context, ops []database.BatchOp) (*trieVi
return nil, err
}

return db.newUntrackedView(ops)
return db.newUntrackedView(ops, true)
}
Loading