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

merkledb -- dynamic root #2177

Merged
merged 171 commits into from
Dec 14, 2023
Merged
Show file tree
Hide file tree
Changes from 160 commits
Commits
Show all changes
171 commits
Select commit Hold shift + click to select a range
45a8a3b
WIP
Oct 16, 2023
c1c2d71
WIP
Oct 16, 2023
fb832f7
nits
Oct 16, 2023
1866331
WIP
Oct 16, 2023
8616169
add getRootKey
Oct 16, 2023
4f14e23
populate rootPath
Oct 16, 2023
5d9a7c1
WIP update getPathTo
Oct 16, 2023
1067d7c
update insert
Oct 17, 2023
f73f6d2
remove TODOs
Oct 17, 2023
f41a29f
remove EndProof invariant on RangeProof
Oct 17, 2023
fc64ec3
all UT in merkledb pass
Oct 17, 2023
ce62579
Merge branch 'dev' into merkledb-dynamic-root
Oct 17, 2023
a4b76f4
appease linter
Oct 17, 2023
4275b07
handle root deletion
Oct 17, 2023
01b9f4b
handle root deletion pt 2
Oct 17, 2023
6bf8ce2
comment
Oct 17, 2023
5ae830d
WIP
Oct 17, 2023
a64dc47
WIP
Oct 17, 2023
fd8ea46
WIP
Oct 18, 2023
6f9f0cf
WIP
Oct 18, 2023
a6d0549
WIP
Oct 18, 2023
af3f9f7
passing merkledb UT
Oct 18, 2023
98e6028
WIP fix sync tests
Oct 18, 2023
614cd66
merkledb and sync tests pass
Oct 18, 2023
9f975f3
nit
Oct 18, 2023
d3538a8
nit
Oct 18, 2023
e84d83e
comment
Oct 18, 2023
92cd323
comment
Oct 18, 2023
888d9b9
nit
Oct 18, 2023
01668e5
nits
Oct 18, 2023
a6c5e1f
appease linter
Oct 18, 2023
c5e8c4d
Update trie_test.go
dboehm-avalabs Oct 18, 2023
5a56753
codec cleanup
Oct 19, 2023
a9cbbf2
Merge branch 'merkledb-dynamic-root' of github.com:ava-labs/avalanche…
Oct 19, 2023
9d7477d
codec cleanup
Oct 19, 2023
34380f2
appease linter
Oct 19, 2023
82c0061
appease linter
Oct 19, 2023
14def09
remove errant test
Oct 19, 2023
8a41266
make test fuzz test
Oct 19, 2023
41e0d8d
fix mock
Oct 19, 2023
37b12a4
typo
Oct 19, 2023
b75987f
cleanup
Oct 19, 2023
b1e71e1
cleanup
Oct 19, 2023
2f55d7b
nit
Oct 19, 2023
c23286d
typo fix; remove unused code
Oct 19, 2023
04ff880
nits/cleanup
Oct 19, 2023
53d49fe
comment
Oct 19, 2023
ae615c1
revert unneeded change
Oct 19, 2023
71fb895
update getProof
Oct 19, 2023
27c8229
nit
Oct 19, 2023
c5a3c2a
nits
Oct 19, 2023
a0fec24
proof cleanup
Oct 19, 2023
e4ea07d
fix test
Oct 19, 2023
d57eac4
all UT pass
Oct 19, 2023
1adbac1
appease linter
Oct 19, 2023
6db69f3
fix mock list
Oct 20, 2023
47dd688
comment nit
Oct 20, 2023
3b89531
TODO --> Background
Oct 20, 2023
d6c3344
comment
Oct 20, 2023
860bd8a
comment
Oct 20, 2023
40049b5
comment
Oct 20, 2023
3b4b83a
comment
Oct 20, 2023
52c9857
Merge remote-tracking branch 'upstream/dev' into merkledb-dynamic-root
Oct 20, 2023
0f6cc05
fix(?) mock
Oct 20, 2023
3c9fc2b
fix(?) test
Oct 20, 2023
605b294
fix(?) test
Oct 20, 2023
07aee5c
fix(?) test
Oct 20, 2023
ee838f0
fix(?) test
Oct 20, 2023
d9bfef9
fix(?) test
Oct 20, 2023
404132b
nits
Oct 20, 2023
e8b3a9b
fix(?) test
Oct 20, 2023
f669d3d
nit
Oct 20, 2023
e52172d
add branchfactor arg to range proof verify
Oct 20, 2023
96e6cec
refactor
Oct 23, 2023
43318e4
test cleanup
Oct 23, 2023
992f628
ErrEmptyRootID --> ErrEmptyProof
Oct 23, 2023
a7faed4
Merge remote-tracking branch 'upstream/dev' into merkledb-dynamic-root
Oct 24, 2023
11ee267
move ClearRanger interface from database to merkledb package
Oct 24, 2023
19f9690
cleanup
Oct 24, 2023
ae6bd64
nit
Oct 24, 2023
52176b7
add tests
Oct 24, 2023
530b6a5
nit
Oct 24, 2023
6a676c2
comment
Oct 24, 2023
e84d1b2
typo
Oct 24, 2023
bece10c
comment
Oct 24, 2023
d730b53
add test
Oct 24, 2023
c9efc4a
add test
Oct 24, 2023
bb70bca
appease linter
Oct 24, 2023
9fc9045
WIP don't encode node twice
Oct 24, 2023
7f142f8
fix test
Oct 24, 2023
08b27ea
add check for empty proof
Oct 24, 2023
545df4a
appease linter
Oct 24, 2023
74ce294
appease linter
Oct 24, 2023
a140e42
Merge branch 'dev' into merkledb-dynamic-root
Oct 24, 2023
f938ae7
fix test
Oct 24, 2023
ddf27ae
Merge branch 'merkledb-dynamic-root' of github.com:ava-labs/avalanche…
Oct 24, 2023
babe99d
Merge branch 'dev' into merkledb-dynamic-root
Oct 24, 2023
f5c333b
nit change var order
Oct 24, 2023
7e7c7b9
Merge branch 'merkledb-dynamic-root' of github.com:ava-labs/avalanche…
Oct 24, 2023
eb157fd
Merge remote-tracking branch 'upstream/dev' into merkledb-dynamic-root
Nov 1, 2023
525e33d
remove unused params
Nov 2, 2023
bb649ea
remove unused params
Nov 2, 2023
d7548ce
Merge remote-tracking branch 'upstream/dev' into merkledb-dynamic-root
Nov 2, 2023
63dd9b9
ClearRange --> Clear
Nov 2, 2023
7e48fc4
remove ClearRequest proto
Nov 2, 2023
d1e0c5e
appease linter
Nov 2, 2023
a972cad
add newline
Nov 2, 2023
b8ef383
Merge remote-tracking branch 'upstream/dev' into merkledb-dynamic-root
Nov 3, 2023
85f557e
Merge branch 'dev' into merkledb-dynamic-root
Nov 6, 2023
7054ec2
Merge remote-tracking branch 'upstream/dev' into merkledb-dynamic-root
Nov 7, 2023
61502bf
Merge branch 'dev' into merkledb-dynamic-root
Nov 7, 2023
a24650b
Merge remote-tracking branch 'upstream/dev' into merkledb-dynamic-root
Nov 7, 2023
c62eb91
remove unneeded copy
Nov 7, 2023
348e766
don't persist whether the root key has a value
Nov 7, 2023
2f401af
fix test
Nov 7, 2023
72747e1
check for additional bytes at end of decodeKey
Nov 7, 2023
e554992
remove errant comment
Nov 8, 2023
dc78849
Merge remote-tracking branch 'upstream/dev' into merkledb-dynamic-root
Nov 8, 2023
974d8a3
change rootChange from *change to change
Nov 8, 2023
a0473ea
change rootChange from change[*node] to change[maybe.Maybe[*node]]; nits
Nov 8, 2023
41c0b5f
nit
Nov 8, 2023
7ea00cb
nit
Nov 8, 2023
271ef1b
add and implement Clearer interface for merkledb
Nov 8, 2023
ebb4486
nit
Nov 8, 2023
70d88a5
add tests
Nov 8, 2023
0ae7028
add Clearer to sync.DB interface
Nov 8, 2023
f7d401b
fix Clear
Nov 8, 2023
b041f53
WIP update test
Nov 9, 2023
192b2fd
fix test
Nov 9, 2023
83aca7e
fix test
Nov 9, 2023
778e172
remove dummy test
Nov 9, 2023
7ad352e
Merge branch 'dev' into merkledb-clear
Nov 9, 2023
9b7fc3a
refactor clear
Nov 9, 2023
35672c3
comment
Nov 9, 2023
7a351bd
Merge branch 'merkledb-clear' of github.com:ava-labs/avalanchego into…
Nov 9, 2023
20e4a04
clear caches
Nov 9, 2023
14e0e97
appease linter
Nov 9, 2023
9bf30c5
Merge remote-tracking branch 'upstream/dev' into merkledb-clear
Nov 9, 2023
fcf0d22
remove redundant deletions; fix prefix
Nov 9, 2023
8c5d762
Merge remote-tracking branch 'upstream/dev' into merkledb-dynamic-root
Nov 9, 2023
5b8d473
Merge branch 'merkledb-clear' into merkledb-dynamic-root
Nov 9, 2023
bb45d69
Merge branch 'dev' into merkledb-clear
Nov 13, 2023
bcd5e94
Merge remote-tracking branch 'upstream/merkledb-clear' into merkledb-…
Nov 13, 2023
2b45f69
fix test
Nov 13, 2023
c6d3b95
Merge remote-tracking branch 'upstream/dev' into merkledb-dynamic-root
Nov 13, 2023
4650bdd
Merge branch 'dev' into merkledb-dynamic-root
Nov 14, 2023
f6f37ca
Merge branch 'dev' into merkledb-dynamic-root
Nov 16, 2023
dbdf325
Merge branch 'dev' into merkledb-dynamic-root
Nov 16, 2023
8cdcdaf
Merge branch 'dev' into merkledb-dynamic-root
Nov 20, 2023
12a507e
Merge branch 'dev' into merkledb-dynamic-root
Nov 27, 2023
53f2bcb
Merge branch 'dev' into merkledb-dynamic-root
Nov 29, 2023
48c3b9d
WIP merge; 2 UT failing
Nov 30, 2023
da6a670
fix tests
Dec 1, 2023
4c5d79c
Merge branch 'dev' into merkledb-dynamic-root
Dec 1, 2023
f143063
Merge branch 'merkledb-dynamic-root' of github.com:ava-labs/avalanche…
Dec 1, 2023
44e6667
nits
Dec 1, 2023
7b99e6a
var name nit
Dec 1, 2023
09427b4
nit
Dec 1, 2023
3fcf63b
fix insert by calculating root ID of old root
Dec 1, 2023
9583691
Merge branch 'dev' into merkledb-dynamic-root
Dec 4, 2023
8431791
Merge remote-tracking branch 'upstream/dev' into merkledb-dynamic-root
Dec 11, 2023
33c9fd7
special case root in db.getNode
Dec 11, 2023
464602a
remove stalek comments
Dec 11, 2023
d351095
refactor remove
Dec 11, 2023
6207461
Merge branch 'dev' into merkledb-dynamic-root
Dec 12, 2023
8546169
Merge branch 'dev' into merkledb-dynamic-root
Dec 12, 2023
429dbc3
Merge branch 'dev' into merkledb-dynamic-root
Dec 12, 2023
ad2d2f1
Merge branch 'dev' into merkledb-dynamic-root
Dec 13, 2023
7963a0b
Update trieview.go
dboehm-avalabs Dec 13, 2023
f7eb1ec
re-add todo
Dec 13, 2023
afa14f5
Merge branch 'dev' into merkledb-dynamic-root
Dec 13, 2023
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
1 change: 0 additions & 1 deletion scripts/mocks.mockgen.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,4 @@ github.com/ava-labs/avalanchego/vms/registry=VMGetter=vms/registry/mock_vm_gette
github.com/ava-labs/avalanchego/vms/registry=VMRegisterer=vms/registry/mock_vm_registerer.go
github.com/ava-labs/avalanchego/vms/registry=VMRegistry=vms/registry/mock_vm_registry.go
github.com/ava-labs/avalanchego/vms=Factory,Manager=vms/mock_manager.go
github.com/ava-labs/avalanchego/x/merkledb=MerkleDB=x/merkledb/mock_db.go
Copy link
Author

Choose a reason for hiding this comment

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

Auto-generation fails because of generics 😭

github.com/ava-labs/avalanchego/x/sync=Client=x/sync/mock_client.go
32 changes: 26 additions & 6 deletions x/merkledb/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,13 @@ type encoder interface {
// Returns the bytes that will be hashed to generate [n]'s ID.
// Assumes [n] is non-nil.
encodeHashValues(n *node) []byte
encodeKey(key Key) []byte
}

type decoder interface {
// Assumes [n] is non-nil.
decodeDBNode(bytes []byte, n *dbNode) error
decodeKey(bytes []byte) (Key, error)
}

func newCodec() encoderDecoder {
Expand Down Expand Up @@ -98,7 +100,6 @@ func (c *codecImpl) encodeDBNode(n *dbNode) []byte {
estimatedLen = estimatedValueLen + minVarIntLen + estimatedNodeChildLen*numChildren
buf = bytes.NewBuffer(make([]byte, 0, estimatedLen))
)

c.encodeMaybeByteSlice(buf, n.value)
c.encodeUint(buf, uint64(numChildren))
// Note we insert children in order of increasing index
Expand All @@ -108,7 +109,7 @@ func (c *codecImpl) encodeDBNode(n *dbNode) []byte {
for _, index := range keys {
entry := n.children[index]
c.encodeUint(buf, uint64(index))
c.encodeKey(buf, entry.compressedKey)
c.encodeKeyToBuffer(buf, entry.compressedKey)
_, _ = buf.Write(entry.id[:])
c.encodeBool(buf, entry.hasValue)
}
Expand All @@ -134,7 +135,7 @@ func (c *codecImpl) encodeHashValues(n *node) []byte {
_, _ = buf.Write(entry.id[:])
}
c.encodeMaybeByteSlice(buf, n.valueDigest)
c.encodeKey(buf, n.key)
c.encodeKeyToBuffer(buf, n.key)

return buf.Bytes()
}
Expand Down Expand Up @@ -172,7 +173,7 @@ func (c *codecImpl) decodeDBNode(b []byte, n *dbNode) error {
}
previousChild = index

compressedKey, err := c.decodeKey(src)
compressedKey, err := c.decodeKeyFromReader(src)
if err != nil {
return err
}
Expand Down Expand Up @@ -330,12 +331,31 @@ func (*codecImpl) decodeID(src *bytes.Reader) (ids.ID, error) {
return id, err
}

func (c *codecImpl) encodeKey(dst *bytes.Buffer, key Key) {
func (c *codecImpl) encodeKey(key Key) []byte {
estimatedLen := binary.MaxVarintLen64 + len(key.Bytes())
dst := bytes.NewBuffer(make([]byte, 0, estimatedLen))
c.encodeKeyToBuffer(dst, key)
return dst.Bytes()
}

func (c *codecImpl) encodeKeyToBuffer(dst *bytes.Buffer, key Key) {
c.encodeUint(dst, uint64(key.length))
_, _ = dst.Write(key.Bytes())
}

func (c *codecImpl) decodeKey(src *bytes.Reader) (Key, error) {
func (c *codecImpl) decodeKey(b []byte) (Key, error) {
src := bytes.NewReader(b)
key, err := c.decodeKeyFromReader(src)
if err != nil {
return Key{}, err
}
if src.Len() != 0 {
return Key{}, errExtraSpace
}
return key, err
}

func (c *codecImpl) decodeKeyFromReader(src *bytes.Reader) (Key, error) {
if minKeyLen > src.Len() {
return Key{}, io.ErrUnexpectedEOF
}
Expand Down
16 changes: 4 additions & 12 deletions x/merkledb/codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,14 @@ func FuzzCodecKey(f *testing.F) {
) {
require := require.New(t)
codec := codec.(*codecImpl)
reader := bytes.NewReader(b)
startLen := reader.Len()
got, err := codec.decodeKey(reader)
got, err := codec.decodeKey(b)
if err != nil {
t.SkipNow()
}
endLen := reader.Len()
numRead := startLen - endLen

// Encoding [got] should be the same as [b].
var buf bytes.Buffer
codec.encodeKey(&buf, got)
bufBytes := buf.Bytes()
require.Len(bufBytes, numRead)
require.Equal(b[:numRead], bufBytes)
gotBytes := codec.encodeKey(got)
require.Equal(b, gotBytes)
},
)
}
Expand Down Expand Up @@ -248,7 +241,6 @@ func FuzzEncodeHashValues(f *testing.F) {

func TestCodecDecodeKeyLengthOverflowRegression(t *testing.T) {
codec := codec.(*codecImpl)
bytes := bytes.NewReader(binary.AppendUvarint(nil, math.MaxInt))
_, err := codec.decodeKey(bytes)
_, err := codec.decodeKey(binary.AppendUvarint(nil, math.MaxInt))
require.ErrorIs(t, err, io.ErrUnexpectedEOF)
}
128 changes: 71 additions & 57 deletions x/merkledb/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@ var (
intermediateNodePrefix = []byte{2}

cleanShutdownKey = []byte(string(metadataPrefix) + "cleanShutdown")
rootDBKey = []byte(string(metadataPrefix) + "root")
Copy link
Contributor

Choose a reason for hiding this comment

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

realized belatedly that you don't actually need this since the root should always be the smallest key present in your nodes db. You should be able to just grab the first key on the iterator, right?

Copy link
Author

Choose a reason for hiding this comment

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

We could do that, but I feel like this is a bit cleaner than having to check both the value and intermediate node databases

Copy link
Contributor

@dboehm-avalabs dboehm-avalabs Dec 13, 2023

Choose a reason for hiding this comment

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

The current code still checks both dbs

hadCleanShutdown = []byte{1}
didNotHaveCleanShutdown = []byte{0}

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

type ChangeProofer interface {
Expand All @@ -64,6 +64,9 @@ type ChangeProofer interface {
// Returns at most [maxLength] key/value pairs.
// Returns [ErrInsufficientHistory] if this node has insufficient history
// to generate the proof.
// Returns ErrEmptyProof if [endRootID] is ids.Empty.
// Note that [endRootID] == ids.Empty means the trie is empty
// (i.e. we don't need a change proof.)
GetChangeProof(
ctx context.Context,
startRootID ids.ID,
Expand Down Expand Up @@ -100,6 +103,9 @@ type RangeProofer interface {
// [start, end] when the root of the trie was [rootID].
// If [start] is Nothing, there's no lower bound on the range.
// If [end] is Nothing, there's no upper bound on the range.
// Returns ErrEmptyProof if [rootID] is ids.Empty.
// Note that [rootID] == ids.Empty means the trie is empty
// (i.e. we don't need a range proof.)
GetRangeProofAtRoot(
ctx context.Context,
rootID ids.ID,
Expand Down Expand Up @@ -201,11 +207,11 @@ type merkleDB struct {
debugTracer trace.Tracer
infoTracer trace.Tracer

// The sentinel node of this trie.
// It is the node with a nil key and is the ancestor of all nodes in the trie.
// If it has a value or has multiple children, it is also the root of the trie.
sentinelNode *node
rootID ids.ID
// The root of this trie.
// Nothing if the trie is empty.
root maybe.Maybe[*node]

rootID ids.ID

// Valid children of this trie.
childViews []*trieView
Expand Down Expand Up @@ -268,6 +274,9 @@ func newDatabase(
// add current root to history (has no changes)
trieDB.history.record(&changeSummary{
rootID: trieDB.rootID,
rootChange: change[maybe.Maybe[*node]]{
after: trieDB.root,
},
values: map[Key]*change[maybe.Maybe[[]byte]]{},
nodes: map[Key]*change[*node]{},
})
Expand Down Expand Up @@ -295,7 +304,8 @@ func newDatabase(
// Deletes every intermediate node and rebuilds them by re-adding every key/value.
// TODO: make this more efficient by only clearing out the stale portions of the trie.
func (db *merkleDB) rebuild(ctx context.Context, cacheSize int) error {
db.sentinelNode = newNode(Key{})
db.root = maybe.Nothing[*node]()
db.rootID = ids.Empty

// Delete intermediate nodes.
if err := database.ClearPrefix(db.baseDB, intermediateNodePrefix, rebuildIntermediateDeletionWriteSize); err != nil {
Expand Down Expand Up @@ -581,13 +591,6 @@ func (db *merkleDB) getMerkleRoot() ids.ID {
return db.rootID
}

// isSentinelNodeTheRoot returns true if the passed in sentinel node has a value and or multiple child nodes
// When this is true, the root of the trie is the sentinel node
// When this is false, the root of the trie is the sentinel node's single child
func isSentinelNodeTheRoot(sentinel *node) bool {
return sentinel.valueDigest.HasValue() || len(sentinel.children) != 1
}

func (db *merkleDB) GetProof(ctx context.Context, key []byte) (*Proof, error) {
db.commitLock.RLock()
defer db.commitLock.RUnlock()
Expand Down Expand Up @@ -644,11 +647,13 @@ func (db *merkleDB) getRangeProofAtRoot(
end maybe.Maybe[[]byte],
maxLength int,
) (*RangeProof, error) {
if db.closed {
switch {
case db.closed:
return nil, database.ErrClosed
}
if maxLength <= 0 {
case maxLength <= 0:
return nil, fmt.Errorf("%w but was %d", ErrInvalidMaxLength, maxLength)
case rootID == ids.Empty:
return nil, ErrEmptyProof
}

historicalView, err := db.getHistoricalViewForRange(rootID, start, end)
Expand All @@ -666,11 +671,13 @@ func (db *merkleDB) GetChangeProof(
end maybe.Maybe[[]byte],
maxLength int,
) (*ChangeProof, error) {
if start.HasValue() && end.HasValue() && bytes.Compare(start.Value(), end.Value()) == 1 {
switch {
case start.HasValue() && end.HasValue() && bytes.Compare(start.Value(), end.Value()) == 1:
return nil, ErrStartAfterEnd
}
if startRootID == endRootID {
case startRootID == endRootID:
return nil, errSameRoot
case endRootID == ids.Empty:
return nil, ErrEmptyProof
}

db.commitLock.RLock()
Expand Down Expand Up @@ -931,13 +938,7 @@ func (db *merkleDB) commitChanges(ctx context.Context, trieToCommit *trieView) e
return nil
}

sentinelChange, ok := changes.nodes[Key{}]
if !ok {
return errNoNewSentinel
}

currentValueNodeBatch := db.valueNodeDB.NewBatch()

_, nodesSpan := db.infoTracer.Start(ctx, "MerkleDB.commitChanges.writeNodes")
for key, nodeChange := range changes.nodes {
shouldAddIntermediate := nodeChange.after != nil && !nodeChange.after.hasValue()
Expand Down Expand Up @@ -973,12 +974,18 @@ func (db *merkleDB) commitChanges(ctx context.Context, trieToCommit *trieView) e
return err
}

// Only modify in-memory state after the commit succeeds
// so that we don't need to clean up on error.
db.sentinelNode = sentinelChange.after
db.rootID = changes.rootID
db.history.record(changes)
return nil

// Update root in database.
db.root = changes.rootChange.after
db.rootID = changes.rootID

if db.root.IsNothing() {
return db.baseDB.Delete(rootDBKey)
}

rootKey := codec.encodeKey(db.root.Value().key)
return db.baseDB.Put(rootDBKey, rootKey)
}

// moveChildViewsToDB removes any child views from the trieToCommit and moves them to the db
Expand Down Expand Up @@ -1014,7 +1021,7 @@ func (db *merkleDB) VerifyChangeProof(
case start.HasValue() && end.HasValue() && bytes.Compare(start.Value(), end.Value()) > 0:
return ErrStartAfterEnd
case proof.Empty():
return ErrNoMerkleProof
return ErrEmptyProof
case end.HasValue() && len(proof.KeyChanges) == 0 && len(proof.EndProof) == 0:
// We requested an end proof but didn't get one.
return ErrNoEndProof
Expand Down Expand Up @@ -1156,37 +1163,41 @@ func (db *merkleDB) invalidateChildrenExcept(exception *trieView) {
}
}

// If the root is on disk, set [db.root] to it.
// Otherwise leave [db.root] as Nothing.
func (db *merkleDB) initializeRoot() error {
// Not sure if the sentinel node exists or if it had a value,
// so check under both prefixes
var err error
db.sentinelNode, err = db.intermediateNodeDB.Get(Key{})
rootKeyBytes, err := db.baseDB.Get(rootDBKey)
if err != nil {
if !errors.Is(err, database.ErrNotFound) {
return err
}
// Root isn't on disk.
return nil
}

if errors.Is(err, database.ErrNotFound) {
// Didn't find the sentinel in the intermediateNodeDB, check the valueNodeDB
db.sentinelNode, err = db.valueNodeDB.Get(Key{})
// Root is on disk.
rootKey, err := codec.decodeKey(rootKeyBytes)
if err != nil {
return err
}

// First, see if root is an intermediate node.
var root *node
root, err = db.getEditableNode(rootKey, false /* hasValue */)
if err != nil {
if !errors.Is(err, database.ErrNotFound) {
return err
}

// Sentinel node doesn't exist in either database prefix.
// Make a new one and store it in the intermediateNodeDB
db.sentinelNode = newNode(Key{})
if err := db.intermediateNodeDB.Put(Key{}, db.sentinelNode); err != nil {
// The root must be a value node.
root, err = db.getEditableNode(rootKey, true /* hasValue */)
if err != nil {
return err
}
}

db.rootID = db.sentinelNode.calculateID(db.metrics)
if !isSentinelNodeTheRoot(db.sentinelNode) {
// If the sentinel node is not the root, the trie's root is the sentinel node's only child
for _, childEntry := range db.sentinelNode.children {
db.rootID = childEntry.id
}
}
db.rootID = root.calculateID(db.metrics)
db.root = maybe.Some(root)
return nil
}

Expand Down Expand Up @@ -1263,12 +1274,15 @@ func (db *merkleDB) getNode(key Key, hasValue bool) (*node, error) {
switch {
case db.closed:
return nil, database.ErrClosed
case key == Key{}:
danlaine marked this conversation as resolved.
Show resolved Hide resolved
return db.sentinelNode, nil
case hasValue:
return db.valueNodeDB.Get(key)
default:
return db.intermediateNodeDB.Get(key)
}
return db.intermediateNodeDB.Get(key)
}

func (db *merkleDB) getRoot() maybe.Maybe[*node] {
return db.root
}

func (db *merkleDB) Clear() error {
danlaine marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -1287,13 +1301,13 @@ func (db *merkleDB) Clear() error {
}

// Clear root
db.sentinelNode = newNode(Key{})
db.rootID = db.sentinelNode.calculateID(db.metrics)
db.root = maybe.Nothing[*node]()
db.rootID = ids.Empty

// Clear history
db.history = newTrieHistory(db.history.maxHistoryLen)
db.history.record(&changeSummary{
rootID: db.getMerkleRoot(),
rootID: db.rootID,
values: map[Key]*change[maybe.Maybe[[]byte]]{},
nodes: map[Key]*change[*node]{},
})
Expand Down
Loading
Loading