Skip to content

Commit

Permalink
x/sync -- Remove EncodeProof, DecodeProof, helpers (#1546)
Browse files Browse the repository at this point in the history
  • Loading branch information
Dan Laine authored May 30, 2023
1 parent 4283af2 commit e5ad19b
Show file tree
Hide file tree
Showing 3 changed files with 14 additions and 404 deletions.
211 changes: 14 additions & 197 deletions x/merkledb/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,20 @@ var (
trueBytes = []byte{trueByte}
falseBytes = []byte{falseByte}

errUnknownVersion = errors.New("unknown codec version")
errEncodeNil = errors.New("can't encode nil pointer or interface")
errDecodeNil = errors.New("can't decode nil")
errNegativeProofPathNodes = errors.New("negative proof path length")
errNegativeNumChildren = errors.New("number of children is negative")
errTooManyChildren = fmt.Errorf("length of children list is larger than branching factor of %d", NodeBranchFactor)
errChildIndexTooLarge = fmt.Errorf("invalid child index. Must be less than branching factor of %d", NodeBranchFactor)
errNegativeNibbleLength = errors.New("nibble length is negative")
errIntTooLarge = errors.New("integer too large to be decoded")
errLeadingZeroes = errors.New("varint has leading zeroes")
errInvalidBool = errors.New("decoded bool is neither true nor false")
errNonZeroNibblePadding = errors.New("nibbles should be padded with 0s")
errExtraSpace = errors.New("trailing buffer space")
errNegativeSliceLength = errors.New("negative slice length")
errInvalidCodecVersion = errors.New("invalid codec version")
errUnknownVersion = errors.New("unknown codec version")
errEncodeNil = errors.New("can't encode nil pointer or interface")
errDecodeNil = errors.New("can't decode nil")
errNegativeNumChildren = errors.New("number of children is negative")
errTooManyChildren = fmt.Errorf("length of children list is larger than branching factor of %d", NodeBranchFactor)
errChildIndexTooLarge = fmt.Errorf("invalid child index. Must be less than branching factor of %d", NodeBranchFactor)
errNegativeNibbleLength = errors.New("nibble length is negative")
errIntTooLarge = errors.New("integer too large to be decoded")
errLeadingZeroes = errors.New("varint has leading zeroes")
errInvalidBool = errors.New("decoded bool is neither true nor false")
errNonZeroNibblePadding = errors.New("nibbles should be padded with 0s")
errExtraSpace = errors.New("trailing buffer space")
errNegativeSliceLength = errors.New("negative slice length")
errInvalidCodecVersion = errors.New("invalid codec version")
)

// EncoderDecoder defines the interface needed by merkleDB to marshal
Expand All @@ -71,15 +70,11 @@ type EncoderDecoder interface {
}

type Encoder interface {
EncodeProof(version uint16, p *Proof) ([]byte, error)

encodeDBNode(version uint16, n *dbNode) ([]byte, error)
encodeHashValues(version uint16, hv *hashValues) ([]byte, error)
}

type Decoder interface {
DecodeProof(bytes []byte, p *Proof) (uint16, error)

decodeDBNode(bytes []byte, n *dbNode) (uint16, error)
}

Expand All @@ -97,31 +92,6 @@ type codecImpl struct {
varIntPool sync.Pool
}

func (c *codecImpl) EncodeProof(version uint16, proof *Proof) ([]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.Path); err != nil {
return nil, err
}
if err := c.encodeByteSlice(buf, proof.Key); err != nil {
return nil, err
}
if err := c.encodeMaybeByteSlice(buf, proof.Value); 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 @@ -200,40 +170,6 @@ func (c *codecImpl) encodeHashValues(version uint16, hv *hashValues) ([]byte, er
return buf.Bytes(), nil
}

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

var (
err error
src = bytes.NewReader(b)
)
gotCodecVersion, err := c.decodeInt(src)
if err != nil {
return 0, err
}
if codecVersion != gotCodecVersion {
return 0, fmt.Errorf("%w: %d", errInvalidCodecVersion, gotCodecVersion)
}
if proof.Path, err = c.decodeProofPath(src); err != nil {
return 0, err
}
if proof.Key, err = c.decodeByteSlice(src); err != nil {
return 0, err
}
if proof.Value, err = c.decodeMaybeByteSlice(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 @@ -467,125 +403,6 @@ func (*codecImpl) decodeID(src *bytes.Reader) (ids.ID, error) {
return id, nil
}

// Assumes a proof path has > 0 nodes.
func (c *codecImpl) decodeProofPath(src *bytes.Reader) ([]ProofNode, error) {
if minProofPathLen > src.Len() {
return nil, io.ErrUnexpectedEOF
}

numProofNodes, err := c.decodeInt(src)
if err != nil {
return nil, err
}
if numProofNodes < 0 {
return nil, errNegativeProofPathNodes
}
if numProofNodes > src.Len()/minProofNodeLen {
return nil, io.ErrUnexpectedEOF
}
result := make([]ProofNode, numProofNodes)
for i := 0; i < numProofNodes; i++ {
if result[i], err = c.decodeProofNode(src); err != nil {
return nil, err
}
}
return result, nil
}

// Invariant: len(path) > 0.
func (c *codecImpl) encodeProofPath(dst io.Writer, path []ProofNode) error {
if err := c.encodeInt(dst, len(path)); err != nil {
return err
}
for _, proofNode := range path {
if err := c.encodeProofNode(proofNode, dst); err != nil {
return err
}
}
return nil
}

func (c *codecImpl) decodeProofNode(src *bytes.Reader) (ProofNode, error) {
if minProofNodeLen > src.Len() {
return ProofNode{}, io.ErrUnexpectedEOF
}

var (
result ProofNode
err error
)
if result.KeyPath, err = c.decodeSerializedPath(src); err != nil {
return result, err
}
if result.ValueOrHash, err = c.decodeMaybeByteSlice(src); err != nil {
return result, err
}
numChildren, err := c.decodeInt(src)
if err != nil {
return result, err
}
switch {
case numChildren < 0:
return result, errNegativeNumChildren
case numChildren > NodeBranchFactor:
return result, errTooManyChildren
case numChildren > src.Len()/minProofNodeChildLen:
return result, io.ErrUnexpectedEOF
}

result.Children = make(map[byte]ids.ID, numChildren)
previousChild := -1
for addedEntries := 0; addedEntries < numChildren; addedEntries++ {
index, err := c.decodeInt(src)
if err != nil {
return result, err
}
if index <= previousChild || index >= NodeBranchFactor {
return result, errChildIndexTooLarge
}
previousChild = index

childID, err := c.decodeID(src)
if err != nil {
return result, err
}
result.Children[byte(index)] = childID
}
return result, nil
}

func (c *codecImpl) encodeProofNode(pn ProofNode, dst io.Writer) error {
if err := c.encodeSerializedPath(pn.KeyPath, dst); err != nil {
return err
}
if err := c.encodeMaybeByteSlice(dst, pn.ValueOrHash); err != nil {
return err
}
if err := c.encodeInt(dst, len(pn.Children)); err != nil {
return err
}
// ensure this is in order
childrenCount := 0
for index := byte(0); index < NodeBranchFactor; index++ {
childID, ok := pn.Children[index]
if !ok {
continue
}
childrenCount++
if err := c.encodeInt(dst, int(index)); err != nil {
return err
}
if _, err := dst.Write(childID[:]); err != nil {
return err
}
}
// there are children present with index >= NodeBranchFactor
if childrenCount != len(pn.Children) {
return errChildIndexTooLarge
}
return nil
}

func (c *codecImpl) encodeSerializedPath(s SerializedPath, dst io.Writer) error {
if err := c.encodeInt(dst, s.NibbleLength); err != nil {
return err
Expand Down
117 changes: 0 additions & 117 deletions x/merkledb/codec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,68 +198,6 @@ func FuzzCodecSerializedPath(f *testing.F) {
)
}

func FuzzCodecProofCanonical(f *testing.F) {
f.Add(
[]byte{
// RootID:
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// Path:
// Num proof nodes = 1
0x02,
// Key Path:
// Nibble Length:
0x00,
// Value:
// Has Value = false
0x00,
// Num Children = 2
0x04,
// Child 0:
// index = 0
0x00,
// childID:
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// Child 1:
// index = 0 <- should fail
0x00,
// childID:
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
// Key:
// length = 0
0x00,
},
)
f.Fuzz(
func(
t *testing.T,
b []byte,
) {
require := require.New(t)

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

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

func FuzzCodecDBNodeCanonical(f *testing.F) {
f.Fuzz(
func(
Expand All @@ -283,47 +221,6 @@ func FuzzCodecDBNodeCanonical(f *testing.F) {
)
}

func FuzzCodecProofDeterministic(f *testing.F) {
f.Fuzz(
func(
t *testing.T,
randSeed int,
key []byte,
numProofNodes uint,
) {
require := require.New(t)

r := rand.New(rand.NewSource(int64(randSeed))) // #nosec G404

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

proof := Proof{
Path: proofNodes,
Key: key,
}

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

var gotProof Proof
gotVersion, err := Codec.DecodeProof(proofBytes, &gotProof)
require.NoError(err)
require.Equal(Version, gotVersion)

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

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

func FuzzCodecDBNodeDeterministic(f *testing.F) {
f.Fuzz(
func(
Expand Down Expand Up @@ -389,20 +286,6 @@ func FuzzCodecDBNodeDeterministic(f *testing.F) {
)
}

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

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

var (
proof Proof
tooShortBytes = make([]byte, minProofLen-1)
)
_, err = Codec.DecodeProof(tooShortBytes, &proof)
require.ErrorIs(err, io.ErrUnexpectedEOF)
}

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

Expand Down
Loading

0 comments on commit e5ad19b

Please sign in to comment.