Skip to content

Commit 2806af7

Browse files
Simplify ChangeProofs to have a change list instead of puts and deletes lists (#1385)
1 parent 90a1238 commit 2806af7

File tree

11 files changed

+159
-305
lines changed

11 files changed

+159
-305
lines changed

x/merkledb/codec.go

Lines changed: 40 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ const (
2626
minCodecVersionLen = minVarIntLen
2727
minSerializedPathLen = minVarIntLen
2828
minByteSliceLen = minVarIntLen
29-
minDeletedKeyLen = minByteSliceLen
3029
minMaybeByteSliceLen = boolLen
3130
minProofPathLen = minVarIntLen
3231
minKeyValueLen = 2 * minByteSliceLen
32+
minKeyChangeLen = minByteSliceLen + minMaybeByteSliceLen
3333
minProofNodeLen = minSerializedPathLen + minMaybeByteSliceLen + minVarIntLen
3434
minProofLen = minCodecVersionLen + minProofPathLen + minByteSliceLen
35-
minChangeProofLen = minCodecVersionLen + +boolLen + 2*minProofPathLen + 2*minVarIntLen
36-
minRangeProofLen = minCodecVersionLen + +2*minProofPathLen + minVarIntLen
35+
minChangeProofLen = minCodecVersionLen + boolLen + 2*minProofPathLen + minVarIntLen
36+
minRangeProofLen = minCodecVersionLen + 2*minProofPathLen + minVarIntLen
3737
minDBNodeLen = minCodecVersionLen + minMaybeByteSliceLen + minVarIntLen
3838
minHashValuesLen = minCodecVersionLen + minVarIntLen + minMaybeByteSliceLen + minSerializedPathLen
3939
minProofNodeChildLen = minVarIntLen + idLen
@@ -150,20 +150,11 @@ func (c *codecImpl) EncodeChangeProof(version uint16, proof *ChangeProof) ([]byt
150150
if err := c.encodeProofPath(buf, proof.EndProof); err != nil {
151151
return nil, err
152152
}
153-
if err := c.encodeInt(buf, len(proof.KeyValues)); err != nil {
154-
return nil, err
155-
}
156-
for _, kv := range proof.KeyValues {
157-
if err := c.encodeKeyValue(kv, buf); err != nil {
158-
return nil, err
159-
}
160-
}
161-
162-
if err := c.encodeInt(buf, len(proof.DeletedKeys)); err != nil {
153+
if err := c.encodeInt(buf, len(proof.KeyChanges)); err != nil {
163154
return nil, err
164155
}
165-
for _, key := range proof.DeletedKeys {
166-
if err := c.encodeByteSlice(buf, key); err != nil {
156+
for _, kv := range proof.KeyChanges {
157+
if err := c.encodeKeyChange(kv, buf); err != nil {
167158
return nil, err
168159
}
169160
}
@@ -343,36 +334,19 @@ func (c *codecImpl) DecodeChangeProof(b []byte, proof *ChangeProof) (uint16, err
343334
return 0, err
344335
}
345336

346-
numKeyValues, err := c.decodeInt(src)
337+
numKeyChanges, err := c.decodeInt(src)
347338
if err != nil {
348339
return 0, err
349340
}
350-
if numKeyValues < 0 {
341+
if numKeyChanges < 0 {
351342
return 0, errNegativeNumKeyValues
352343
}
353-
if numKeyValues > src.Len()/minKeyValueLen {
344+
if numKeyChanges > src.Len()/minKeyChangeLen {
354345
return 0, io.ErrUnexpectedEOF
355346
}
356-
proof.KeyValues = make([]KeyValue, numKeyValues)
357-
for i := range proof.KeyValues {
358-
if proof.KeyValues[i], err = c.decodeKeyValue(src); err != nil {
359-
return 0, err
360-
}
361-
}
362-
363-
numDeletedKeys, err := c.decodeInt(src)
364-
if err != nil {
365-
return 0, err
366-
}
367-
if numDeletedKeys < 0 {
368-
return 0, errNegativeNumKeyValues
369-
}
370-
if numDeletedKeys > src.Len()/minDeletedKeyLen {
371-
return 0, io.ErrUnexpectedEOF
372-
}
373-
proof.DeletedKeys = make([][]byte, numDeletedKeys)
374-
for i := range proof.DeletedKeys {
375-
if proof.DeletedKeys[i], err = c.decodeByteSlice(src); err != nil {
347+
proof.KeyChanges = make([]KeyChange, numKeyChanges)
348+
for i := range proof.KeyChanges {
349+
if proof.KeyChanges[i], err = c.decodeKeyChange(src); err != nil {
376350
return 0, err
377351
}
378352
}
@@ -499,6 +473,24 @@ func (c *codecImpl) decodeDBNode(b []byte, n *dbNode) (uint16, error) {
499473
return codecVersion, err
500474
}
501475

476+
func (c *codecImpl) decodeKeyChange(src *bytes.Reader) (KeyChange, error) {
477+
if minKeyChangeLen > src.Len() {
478+
return KeyChange{}, io.ErrUnexpectedEOF
479+
}
480+
481+
var (
482+
result KeyChange
483+
err error
484+
)
485+
if result.Key, err = c.decodeByteSlice(src); err != nil {
486+
return result, err
487+
}
488+
if result.Value, err = c.decodeMaybeByteSlice(src); err != nil {
489+
return result, err
490+
}
491+
return result, nil
492+
}
493+
502494
func (c *codecImpl) decodeKeyValue(src *bytes.Reader) (KeyValue, error) {
503495
if minKeyValueLen > src.Len() {
504496
return KeyValue{}, io.ErrUnexpectedEOF
@@ -517,6 +509,16 @@ func (c *codecImpl) decodeKeyValue(src *bytes.Reader) (KeyValue, error) {
517509
return result, nil
518510
}
519511

512+
func (c *codecImpl) encodeKeyChange(kv KeyChange, dst io.Writer) error {
513+
if err := c.encodeByteSlice(dst, kv.Key); err != nil {
514+
return err
515+
}
516+
if err := c.encodeMaybeByteSlice(dst, kv.Value); err != nil {
517+
return err
518+
}
519+
return nil
520+
}
521+
520522
func (c *codecImpl) encodeKeyValue(kv KeyValue, dst io.Writer) error {
521523
if err := c.encodeByteSlice(dst, kv.Key); err != nil {
522524
return err

x/merkledb/codec_test.go

Lines changed: 20 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,22 @@ func newRandomProofNode(r *rand.Rand) ProofNode {
5252
}
5353
}
5454

55-
func newKeyValues(r *rand.Rand, num uint) []KeyValue {
56-
keyValues := make([]KeyValue, num)
57-
for i := range keyValues {
55+
func newKeyChanges(r *rand.Rand, num uint) []KeyChange {
56+
keyChanges := make([]KeyChange, num)
57+
for i := range keyChanges {
5858
key := make([]byte, r.Intn(32)) // #nosec G404
5959
_, _ = r.Read(key) // #nosec G404
6060
val := make([]byte, r.Intn(32)) // #nosec G404
6161
_, _ = r.Read(val) // #nosec G404
62-
keyValues[i] = KeyValue{
62+
if len(val) == 0 {
63+
val = nil
64+
}
65+
keyChanges[i] = KeyChange{
6366
Key: key,
64-
Value: val,
67+
Value: Some(val),
6568
}
6669
}
67-
return keyValues
70+
return keyChanges
6871
}
6972

7073
func nilEmptySlices(dest interface{}) {
@@ -384,6 +387,7 @@ func FuzzCodecChangeProofDeterministic(f *testing.F) {
384387
randSeed int,
385388
hadRootsInHistory bool,
386389
numProofNodes uint,
390+
numChangedKeys uint,
387391
numDeletedKeys uint,
388392
) {
389393
require := require.New(t)
@@ -397,18 +401,18 @@ func FuzzCodecChangeProofDeterministic(f *testing.F) {
397401
endProofNodes[i] = newRandomProofNode(r)
398402
}
399403

400-
deletedKeys := make([][]byte, numDeletedKeys)
401-
for i := range deletedKeys {
402-
deletedKeys[i] = make([]byte, r.Intn(32)) // #nosec G404
403-
_, _ = r.Read(deletedKeys[i]) // #nosec G404
404+
keyChanges := newKeyChanges(r, numChangedKeys)
405+
for i := uint(0); i < numDeletedKeys; i++ {
406+
keyToDelete := make([]byte, r.Intn(32)) // #nosec G404
407+
_, _ = r.Read(keyToDelete) // #nosec G404
408+
keyChanges = append(keyChanges, KeyChange{Key: keyToDelete})
404409
}
405410

406411
proof := ChangeProof{
407412
HadRootsInHistory: hadRootsInHistory,
408413
StartProof: startProofNodes,
409414
EndProof: endProofNodes,
410-
KeyValues: newKeyValues(r, numProofNodes),
411-
DeletedKeys: deletedKeys,
415+
KeyChanges: keyChanges,
412416
}
413417

414418
proofBytes, err := Codec.EncodeChangeProof(Version, &proof)
@@ -586,36 +590,19 @@ func TestCodec_DecodeChangeProof(t *testing.T) {
586590
HadRootsInHistory: true,
587591
StartProof: nil,
588592
EndProof: nil,
589-
KeyValues: nil,
590-
DeletedKeys: nil,
593+
KeyChanges: nil,
591594
}
592595

593596
proofBytes, err := Codec.EncodeChangeProof(Version, &proof)
594597
require.NoError(err)
595598

596-
// Remove key-values length and deleted keys length (both 0) from end
597-
proofBytes = proofBytes[:len(proofBytes)-2*minVarIntLen]
599+
// Remove key-values length (0) from end
600+
proofBytes = proofBytes[:len(proofBytes)-minVarIntLen]
598601

599-
// Put key-values length of -1 and deleted keys length of 0
602+
// Put key-values length of -1
600603
proofBytesBuf := bytes.NewBuffer(proofBytes)
601604
err = Codec.(*codecImpl).encodeInt(proofBytesBuf, -1)
602605
require.NoError(err)
603-
err = Codec.(*codecImpl).encodeInt(proofBytesBuf, 0)
604-
require.NoError(err)
605-
606-
_, err = Codec.DecodeChangeProof(proofBytesBuf.Bytes(), &parsedProof)
607-
require.ErrorIs(err, errNegativeNumKeyValues)
608-
609-
proofBytes = proofBytesBuf.Bytes()
610-
proofBytes = proofBytes[:len(proofBytes)-2*minVarIntLen]
611-
proofBytesBuf = bytes.NewBuffer(proofBytes)
612-
613-
// Remove key-values length and deleted keys length from end
614-
// Put key-values length of 0 and deleted keys length of -1
615-
err = Codec.(*codecImpl).encodeInt(proofBytesBuf, 0)
616-
require.NoError(err)
617-
err = Codec.(*codecImpl).encodeInt(proofBytesBuf, -1)
618-
require.NoError(err)
619606

620607
_, err = Codec.DecodeChangeProof(proofBytesBuf.Bytes(), &parsedProof)
621608
require.ErrorIs(err, errNegativeNumKeyValues)

x/merkledb/db.go

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -516,24 +516,23 @@ func (db *Database) GetChangeProof(
516516
})
517517

518518
// TODO: sync.pool these buffers
519-
result.KeyValues = make([]KeyValue, 0, len(changedKeys))
520-
result.DeletedKeys = make([][]byte, 0, len(changedKeys))
519+
result.KeyChanges = make([]KeyChange, 0, len(changedKeys))
521520

522521
for _, key := range changedKeys {
523522
change := changes.values[key]
524523
serializedKey := key.Serialize().Value
525524

526-
if change.after.IsNothing() {
527-
result.DeletedKeys = append(result.DeletedKeys, serializedKey)
528-
} else {
529-
result.KeyValues = append(result.KeyValues, KeyValue{
530-
Key: serializedKey,
531-
// create a copy so edits of the []byte don't affect the db
532-
Value: slices.Clone(change.after.value),
533-
})
534-
}
525+
result.KeyChanges = append(result.KeyChanges, KeyChange{
526+
Key: serializedKey,
527+
// create a copy so edits of the []byte don't affect the db
528+
Value: Clone(change.after),
529+
})
530+
}
531+
532+
largestKey := end
533+
if len(result.KeyChanges) > 0 {
534+
largestKey = result.KeyChanges[len(result.KeyChanges)-1].Key
535535
}
536-
largestKey := result.getLargestKey(end)
537536

538537
// Since we hold [db.commitlock] we must still have sufficient
539538
// history to recreate the trie at [endRootID].
@@ -1121,20 +1120,18 @@ func (db *Database) prepareBatchView(ops []database.BatchOp) (*trieView, error)
11211120
// inserted and the key/value pairs in [proof.DeletedKeys] removed.
11221121
// Assumes [db.commitLock] is locked.
11231122
func (db *Database) prepareChangeProofView(proof *ChangeProof) (*trieView, error) {
1124-
view, err := db.newUntrackedView(len(proof.KeyValues))
1123+
view, err := db.newUntrackedView(len(proof.KeyChanges))
11251124
if err != nil {
11261125
return nil, err
11271126
}
11281127
// Don't need to lock [view] because nobody else has a reference to it.
11291128

1130-
for _, kv := range proof.KeyValues {
1131-
if err := view.insert(kv.Key, kv.Value); err != nil {
1132-
return nil, err
1133-
}
1134-
}
1135-
1136-
for _, keyToDelete := range proof.DeletedKeys {
1137-
if err := view.remove(keyToDelete); err != nil {
1129+
for _, kv := range proof.KeyChanges {
1130+
if kv.Value.IsNothing() {
1131+
if err := view.remove(kv.Key); err != nil {
1132+
return nil, err
1133+
}
1134+
} else if err := view.insert(kv.Key, kv.Value.value); err != nil {
11381135
return nil, err
11391136
}
11401137
}

x/merkledb/db_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,7 @@ func runRandDBTest(require *require.Assertions, r *rand.Rand, rt randTest) {
820820
root,
821821
)
822822
require.NoError(err)
823-
require.LessOrEqual(len(changeProof.KeyValues)+len(changeProof.DeletedKeys), 100)
823+
require.LessOrEqual(len(changeProof.KeyChanges), 100)
824824
case opWriteBatch:
825825
oldRoot, err := db.GetMerkleRoot(context.Background())
826826
require.NoError(err)

0 commit comments

Comments
 (0)