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

tlv: Added bool to primitive #8057

Merged
merged 2 commits into from
Oct 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions docs/release-notes/release-notes-0.18.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@

# Improvements
## Functional Updates
### Tlv
* [Bool was added](https://github.com/lightningnetwork/lnd/pull/8057) to the
primitive type of the tlv package.

## RPC Updates

* [Deprecated](https://github.com/lightningnetwork/lnd/pull/7175)
Expand Down Expand Up @@ -79,3 +83,4 @@
* Carla Kirk-Cohen
* Elle Mouton
* Yong Yu
* Ononiwu Maureen Chiamaka
37 changes: 23 additions & 14 deletions tlv/fuzz_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,13 @@ func FuzzVarBytes(f *testing.F) {
})
}

func FuzzBool(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
var val bool
harness(t, data, EBool, DBool, &val, 1)
})
}

// bigSizeHarness works the same as harness, except that it compares decoded
// values instead of encoded values. We do this because DBigSize may leave some
// bytes unparsed from data, causing the encoded data to be shorter than the
Expand Down Expand Up @@ -210,20 +217,21 @@ func encodeParsedTypes(t *testing.T, parsedTypes TypeMap,
func FuzzStream(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
var (
u8 uint8
u16 uint16
u32 uint32
u64 uint64
b32 [32]byte
b33 [33]byte
b64 [64]byte
pk *btcec.PublicKey
b []byte
bs32 uint32
bs64 uint64
tu16 uint16
tu32 uint32
tu64 uint64
u8 uint8
u16 uint16
u32 uint32
u64 uint64
b32 [32]byte
b33 [33]byte
b64 [64]byte
pk *btcec.PublicKey
b []byte
bs32 uint32
bs64 uint64
tu16 uint16
tu32 uint32
tu64 uint64
boolean bool
)

sizeTU16 := func() uint64 {
Expand Down Expand Up @@ -260,6 +268,7 @@ func FuzzStream(f *testing.F) {
MakeDynamicRecord(
13, &tu64, sizeTU64, ETUint64, DTUint64,
),
MakePrimitiveRecord(14, &boolean),
}
decodeStream := MustNewStream(decodeRecords...)

Expand Down
43 changes: 43 additions & 0 deletions tlv/primitive.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tlv

import (
"encoding/binary"
"errors"
"fmt"
"io"

Expand Down Expand Up @@ -143,6 +144,33 @@ func EUint64T(w io.Writer, val uint64, buf *[8]byte) error {
return err
}

// EBool encodes a boolean. An error is returned if val is not a boolean.
func EBool(w io.Writer, val interface{}, buf *[8]byte) error {
if i, ok := val.(*bool); ok {
if *i {
buf[0] = 1
} else {
buf[0] = 0
}
_, err := w.Write(buf[:1])
return err
}
return NewTypeForEncodingErr(val, "bool")
}

// EBoolT encodes a bool val to the provided io.Writer. This method is exposed
// so that encodings for custom bool-like types can be created without
// incurring an extra heap allocation.
func EBoolT(w io.Writer, val bool, buf *[8]byte) error {
if val {
buf[0] = 1
} else {
buf[0] = 0
}
_, err := w.Write(buf[:1])
return err
}

// DUint8 is a Decoder for uint8 values. An error is returned if val is not a
// *uint8.
func DUint8(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
Expand Down Expand Up @@ -195,6 +223,21 @@ func DUint64(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
return NewTypeForDecodingErr(val, "uint64", l, 8)
}

// DBool decodes a boolean. An error is returned if val is not a boolean.
func DBool(r io.Reader, val interface{}, buf *[8]byte, l uint64) error {
if i, ok := val.(*bool); ok && l == 1 {
if _, err := io.ReadFull(r, buf[:1]); err != nil {
return err
}
if buf[0] != 0 && buf[0] != 1 {
return errors.New("corrupted data")
}
*i = buf[0] != 0
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe we should return an error if val is not 0 or 1... If that happens when decoding from the DB it's a strong indication that there's a bug or data corruption somewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the review, pushed new changes with respect to this.

return nil
}
return NewTypeForDecodingErr(val, "bool", l, 1)
}

// EBytes32 is an Encoder for 32-byte arrays. An error is returned if val is not
// a *[32]byte.
func EBytes32(w io.Writer, val interface{}, _ *[8]byte) error {
Expand Down
49 changes: 31 additions & 18 deletions tlv/primitive_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ var testPK, _ = btcec.ParsePubKey([]byte{0x02,
})

type primitive struct {
u8 byte
u16 uint16
u32 uint32
u64 uint64
b32 [32]byte
b33 [33]byte
b64 [64]byte
pk *btcec.PublicKey
bytes []byte
u8 byte
u16 uint16
u32 uint32
u64 uint64
b32 [32]byte
b33 [33]byte
b64 [64]byte
pk *btcec.PublicKey
bytes []byte
boolean bool
}

// TestWrongEncodingType asserts that all primitives encoders will fail with a
Expand All @@ -41,6 +42,7 @@ func TestWrongEncodingType(t *testing.T) {
tlv.EBytes64,
tlv.EPubKey,
tlv.EVarBytes,
tlv.EBool,
}

// We'll use an int32 since it is not a primitive type, which should
Expand Down Expand Up @@ -73,6 +75,7 @@ func TestWrongDecodingType(t *testing.T) {
tlv.DBytes64,
tlv.DPubKey,
tlv.DVarBytes,
tlv.DBool,
}

// We'll use an int32 since it is not a primitive type, which should
Expand Down Expand Up @@ -108,15 +111,16 @@ type fieldDecoder struct {
// to decode the output and arrive at the same fields.
func TestPrimitiveEncodings(t *testing.T) {
prim := primitive{
u8: 0x01,
u16: 0x0201,
u32: 0x02000001,
u64: 0x0200000000000001,
b32: [32]byte{0x02, 0x01},
b33: [33]byte{0x03, 0x01},
b64: [64]byte{0x02, 0x01},
pk: testPK,
bytes: []byte{0xaa, 0xbb},
u8: 0x01,
u16: 0x0201,
u32: 0x02000001,
u64: 0x0200000000000001,
b32: [32]byte{0x02, 0x01},
b33: [33]byte{0x03, 0x01},
b64: [64]byte{0x02, 0x01},
pk: testPK,
bytes: []byte{0xaa, 0xbb},
boolean: true,
}

encoders := []fieldEncoder{
Expand Down Expand Up @@ -156,6 +160,10 @@ func TestPrimitiveEncodings(t *testing.T) {
val: &prim.bytes,
encoder: tlv.EVarBytes,
},
{
guggero marked this conversation as resolved.
Show resolved Hide resolved
val: &prim.boolean,
encoder: tlv.EBool,
},
}

// First we'll encode the primitive fields into a buffer.
Expand Down Expand Up @@ -222,6 +230,11 @@ func TestPrimitiveEncodings(t *testing.T) {
decoder: tlv.DVarBytes,
size: 2,
},
{
val: &prim2.boolean,
decoder: tlv.DBool,
size: 1,
},
}

for _, field := range decoders {
Expand Down
5 changes: 5 additions & 0 deletions tlv/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ func MakePrimitiveRecord(typ Type, val interface{}) Record {
decoder Decoder
)
switch e := val.(type) {
case *bool:
staticSize = 1
encoder = EBool
decoder = DBool
guggero marked this conversation as resolved.
Show resolved Hide resolved

case *uint8:
staticSize = 1
encoder = EUint8
Expand Down
Loading