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: add new types and functions #8692

Merged
merged 3 commits into from
Apr 26, 2024
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
12 changes: 6 additions & 6 deletions tlv/go.mod
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
module github.com/lightningnetwork/lnd/tlv

require (
github.com/btcsuite/btcd v0.23.3
github.com/btcsuite/btcd/btcec/v2 v2.1.3
github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af
github.com/btcsuite/btcd/btcec/v2 v2.3.2
github.com/davecgh/go-spew v1.1.1
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1
github.com/lightningnetwork/lnd/fn v1.0.4
github.com/stretchr/testify v1.8.2
github.com/stretchr/testify v1.8.4
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b
)

require (
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect
github.com/kr/pretty v0.3.0 // indirect
github.com/lightninglabs/neutrino/cache v1.1.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/rogpeppe/go-internal v1.9.0 // indirect
golang.org/x/crypto v0.7.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/crypto v0.16.0 // indirect
golang.org/x/sys v0.15.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Expand Down
31 changes: 12 additions & 19 deletions tlv/go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
github.com/btcsuite/btcd v0.23.3 h1:4KH/JKy9WiCd+iUS9Mu0Zp7Dnj17TGdKrg9xc/FGj24=
github.com/btcsuite/btcd v0.23.3/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY=
github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE=
github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af h1:F60A3wst4/fy9Yr1Vn8MYmFlfn7DNLxp8o8UTvhqgBE=
github.com/btcsuite/btcd v0.24.1-0.20240301210420-1a2b599bf1af/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg=
github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ=
github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
Expand All @@ -28,24 +27,18 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b h1:kLiC65FbiHWFAOu+lxwNPujcsl8VYyTYYEZnsOO1WK4=
golang.org/x/exp v0.0.0-20231226003508-02704c960a9b/go.mod h1:iRJReGqOEeBhDZGkGbynYwcHlctCvnjTYIamk7uXpHI=
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
4 changes: 4 additions & 0 deletions tlv/record.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ type Type uint64
// slice containing the encoded data.
type TypeMap map[Type][]byte

// Blob is a type alias for a byte slice. It's used to indicate that a slice of
// bytes is actually an encoded TLV stream.
type Blob = []byte
Comment on lines +20 to +22
Copy link
Collaborator

Choose a reason for hiding this comment

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

If there are different semantics here that are being assumed, I'd recommend not making it a simple alias and actually make a newtype (dropping the =)

Copy link
Member

Choose a reason for hiding this comment

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

In this case using the type alias is useful, as then I can put it in a tlv.RecordT as a primitive type. Otherwise, you'd need the record method wrapper.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We should just make RecordT a primitive type, no? I just want to stress that type aliases are not useful for anything other than giving the reader a hint on how something is used, it assures no correct api usage the way that a newtype wrapper does.


// Encoder is a signature for methods that can encode TLV values. An error
// should be returned if the Encoder cannot support the underlying type of val.
// The provided scratch buffer must be non-nil.
Expand Down
46 changes: 41 additions & 5 deletions tlv/record_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,15 @@ type OptionalRecordT[T TlvType, V any] struct {
// TlvType returns the type of the record. This is the value used to identify
// this type on the wire. This value is bound to the specified TlvType type
// param.
func (t *OptionalRecordT[T, V]) TlvType() Type {
func (o *OptionalRecordT[T, V]) TlvType() Type {
zeroRecord := ZeroRecordT[T, V]()
return zeroRecord.TlvType()
}

// WhenSomeV executes the given function if the optional record is present.
// This operates on the inner most type, V, which is the value of the record.
func (t *OptionalRecordT[T, V]) WhenSomeV(f func(V)) {
t.Option.WhenSome(func(r RecordT[T, V]) {
func (o *OptionalRecordT[T, V]) WhenSomeV(f func(V)) {
o.Option.WhenSome(func(r RecordT[T, V]) {
f(r.Val)
})
}
Expand All @@ -126,7 +126,7 @@ func (o *OptionalRecordT[T, V]) UnwrapOrFailV(t *testing.T) V {
return inner.Val
}

// UnwrapOrErr is used to extract a value from an option, if the option is
// UnwrapOrErrV is used to extract a value from an option, if the option is
// empty, then the specified error is returned directly. This gives the
// underlying value of the record, instead of the record itself.
func (o *OptionalRecordT[T, V]) UnwrapOrErrV(err error) (V, error) {
Expand All @@ -141,10 +141,19 @@ func (o *OptionalRecordT[T, V]) UnwrapOrErrV(err error) (V, error) {
}

// Zero returns a zero value of the record type.
func (t *OptionalRecordT[T, V]) Zero() RecordT[T, V] {
func (o *OptionalRecordT[T, V]) Zero() RecordT[T, V] {
return ZeroRecordT[T, V]()
}

// ValOpt returns an Option of the underlying value. This can be used to chain
// other option related methods to avoid needing to first go through the outer
// record.
func (o *OptionalRecordT[T, V]) ValOpt() fn.Option[V] {
return fn.MapOption(func(record RecordT[T, V]) V {
return record.Val
Copy link
Collaborator

@ProofOfKeags ProofOfKeags Apr 25, 2024

Choose a reason for hiding this comment

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

Pondering...

This makes me wonder if we should extract out Val as a function. We have the following issue here:

-- we have these
type OptionalRecord t v = Option (Record t v)
valOpt :: Option (Record t v) -> Option v
mapOption :: (a -> b) -> Option a -> Option b

-- if we had this
val :: Record t v -> v

-- then we could do this
valOpt :: OptionalRecord t v -> Option v
valOpt = mapOption val

Porting this go to is tricky since there's no (to my knowledge) easy way for us to refer to a method without yet supplying its receiver argument. So defining something like this wouldn't fix the ergonomics:

func (r RecordT[T, V]) Val() V { ... }

func (o *OptionalRecordT[T, V]) ValOpt() fn.Option[V] {
        return fn.MapOption(RecordT.Val)(*o) // no sir
}

// but we could do this
func Val[T, V any](r Record[T, V]) V { ... }

// and then do this
func (o *OptionalRecordT[T, V] ValOpt() fn.Option[V] {
        return fn.MapOption(Val)(*o) // can do
}

I say all this because if we have to create new names for things every time we want to map a function over some functor, then Map<FUNCTOR> loses a lot of its luster.

If you have ideas and/or opinions here, lmk. This code is fine, it just points to the fact that there's room for improvement on our functional patterns.

})(o.Option)
}

// SomeRecordT creates a new OptionalRecordT type from a given RecordT type.
func SomeRecordT[T TlvType, V any](record RecordT[T, V]) OptionalRecordT[T, V] {
return OptionalRecordT[T, V]{
Expand All @@ -159,3 +168,30 @@ func ZeroRecordT[T TlvType, V any]() RecordT[T, V] {
Val: v,
}
}

// BigSizeT is a high-order type that represents a TLV record that encodes an
// integer as a BigSize value in the stream.
type BigSizeT[T constraints.Integer] struct {
Copy link
Collaborator

Choose a reason for hiding this comment

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

🫡

// We'll store the base value in the struct as a uin64, but then expose
// a public method to cast to the specified type.
v uint64
}

// NewBigSizeT creates a new BigSizeT type from a given integer type.
func NewBigSizeT[T constraints.Integer](val T) BigSizeT[T] {
return BigSizeT[T]{
v: uint64(val),
}
}

// Int returns the underlying integer value of the BigSize record.
func (b BigSizeT[T]) Int() T {
return T(b.v)
}

// Record returns the underlying record interface for the record type.
func (b *BigSizeT[T]) Record() Record {
// We use a zero value for the type here as this should be used with
// the higher order RecordT type.
return MakeBigSizeRecord(0, &b.v)
}
Loading