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 -- Remove EncodeProof, DecodeProof, helpers #1546

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