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

x/sync -- use for sending Range Proofs #1537

Merged
merged 58 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
d6426bf
Update sync.proto
dboehm-avalabs May 3, 2023
51e75d8
Update sync.proto
dboehm-avalabs May 3, 2023
dd74d3b
Update sync.proto
dboehm-avalabs May 3, 2023
b3b68ff
sync.proto recommended changes
rkuris May 3, 2023
f1eb6de
Review comments
rkuris May 4, 2023
6cce0d3
rename KeyProof to ProofNode; rename ProofNode.value_hash to ProofNod…
May 18, 2023
ec51aeb
actually add SerializedPath message
May 18, 2023
63386d4
int --> uint32 in proto
May 19, 2023
e108976
regenerate .pb.go files
May 19, 2023
7845dfd
Merge remote-tracking branch 'upstream/dev' into MerkleSyncProtos
May 19, 2023
d04c7a1
Merge branch 'MerkleSyncProtos' into MerkleSyncProtos-use-proto
May 19, 2023
dfe894d
add ToProto for ProofNode and RangeProof; send range proof repsonse a…
May 19, 2023
5af51ac
add FromProto for ProofNode; add tests
May 19, 2023
42422d1
add tests; add proto MaybeBytes type
May 19, 2023
35267b5
add proto MaybeBytes type
May 22, 2023
0d08f96
Merge branch 'MerkleSyncProtos' into MerkleSyncProtos-use-proto
May 22, 2023
ef94cf1
add to test
May 22, 2023
860ee03
disallow maybe with nothing and value
May 22, 2023
b3d3387
add comment
May 22, 2023
70e4ab4
add more proofNode validity checks
May 22, 2023
c0b2bdc
remove EncodeRangeProof and DecodeRangeProof
May 22, 2023
6dae5b5
Merge remote-tracking branch 'upstream/dev' into MerkleSyncProtos
May 22, 2023
e0f48b7
remove unused functions
May 22, 2023
7c1eb7c
import nits
May 22, 2023
e493c27
Merge branch 'MerkleSyncProtos' into MerkleSyncProtos-use-proto
May 22, 2023
d33831f
make value in MaybeBytes non-optional; make value in KeyChange a Mayb…
May 22, 2023
3d6b53a
Merge branch 'MerkleSyncProtos' into MerkleSyncProtos-use-proto
May 22, 2023
a47ad58
regenerate .pb.go files
May 22, 2023
2859a54
add had_roots_in_history to ChangeProof
May 22, 2023
1115354
Merge branch 'MerkleSyncProtos-use-proto' of github.com:ava-labs/aval…
May 22, 2023
d0cde74
Merge branch 'MerkleSyncProtos' into MerkleSyncProtos-use-proto
May 22, 2023
d6daa7b
fix proto ordering
May 22, 2023
4d47288
Merge branch 'MerkleSyncProtos' into MerkleSyncProtos-use-proto
May 22, 2023
35fbcec
update .pb.go files
May 22, 2023
de32cda
comment nit
May 22, 2023
86a1b63
Merge branch 'MerkleSyncProtos' into MerkleSyncProtos-use-proto
May 22, 2023
73bcfd0
/* --> // for proto comments
May 23, 2023
7b434ea
Merge remote-tracking branch 'upstream/dev' into MerkleSyncProtos
May 23, 2023
4dffa64
Merge branch 'MerkleSyncProtos' into MerkleSyncProtos-use-proto
May 23, 2023
72b748c
/* --> // for proto comments
May 23, 2023
bb98fc5
Merge branch 'MerkleSyncProtos' into MerkleSyncProtos-use-proto
May 23, 2023
f7d39a7
nit
May 23, 2023
0524de5
Merge branch 'dev' into MerkleSyncProtos
May 24, 2023
0050f21
Merge remote-tracking branch 'upstream/dev' into MerkleSyncProtos
May 24, 2023
c99236d
Merge branch 'MerkleSyncProtos' of github.com:ava-labs/avalanchego in…
May 24, 2023
61f4c27
Merge branch 'MerkleSyncProtos' into MerkleSyncProtos-use-proto
May 24, 2023
cad3a80
rename RangeProofResponse --> RangeProof
May 24, 2023
52af5a6
Merge branch 'MerkleSyncProtos' into MerkleSyncProtos-use-proto
May 24, 2023
4417a28
update .pb.go files
May 24, 2023
f0c7833
Merge remote-tracking branch 'upstream/dev' into MerkleSyncProtos-use…
May 25, 2023
c2d6692
add nil checks in ProofNode.UnmarshalProto
May 25, 2023
78da8e0
Merge remote-tracking branch 'upstream/dev' into MerkleSyncProtos-use…
May 25, 2023
be0f6bd
add nil checks in RangeProof.UnmarshalProto
May 25, 2023
d05f98c
Merge remote-tracking branch 'upstream/dev' into MerkleSyncProtos-use…
May 30, 2023
dfea9e5
change nibble length field from uint32 to uint64
May 30, 2023
cea0f50
import nit
May 30, 2023
9022216
appease linter
May 30, 2023
b7263eb
Merge branch 'dev' into MerkleSyncProtos-use-proto
May 31, 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
7 changes: 5 additions & 2 deletions utils/hashing/hashing.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package hashing

import (
"crypto/sha256"
"errors"
"fmt"
"io"

Expand All @@ -16,6 +17,8 @@ const (
AddrLen = ripemd160.Size
)

var ErrInvalidHashLen = errors.New("invalid hash length")
Copy link
Author

Choose a reason for hiding this comment

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

added so that we can assert this error in a sync test


// Hash256 A 256 bit long hash value.
type Hash256 = [HashLen]byte

Expand Down Expand Up @@ -85,7 +88,7 @@ func Checksum(bytes []byte, length int) []byte {
func ToHash256(bytes []byte) (Hash256, error) {
hash := Hash256{}
if bytesLen := len(bytes); bytesLen != HashLen {
return hash, fmt.Errorf("expected 32 bytes but got %d", bytesLen)
return hash, fmt.Errorf("%w: expected 32 bytes but got %d", ErrInvalidHashLen, bytesLen)
}
copy(hash[:], bytes)
return hash, nil
Expand All @@ -94,7 +97,7 @@ func ToHash256(bytes []byte) (Hash256, error) {
func ToHash160(bytes []byte) (Hash160, error) {
hash := Hash160{}
if bytesLen := len(bytes); bytesLen != ripemd160.Size {
return hash, fmt.Errorf("expected 20 bytes but got %d", bytesLen)
return hash, fmt.Errorf("%w: expected 20 bytes but got %d", ErrInvalidHashLen, bytesLen)
}
copy(hash[:], bytes)
return hash, nil
Expand Down
109 changes: 0 additions & 109 deletions x/merkledb/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ type EncoderDecoder interface {
type Encoder interface {
EncodeProof(version uint16, p *Proof) ([]byte, error)
EncodeChangeProof(version uint16, p *ChangeProof) ([]byte, error)
EncodeRangeProof(version uint16, p *RangeProof) ([]byte, error)

encodeDBNode(version uint16, n *dbNode) ([]byte, error)
encodeHashValues(version uint16, hv *hashValues) ([]byte, error)
Expand All @@ -83,7 +82,6 @@ type Encoder interface {
type Decoder interface {
DecodeProof(bytes []byte, p *Proof) (uint16, error)
DecodeChangeProof(bytes []byte, p *ChangeProof) (uint16, error)
DecodeRangeProof(bytes []byte, p *RangeProof) (uint16, error)

decodeDBNode(bytes []byte, n *dbNode) (uint16, error)
}
Expand Down Expand Up @@ -161,37 +159,6 @@ func (c *codecImpl) EncodeChangeProof(version uint16, proof *ChangeProof) ([]byt
return buf.Bytes(), nil
}

func (c *codecImpl) EncodeRangeProof(version uint16, proof *RangeProof) ([]byte, error) {
if proof == nil {
return nil, errEncodeNil
}

if version != codecVersion {
return nil, fmt.Errorf("%w: %d", errUnknownVersion, version)
}

buf := &bytes.Buffer{}
if err := c.encodeInt(buf, int(version)); err != nil {
return nil, err
}
if err := c.encodeProofPath(buf, proof.StartProof); err != nil {
return nil, err
}
if err := c.encodeProofPath(buf, proof.EndProof); err != nil {
return nil, err
}
if err := c.encodeInt(buf, len(proof.KeyValues)); err != nil {
return nil, err
}
for _, kv := range proof.KeyValues {
if err := c.encodeKeyValue(kv, buf); err != nil {
return nil, err
}
}

return buf.Bytes(), nil
}

func (c *codecImpl) encodeDBNode(version uint16, n *dbNode) ([]byte, error) {
if n == nil {
return nil, errEncodeNil
Expand Down Expand Up @@ -356,54 +323,6 @@ func (c *codecImpl) DecodeChangeProof(b []byte, proof *ChangeProof) (uint16, err
return codecVersion, nil
}

func (c *codecImpl) DecodeRangeProof(b []byte, proof *RangeProof) (uint16, error) {
if proof == nil {
return 0, errDecodeNil
}
if minRangeProofLen > len(b) {
return 0, io.ErrUnexpectedEOF
}

var (
src = bytes.NewReader(b)
err error
)
gotCodecVersion, err := c.decodeInt(src)
if err != nil {
return 0, err
}
if codecVersion != gotCodecVersion {
return 0, fmt.Errorf("%w: %d", errInvalidCodecVersion, gotCodecVersion)
}
if proof.StartProof, err = c.decodeProofPath(src); err != nil {
return 0, err
}
if proof.EndProof, err = c.decodeProofPath(src); err != nil {
return 0, err
}

numKeyValues, err := c.decodeInt(src)
if err != nil {
return 0, err
}
if numKeyValues < 0 {
return 0, errNegativeNumKeyValues
}
if numKeyValues > src.Len()/minKeyValueLen {
return 0, io.ErrUnexpectedEOF
}
proof.KeyValues = make([]KeyValue, numKeyValues)
for i := range proof.KeyValues {
if proof.KeyValues[i], err = c.decodeKeyValue(src); err != nil {
return 0, err
}
}
if src.Len() != 0 {
return 0, errExtraSpace
}
return codecVersion, nil
}

func (c *codecImpl) decodeDBNode(b []byte, n *dbNode) (uint16, error) {
if n == nil {
return 0, errDecodeNil
Expand Down Expand Up @@ -491,24 +410,6 @@ func (c *codecImpl) decodeKeyChange(src *bytes.Reader) (KeyChange, error) {
return result, nil
}

func (c *codecImpl) decodeKeyValue(src *bytes.Reader) (KeyValue, error) {
if minKeyValueLen > src.Len() {
return KeyValue{}, io.ErrUnexpectedEOF
}

var (
result KeyValue
err error
)
if result.Key, err = c.decodeByteSlice(src); err != nil {
return result, err
}
if result.Value, err = c.decodeByteSlice(src); err != nil {
return result, err
}
return result, nil
}

func (c *codecImpl) encodeKeyChange(kv KeyChange, dst io.Writer) error {
if err := c.encodeByteSlice(dst, kv.Key); err != nil {
return err
Expand All @@ -519,16 +420,6 @@ func (c *codecImpl) encodeKeyChange(kv KeyChange, dst io.Writer) error {
return nil
}

func (c *codecImpl) encodeKeyValue(kv KeyValue, dst io.Writer) error {
if err := c.encodeByteSlice(dst, kv.Key); err != nil {
return err
}
if err := c.encodeByteSlice(dst, kv.Value); err != nil {
return err
}
return nil
}

func (*codecImpl) encodeBool(dst io.Writer, value bool) error {
bytesValue := falseBytes
if value {
Expand Down
150 changes: 21 additions & 129 deletions x/merkledb/codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import (
func newRandomProofNode(r *rand.Rand) ProofNode {
key := make([]byte, r.Intn(32)) // #nosec G404
_, _ = r.Read(key) // #nosec G404
serializedKey := newPath(key).Serialize()

val := make([]byte, r.Intn(64)) // #nosec G404
_, _ = r.Read(val) // #nosec G404

Expand All @@ -32,22 +34,28 @@ func newRandomProofNode(r *rand.Rand) ProofNode {
children[byte(j)] = childID
}
}
// use the hash instead when length is greater than the hash length
if len(val) >= HashLength {
val = hashing.ComputeHash256(val)
} else if len(val) == 0 {
// We do this because when we encode a value of []byte{} we will later
// decode it as nil.
// Doing this prevents inconsistency when comparing the encoded and
// decoded values.
// Calling nilEmptySlices doesn't set this because it is a private
// variable on the struct
val = nil

hasValue := rand.Intn(2) == 1 // #nosec G404
var valueOrHash Maybe[[]byte]
if hasValue {
// use the hash instead when length is greater than the hash length
if len(val) >= HashLength {
val = hashing.ComputeHash256(val)
} else if len(val) == 0 {
// We do this because when we encode a value of []byte{} we will later
// decode it as nil.
// Doing this prevents inconsistency when comparing the encoded and
// decoded values.
// Calling nilEmptySlices doesn't set this because it is a private
// variable on the struct
val = nil
}
valueOrHash = Some(val)
Comment on lines +38 to +53
Copy link
Author

Choose a reason for hiding this comment

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

Changed so that the ValueOrHash can be None, whereas previously it was always Some

}

return ProofNode{
KeyPath: newPath(key).Serialize(),
ValueOrHash: Some(val),
KeyPath: serializedKey,
ValueOrHash: valueOrHash,
Children: children,
}
}
Expand Down Expand Up @@ -293,29 +301,6 @@ func FuzzCodecChangeProofCanonical(f *testing.F) {
)
}

func FuzzCodecRangeProofCanonical(f *testing.F) {
f.Fuzz(
func(
t *testing.T,
b []byte,
) {
require := require.New(t)

codec := Codec.(*codecImpl)
proof := &RangeProof{}
got, err := codec.DecodeRangeProof(b, proof)
if err != nil {
return
}

// Encoding [proof] should be the same as [b].
buf, err := codec.EncodeRangeProof(got, proof)
require.NoError(err)
require.Equal(b, buf)
},
)
}

func FuzzCodecDBNodeCanonical(f *testing.F) {
f.Fuzz(
func(
Expand Down Expand Up @@ -434,66 +419,6 @@ func FuzzCodecChangeProofDeterministic(f *testing.F) {
)
}

func FuzzCodecRangeProofDeterministic(f *testing.F) {
f.Fuzz(
func(
t *testing.T,
randSeed int,
numStartProofNodes uint,
numEndProofNodes uint,
numKeyValues uint,
) {
r := rand.New(rand.NewSource(int64(randSeed))) // #nosec G404

var rootID ids.ID
_, _ = r.Read(rootID[:]) // #nosec G404

startProofNodes := make([]ProofNode, numStartProofNodes)
for i := range startProofNodes {
startProofNodes[i] = newRandomProofNode(r)
}

endProofNodes := make([]ProofNode, numEndProofNodes)
for i := range endProofNodes {
endProofNodes[i] = newRandomProofNode(r)
}

keyValues := make([]KeyValue, numKeyValues)
for i := range keyValues {
key := make([]byte, r.Intn(32)) // #nosec G404
_, _ = r.Read(key) // #nosec G404
val := make([]byte, r.Intn(32)) // #nosec G404
_, _ = r.Read(val) // #nosec G404
keyValues[i] = KeyValue{
Key: key,
Value: val,
}
}

proof := RangeProof{
StartProof: startProofNodes,
EndProof: endProofNodes,
KeyValues: keyValues,
}

proofBytes, err := Codec.EncodeRangeProof(Version, &proof)
require.NoError(t, err)

var gotProof RangeProof
_, err = Codec.DecodeRangeProof(proofBytes, &gotProof)
require.NoError(t, err)

nilEmptySlices(&proof)
nilEmptySlices(&gotProof)
require.Equal(t, proof, gotProof)

proofBytes2, err := Codec.EncodeRangeProof(Version, &gotProof)
require.NoError(t, err)
require.Equal(t, proofBytes, proofBytes2)
},
)
}

func FuzzCodecDBNodeDeterministic(f *testing.F) {
f.Fuzz(
func(
Expand Down Expand Up @@ -608,39 +533,6 @@ func TestCodec_DecodeChangeProof(t *testing.T) {
require.ErrorIs(err, errNegativeNumKeyValues)
}

func TestCodec_DecodeRangeProof(t *testing.T) {
require := require.New(t)

_, err := Codec.DecodeRangeProof([]byte{1}, nil)
require.ErrorIs(err, errDecodeNil)

var (
parsedProof RangeProof
tooShortBytes = make([]byte, minRangeProofLen-1)
)
_, err = Codec.DecodeRangeProof(tooShortBytes, &parsedProof)
require.ErrorIs(err, io.ErrUnexpectedEOF)

proof := RangeProof{
StartProof: nil,
EndProof: nil,
KeyValues: nil,
}

proofBytes, err := Codec.EncodeRangeProof(Version, &proof)
require.NoError(err)

// Remove key-values length (0) from end
proofBytes = proofBytes[:len(proofBytes)-minVarIntLen]
proofBytesBuf := bytes.NewBuffer(proofBytes)
// Put key-value length (-1) at end
err = Codec.(*codecImpl).encodeInt(proofBytesBuf, -1)
require.NoError(err)

_, err = Codec.DecodeRangeProof(proofBytesBuf.Bytes(), &parsedProof)
require.ErrorIs(err, errNegativeNumKeyValues)
}

func TestCodec_DecodeDBNode(t *testing.T) {
require := require.New(t)

Expand Down
Loading