Skip to content

Commit

Permalink
remove Amino (#265)
Browse files Browse the repository at this point in the history
Removes Amino and adds varint utility-functions instead, keeping the same serialized format. Partial fix for #242.

The last vestige of Amino is in the proof handling, namely `AbsenceOp` and `ValueOp`, which are structs encoded/decoded with Amino.
  • Loading branch information
erikgrinaker authored Jun 30, 2020
1 parent 4f7cdd7 commit 5d5829f
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 69 deletions.
28 changes: 14 additions & 14 deletions docs/node/node.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,43 +34,43 @@ Every node is persisted by encoding the key, version, height, size and hash. If
```golang
// Writes the node as a serialized byte slice to the supplied io.Writer.
func (node *Node) writeBytes(w io.Writer) error {
cause := amino.EncodeInt8(w, node.height)
cause := encodeVarint(w, node.height)
if cause != nil {
return errors.Wrap(cause, "writing height")
}
cause = amino.EncodeVarint(w, node.size)
cause = encodeVarint(w, node.size)
if cause != nil {
return errors.Wrap(cause, "writing size")
}
cause = amino.EncodeVarint(w, node.version)
cause = encodeVarint(w, node.version)
if cause != nil {
return errors.Wrap(cause, "writing version")
}

// Unlike writeHashBytes, key is written for inner nodes.
cause = amino.EncodeByteSlice(w, node.key)
cause = encodeBytes(w, node.key)
if cause != nil {
return errors.Wrap(cause, "writing key")
}

if node.isLeaf() {
cause = amino.EncodeByteSlice(w, node.value)
cause = encodeBytes(w, node.value)
if cause != nil {
return errors.Wrap(cause, "writing value")
}
} else {
if node.leftHash == nil {
panic("node.leftHash was nil in writeBytes")
}
cause = amino.EncodeByteSlice(w, node.leftHash)
cause = encodeBytes(w, node.leftHash)
if cause != nil {
return errors.Wrap(cause, "writing left hash")
}

if node.rightHash == nil {
panic("node.rightHash was nil in writeBytes")
}
cause = amino.EncodeByteSlice(w, node.rightHash)
cause = encodeBytes(w, node.rightHash)
if cause != nil {
return errors.Wrap(cause, "writing right hash")
}
Expand All @@ -87,42 +87,42 @@ A node's hash is calculated by hashing the height, size, and version of the node
// Writes the node's hash to the given io.Writer. This function expects
// child hashes to be already set.
func (node *Node) writeHashBytes(w io.Writer) error {
err := amino.EncodeInt8(w, node.height)
err := encodeVarint(w, node.height)
if err != nil {
return errors.Wrap(err, "writing height")
}
err = amino.EncodeVarint(w, node.size)
err = encodeVarint(w, node.size)
if err != nil {
return errors.Wrap(err, "writing size")
}
err = amino.EncodeVarint(w, node.version)
err = encodeVarint(w, node.version)
if err != nil {
return errors.Wrap(err, "writing version")
}

// Key is not written for inner nodes, unlike writeBytes.

if node.isLeaf() {
err = amino.EncodeByteSlice(w, node.key)
err = encodeBytes(w, node.key)
if err != nil {
return errors.Wrap(err, "writing key")
}
// Indirection needed to provide proofs without values.
// (e.g. proofLeafNode.ValueHash)
valueHash := tmhash.Sum(node.value)
err = amino.EncodeByteSlice(w, valueHash)
err = encodeBytes(w, valueHash)
if err != nil {
return errors.Wrap(err, "writing value")
}
} else {
if node.leftHash == nil || node.rightHash == nil {
panic("Found an empty child hash")
}
err = amino.EncodeByteSlice(w, node.leftHash)
err = encodeBytes(w, node.leftHash)
if err != nil {
return errors.Wrap(err, "writing left hash")
}
err = amino.EncodeByteSlice(w, node.rightHash)
err = encodeBytes(w, node.rightHash)
if err != nil {
return errors.Wrap(err, "writing right hash")
}
Expand Down
104 changes: 104 additions & 0 deletions encoding.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package iavl

import (
"encoding/binary"
"errors"
"fmt"
"io"
"math/bits"
)

// decodeBytes decodes a varint length-prefixed byte slice, returning it along with the number
// of input bytes read.
func decodeBytes(bz []byte) ([]byte, int, error) {
size, n, err := decodeUvarint(bz)
if err != nil {
return nil, n, err
}
if int(size) < 0 {
return nil, n, fmt.Errorf("invalid negative length %v decoding []byte", size)
}
if len(bz) < n+int(size) {
return nil, n, fmt.Errorf("insufficient bytes decoding []byte of length %v", size)
}
bz2 := make([]byte, size)
copy(bz2, bz[n:n+int(size)])
n += int(size)
return bz2, n, nil
}

// decodeUvarint decodes a varint-encoded unsigned integer from a byte slice, returning it and the
// number of bytes decoded.
func decodeUvarint(bz []byte) (uint64, int, error) {
u, n := binary.Uvarint(bz)
if n == 0 {
// buf too small
return u, n, errors.New("buffer too small")
} else if n < 0 {
// value larger than 64 bits (overflow)
// and -n is the number of bytes read
n = -n
return u, n, errors.New("EOF decoding uvarint")
}
return u, n, nil
}

// decodeVarint decodes a varint-encoded integer from a byte slice, returning it and the number of
// bytes decoded.
func decodeVarint(bz []byte) (int64, int, error) {
i, n := binary.Varint(bz)
if n == 0 {
return i, n, errors.New("buffer too small")
} else if n < 0 {
// value larger than 64 bits (overflow)
// and -n is the number of bytes read
n = -n
return i, n, errors.New("EOF decoding varint")
}
return i, n, nil
}

// encodeBytes writes a varint length-prefixed byte slice to the writer.
func encodeBytes(w io.Writer, bz []byte) error {
err := encodeUvarint(w, uint64(len(bz)))
if err != nil {
return err
}
_, err = w.Write(bz)
return err
}

// encodeBytesSize returns the byte size of the given slice including length-prefixing.
func encodeBytesSize(bz []byte) int {
return encodeUvarintSize(uint64(len(bz))) + len(bz)
}

// encodeUvarint writes a varint-encoded unsigned integer to an io.Writer.
func encodeUvarint(w io.Writer, u uint64) error {
var buf [binary.MaxVarintLen64]byte
n := binary.PutUvarint(buf[:], u)
_, err := w.Write(buf[0:n])
return err
}

// encodeUvarintSize returns the byte size of the given integer as a varint.
func encodeUvarintSize(u uint64) int {
if u == 0 {
return 1
}
return (bits.Len64(u) + 6) / 7
}

// encodeVarint writes a varint-encoded integer to an io.Writer.
func encodeVarint(w io.Writer, i int64) error {
var buf [binary.MaxVarintLen64]byte
n := binary.PutVarint(buf[:], i)
_, err := w.Write(buf[0:n])
return err
}

// encodeVarintSize returns the byte size of the given integer as a varint.
func encodeVarintSize(i int64) int {
var buf [binary.MaxVarintLen64]byte
return binary.PutVarint(buf[:], i)
}
1 change: 1 addition & 0 deletions export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ func setupExportTreeBasic(t require.TestingT) *ImmutableTree {
}

// setupExportTreeRandom sets up a randomly generated tree.
// nolint: dupl
func setupExportTreeRandom(t *testing.T) *ImmutableTree {
const (
randSeed = 49872768940 // For deterministic tests
Expand Down
Loading

0 comments on commit 5d5829f

Please sign in to comment.