From 78d5806555fd6243baaf2996b16a1ea6addf93b5 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 6 Dec 2023 19:07:11 -0800 Subject: [PATCH 1/3] tlv/internal: add new internal package for generating TLV type structs In this commit, we add some new code generation to the codebase. As we'll see in a future commit, this'll allow us to create a new Record[T, V] type, where T is actually a concrete _struct_ that implements a special interface that deems it as a valid TLV type. --- tlv/internal/gen/gen_tlv_types.go | 59 +++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 tlv/internal/gen/gen_tlv_types.go diff --git a/tlv/internal/gen/gen_tlv_types.go b/tlv/internal/gen/gen_tlv_types.go new file mode 100644 index 000000000..d35a2b650 --- /dev/null +++ b/tlv/internal/gen/gen_tlv_types.go @@ -0,0 +1,59 @@ +package main + +import ( + "bytes" + "flag" + "os" + "text/template" +) + +const ( + numberOfTypes = 100 + defaultOutputFile = "tlv_types_generated.go" +) + +const typeCodeTemplate = `// Code generated by tlv/internal/gen; DO NOT EDIT. + +package tlv + +{{- range $index, $element := . }} + +type tlvType{{ $index }} struct{} + +func (t *tlvType{{ $index }}) typeVal() Type { + return {{ $index }} +} + +type TlvType{{ $index }} = *tlvType{{ $index }} +{{- end }} +` + +func main() { + // Create a slice of items that the template can range over. + var items []struct{} + for i := uint16(0); i <= numberOfTypes; i++ { + items = append(items, struct{}{}) + } + + tpl, err := template.New("tlv").Parse(typeCodeTemplate) + if err != nil { + panic(err) + } + + // Execute the template + var out bytes.Buffer + err = tpl.Execute(&out, items) + if err != nil { + panic(err) + } + + outputFile := flag.String( + "o", defaultOutputFile, "Output file for generated code", + ) + flag.Parse() + + err = os.WriteFile(*outputFile, out.Bytes(), 0644) + if err != nil { + panic(err) + } +} From c304c2a8bbb366572b142dcea1e0ff483bce54ad Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 6 Dec 2023 19:08:15 -0800 Subject: [PATCH 2/3] tlv: run go generate for new set of TLV types This shouldn't need to be run again, as this implementation restricts things to just values 0-99, due to a hard upper limit with the way Go unions work under the hood. --- tlv/tlv_type_param.go | 16 + tlv/tlv_types_generated.go | 811 +++++++++++++++++++++++++++++++++++++ 2 files changed, 827 insertions(+) create mode 100644 tlv/tlv_type_param.go create mode 100644 tlv/tlv_types_generated.go diff --git a/tlv/tlv_type_param.go b/tlv/tlv_type_param.go new file mode 100644 index 000000000..0d2c819f1 --- /dev/null +++ b/tlv/tlv_type_param.go @@ -0,0 +1,16 @@ +package tlv + +import "fmt" + +// TlvType is an interface used to enable binding the integer type of a TLV +// record to the type at compile time. +type TlvType interface { + typeVal() Type +} + +//go:generate go run internal/gen/gen_tlv_types.go -o tlv_types_generated.go + +func main() { + // This function is only here to satisfy the go:generate directive. + fmt.Println("Generating TLV type structures...") +} diff --git a/tlv/tlv_types_generated.go b/tlv/tlv_types_generated.go new file mode 100644 index 000000000..a18b74bb0 --- /dev/null +++ b/tlv/tlv_types_generated.go @@ -0,0 +1,811 @@ +// Code generated by tlv/internal/gen; DO NOT EDIT. + +package tlv + +type tlvType0 struct{} + +func (t *tlvType0) typeVal() Type { + return 0 +} + +type TlvType0 = *tlvType0 + +type tlvType1 struct{} + +func (t *tlvType1) typeVal() Type { + return 1 +} + +type TlvType1 = *tlvType1 + +type tlvType2 struct{} + +func (t *tlvType2) typeVal() Type { + return 2 +} + +type TlvType2 = *tlvType2 + +type tlvType3 struct{} + +func (t *tlvType3) typeVal() Type { + return 3 +} + +type TlvType3 = *tlvType3 + +type tlvType4 struct{} + +func (t *tlvType4) typeVal() Type { + return 4 +} + +type TlvType4 = *tlvType4 + +type tlvType5 struct{} + +func (t *tlvType5) typeVal() Type { + return 5 +} + +type TlvType5 = *tlvType5 + +type tlvType6 struct{} + +func (t *tlvType6) typeVal() Type { + return 6 +} + +type TlvType6 = *tlvType6 + +type tlvType7 struct{} + +func (t *tlvType7) typeVal() Type { + return 7 +} + +type TlvType7 = *tlvType7 + +type tlvType8 struct{} + +func (t *tlvType8) typeVal() Type { + return 8 +} + +type TlvType8 = *tlvType8 + +type tlvType9 struct{} + +func (t *tlvType9) typeVal() Type { + return 9 +} + +type TlvType9 = *tlvType9 + +type tlvType10 struct{} + +func (t *tlvType10) typeVal() Type { + return 10 +} + +type TlvType10 = *tlvType10 + +type tlvType11 struct{} + +func (t *tlvType11) typeVal() Type { + return 11 +} + +type TlvType11 = *tlvType11 + +type tlvType12 struct{} + +func (t *tlvType12) typeVal() Type { + return 12 +} + +type TlvType12 = *tlvType12 + +type tlvType13 struct{} + +func (t *tlvType13) typeVal() Type { + return 13 +} + +type TlvType13 = *tlvType13 + +type tlvType14 struct{} + +func (t *tlvType14) typeVal() Type { + return 14 +} + +type TlvType14 = *tlvType14 + +type tlvType15 struct{} + +func (t *tlvType15) typeVal() Type { + return 15 +} + +type TlvType15 = *tlvType15 + +type tlvType16 struct{} + +func (t *tlvType16) typeVal() Type { + return 16 +} + +type TlvType16 = *tlvType16 + +type tlvType17 struct{} + +func (t *tlvType17) typeVal() Type { + return 17 +} + +type TlvType17 = *tlvType17 + +type tlvType18 struct{} + +func (t *tlvType18) typeVal() Type { + return 18 +} + +type TlvType18 = *tlvType18 + +type tlvType19 struct{} + +func (t *tlvType19) typeVal() Type { + return 19 +} + +type TlvType19 = *tlvType19 + +type tlvType20 struct{} + +func (t *tlvType20) typeVal() Type { + return 20 +} + +type TlvType20 = *tlvType20 + +type tlvType21 struct{} + +func (t *tlvType21) typeVal() Type { + return 21 +} + +type TlvType21 = *tlvType21 + +type tlvType22 struct{} + +func (t *tlvType22) typeVal() Type { + return 22 +} + +type TlvType22 = *tlvType22 + +type tlvType23 struct{} + +func (t *tlvType23) typeVal() Type { + return 23 +} + +type TlvType23 = *tlvType23 + +type tlvType24 struct{} + +func (t *tlvType24) typeVal() Type { + return 24 +} + +type TlvType24 = *tlvType24 + +type tlvType25 struct{} + +func (t *tlvType25) typeVal() Type { + return 25 +} + +type TlvType25 = *tlvType25 + +type tlvType26 struct{} + +func (t *tlvType26) typeVal() Type { + return 26 +} + +type TlvType26 = *tlvType26 + +type tlvType27 struct{} + +func (t *tlvType27) typeVal() Type { + return 27 +} + +type TlvType27 = *tlvType27 + +type tlvType28 struct{} + +func (t *tlvType28) typeVal() Type { + return 28 +} + +type TlvType28 = *tlvType28 + +type tlvType29 struct{} + +func (t *tlvType29) typeVal() Type { + return 29 +} + +type TlvType29 = *tlvType29 + +type tlvType30 struct{} + +func (t *tlvType30) typeVal() Type { + return 30 +} + +type TlvType30 = *tlvType30 + +type tlvType31 struct{} + +func (t *tlvType31) typeVal() Type { + return 31 +} + +type TlvType31 = *tlvType31 + +type tlvType32 struct{} + +func (t *tlvType32) typeVal() Type { + return 32 +} + +type TlvType32 = *tlvType32 + +type tlvType33 struct{} + +func (t *tlvType33) typeVal() Type { + return 33 +} + +type TlvType33 = *tlvType33 + +type tlvType34 struct{} + +func (t *tlvType34) typeVal() Type { + return 34 +} + +type TlvType34 = *tlvType34 + +type tlvType35 struct{} + +func (t *tlvType35) typeVal() Type { + return 35 +} + +type TlvType35 = *tlvType35 + +type tlvType36 struct{} + +func (t *tlvType36) typeVal() Type { + return 36 +} + +type TlvType36 = *tlvType36 + +type tlvType37 struct{} + +func (t *tlvType37) typeVal() Type { + return 37 +} + +type TlvType37 = *tlvType37 + +type tlvType38 struct{} + +func (t *tlvType38) typeVal() Type { + return 38 +} + +type TlvType38 = *tlvType38 + +type tlvType39 struct{} + +func (t *tlvType39) typeVal() Type { + return 39 +} + +type TlvType39 = *tlvType39 + +type tlvType40 struct{} + +func (t *tlvType40) typeVal() Type { + return 40 +} + +type TlvType40 = *tlvType40 + +type tlvType41 struct{} + +func (t *tlvType41) typeVal() Type { + return 41 +} + +type TlvType41 = *tlvType41 + +type tlvType42 struct{} + +func (t *tlvType42) typeVal() Type { + return 42 +} + +type TlvType42 = *tlvType42 + +type tlvType43 struct{} + +func (t *tlvType43) typeVal() Type { + return 43 +} + +type TlvType43 = *tlvType43 + +type tlvType44 struct{} + +func (t *tlvType44) typeVal() Type { + return 44 +} + +type TlvType44 = *tlvType44 + +type tlvType45 struct{} + +func (t *tlvType45) typeVal() Type { + return 45 +} + +type TlvType45 = *tlvType45 + +type tlvType46 struct{} + +func (t *tlvType46) typeVal() Type { + return 46 +} + +type TlvType46 = *tlvType46 + +type tlvType47 struct{} + +func (t *tlvType47) typeVal() Type { + return 47 +} + +type TlvType47 = *tlvType47 + +type tlvType48 struct{} + +func (t *tlvType48) typeVal() Type { + return 48 +} + +type TlvType48 = *tlvType48 + +type tlvType49 struct{} + +func (t *tlvType49) typeVal() Type { + return 49 +} + +type TlvType49 = *tlvType49 + +type tlvType50 struct{} + +func (t *tlvType50) typeVal() Type { + return 50 +} + +type TlvType50 = *tlvType50 + +type tlvType51 struct{} + +func (t *tlvType51) typeVal() Type { + return 51 +} + +type TlvType51 = *tlvType51 + +type tlvType52 struct{} + +func (t *tlvType52) typeVal() Type { + return 52 +} + +type TlvType52 = *tlvType52 + +type tlvType53 struct{} + +func (t *tlvType53) typeVal() Type { + return 53 +} + +type TlvType53 = *tlvType53 + +type tlvType54 struct{} + +func (t *tlvType54) typeVal() Type { + return 54 +} + +type TlvType54 = *tlvType54 + +type tlvType55 struct{} + +func (t *tlvType55) typeVal() Type { + return 55 +} + +type TlvType55 = *tlvType55 + +type tlvType56 struct{} + +func (t *tlvType56) typeVal() Type { + return 56 +} + +type TlvType56 = *tlvType56 + +type tlvType57 struct{} + +func (t *tlvType57) typeVal() Type { + return 57 +} + +type TlvType57 = *tlvType57 + +type tlvType58 struct{} + +func (t *tlvType58) typeVal() Type { + return 58 +} + +type TlvType58 = *tlvType58 + +type tlvType59 struct{} + +func (t *tlvType59) typeVal() Type { + return 59 +} + +type TlvType59 = *tlvType59 + +type tlvType60 struct{} + +func (t *tlvType60) typeVal() Type { + return 60 +} + +type TlvType60 = *tlvType60 + +type tlvType61 struct{} + +func (t *tlvType61) typeVal() Type { + return 61 +} + +type TlvType61 = *tlvType61 + +type tlvType62 struct{} + +func (t *tlvType62) typeVal() Type { + return 62 +} + +type TlvType62 = *tlvType62 + +type tlvType63 struct{} + +func (t *tlvType63) typeVal() Type { + return 63 +} + +type TlvType63 = *tlvType63 + +type tlvType64 struct{} + +func (t *tlvType64) typeVal() Type { + return 64 +} + +type TlvType64 = *tlvType64 + +type tlvType65 struct{} + +func (t *tlvType65) typeVal() Type { + return 65 +} + +type TlvType65 = *tlvType65 + +type tlvType66 struct{} + +func (t *tlvType66) typeVal() Type { + return 66 +} + +type TlvType66 = *tlvType66 + +type tlvType67 struct{} + +func (t *tlvType67) typeVal() Type { + return 67 +} + +type TlvType67 = *tlvType67 + +type tlvType68 struct{} + +func (t *tlvType68) typeVal() Type { + return 68 +} + +type TlvType68 = *tlvType68 + +type tlvType69 struct{} + +func (t *tlvType69) typeVal() Type { + return 69 +} + +type TlvType69 = *tlvType69 + +type tlvType70 struct{} + +func (t *tlvType70) typeVal() Type { + return 70 +} + +type TlvType70 = *tlvType70 + +type tlvType71 struct{} + +func (t *tlvType71) typeVal() Type { + return 71 +} + +type TlvType71 = *tlvType71 + +type tlvType72 struct{} + +func (t *tlvType72) typeVal() Type { + return 72 +} + +type TlvType72 = *tlvType72 + +type tlvType73 struct{} + +func (t *tlvType73) typeVal() Type { + return 73 +} + +type TlvType73 = *tlvType73 + +type tlvType74 struct{} + +func (t *tlvType74) typeVal() Type { + return 74 +} + +type TlvType74 = *tlvType74 + +type tlvType75 struct{} + +func (t *tlvType75) typeVal() Type { + return 75 +} + +type TlvType75 = *tlvType75 + +type tlvType76 struct{} + +func (t *tlvType76) typeVal() Type { + return 76 +} + +type TlvType76 = *tlvType76 + +type tlvType77 struct{} + +func (t *tlvType77) typeVal() Type { + return 77 +} + +type TlvType77 = *tlvType77 + +type tlvType78 struct{} + +func (t *tlvType78) typeVal() Type { + return 78 +} + +type TlvType78 = *tlvType78 + +type tlvType79 struct{} + +func (t *tlvType79) typeVal() Type { + return 79 +} + +type TlvType79 = *tlvType79 + +type tlvType80 struct{} + +func (t *tlvType80) typeVal() Type { + return 80 +} + +type TlvType80 = *tlvType80 + +type tlvType81 struct{} + +func (t *tlvType81) typeVal() Type { + return 81 +} + +type TlvType81 = *tlvType81 + +type tlvType82 struct{} + +func (t *tlvType82) typeVal() Type { + return 82 +} + +type TlvType82 = *tlvType82 + +type tlvType83 struct{} + +func (t *tlvType83) typeVal() Type { + return 83 +} + +type TlvType83 = *tlvType83 + +type tlvType84 struct{} + +func (t *tlvType84) typeVal() Type { + return 84 +} + +type TlvType84 = *tlvType84 + +type tlvType85 struct{} + +func (t *tlvType85) typeVal() Type { + return 85 +} + +type TlvType85 = *tlvType85 + +type tlvType86 struct{} + +func (t *tlvType86) typeVal() Type { + return 86 +} + +type TlvType86 = *tlvType86 + +type tlvType87 struct{} + +func (t *tlvType87) typeVal() Type { + return 87 +} + +type TlvType87 = *tlvType87 + +type tlvType88 struct{} + +func (t *tlvType88) typeVal() Type { + return 88 +} + +type TlvType88 = *tlvType88 + +type tlvType89 struct{} + +func (t *tlvType89) typeVal() Type { + return 89 +} + +type TlvType89 = *tlvType89 + +type tlvType90 struct{} + +func (t *tlvType90) typeVal() Type { + return 90 +} + +type TlvType90 = *tlvType90 + +type tlvType91 struct{} + +func (t *tlvType91) typeVal() Type { + return 91 +} + +type TlvType91 = *tlvType91 + +type tlvType92 struct{} + +func (t *tlvType92) typeVal() Type { + return 92 +} + +type TlvType92 = *tlvType92 + +type tlvType93 struct{} + +func (t *tlvType93) typeVal() Type { + return 93 +} + +type TlvType93 = *tlvType93 + +type tlvType94 struct{} + +func (t *tlvType94) typeVal() Type { + return 94 +} + +type TlvType94 = *tlvType94 + +type tlvType95 struct{} + +func (t *tlvType95) typeVal() Type { + return 95 +} + +type TlvType95 = *tlvType95 + +type tlvType96 struct{} + +func (t *tlvType96) typeVal() Type { + return 96 +} + +type TlvType96 = *tlvType96 + +type tlvType97 struct{} + +func (t *tlvType97) typeVal() Type { + return 97 +} + +type TlvType97 = *tlvType97 + +type tlvType98 struct{} + +func (t *tlvType98) typeVal() Type { + return 98 +} + +type TlvType98 = *tlvType98 + +type tlvType99 struct{} + +func (t *tlvType99) typeVal() Type { + return 99 +} + +type TlvType99 = *tlvType99 + +type tlvType100 struct{} + +func (t *tlvType100) typeVal() Type { + return 100 +} + +type TlvType100 = *tlvType100 From 63e86b72c57193024972f4c5ddbf05d5e8824b21 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Fri, 27 Oct 2023 18:44:48 -0700 Subject: [PATCH 3/3] tlv: add new RecordT[T, V] utility type In this commit, we add a new type, `RecordT[T, V]` to reduce some of the common boiler plate for TLV types. This type lets you take either a primitive type, or an existing Record, and gain common methods used to create tlv streams. It also serves as extra type annotation as well, since wire structs can use this to wrap any existing type and gain the relevant record methods. This implementation ensures that the very definition of the field also binds the TLV type value. It does this by using the generated code to map a struct like TlvType1 to an actually Type like Type(1). --- tlv/go.mod | 4 +- tlv/go.sum | 8 +++- tlv/record_type.go | 85 +++++++++++++++++++++++++++++++++++++ tlv/record_type_test.go | 93 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+), 3 deletions(-) create mode 100644 tlv/record_type.go create mode 100644 tlv/record_type_test.go diff --git a/tlv/go.mod b/tlv/go.mod index 341a042d1..51ee83e25 100644 --- a/tlv/go.mod +++ b/tlv/go.mod @@ -5,7 +5,9 @@ require ( github.com/btcsuite/btcd/btcec/v2 v2.1.3 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.0 github.com/stretchr/testify v1.8.2 + golang.org/x/exp v0.0.0-20231006140011-7918f672742d ) require ( @@ -14,7 +16,7 @@ require ( 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.8.0 // indirect + golang.org/x/sys v0.13.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/tlv/go.sum b/tlv/go.sum index 53e545b07..60d32c136 100644 --- a/tlv/go.sum +++ b/tlv/go.sum @@ -19,6 +19,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lightningnetwork/lnd/fn v1.0.0 h1:I5VG9AD63mOQ89RMQEu7HRI1r68wn8yz539LoylUIKM= +github.com/lightningnetwork/lnd/fn v1.0.0/go.mod h1:XV+0vBXSnh3aUjskJUv58TOpsveiXQ+ac8rEnXZDGFc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= @@ -33,8 +35,10 @@ github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ 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= -golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= +golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= +golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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= diff --git a/tlv/record_type.go b/tlv/record_type.go new file mode 100644 index 000000000..1b25a6ade --- /dev/null +++ b/tlv/record_type.go @@ -0,0 +1,85 @@ +package tlv + +import ( + "github.com/btcsuite/btcd/btcec/v2" + "github.com/lightningnetwork/lnd/fn" + "golang.org/x/exp/constraints" +) + +// RecordT is a high-order type makes it easy to encode known "primitive" types +// as TLV records. +type RecordT[T TlvType, V any] struct { + // recordType is the type of the TLV record. + recordType T + + // val is the value of the underlying record. Go doesn't let us just + // embed the type param as a struct field, so we need an intermediate + // variable. + Val V +} + +// RecordProducerT is a type-aware wrapper around the normal RecordProducer +// interface. +type RecordProducerT[T any] interface { + RecordProducer + + // This is a non-interface type constraint that allows us to pass a + // concrete type as a type parameter rather than a pointer to the type + // that satisfies the Record interface. + *T +} + +// NewRecordT creates a new RecordT type from a given RecordProducer type. This +// is useful to wrap a given record in this utility type, which also serves as +// an extra type annotation. The underlying type of the record is retained. +func NewRecordT[T TlvType, K any, V RecordProducerT[K]]( + record K, +) RecordT[T, K] { + + return RecordT[T, K]{ + Val: record, + } +} + +// Primitive is a type constraint that capture the set of "primitive" types, +// which are the built in stdlib types, and type defs of those types +type Primitive interface { + constraints.Unsigned | ~[]byte | ~[32]byte | ~[33]byte | ~bool | + ~*btcec.PublicKey | ~[64]byte +} + +// NewPrimitiveRecord creates a new RecordT type from a given primitive type. +func NewPrimitiveRecord[T TlvType, V Primitive](val V) RecordT[T, V] { + return RecordT[T, V]{ + Val: val, + } +} + +// Record returns the underlying record interface for the record type. +func (t *RecordT[T, V]) Record() Record { + // Go doesn't allow type assertions on a type param, so to work around + // this, we'll convert to any, then do our type assertion. + tlvRecord, ok := any(&t.Val).(RecordProducer) + if !ok { + return MakePrimitiveRecord( + t.recordType.typeVal(), &t.Val, + ) + } + + return tlvRecord.Record() +} + +// OptionalRecordT is a high-order type that represents an optional TLV record. +// This can be used when a TLV record doesn't always need to be present (ok to +// be odd). +type OptionalRecordT[T TlvType, V any] struct { + fn.Option[RecordT[T, V]] +} + +// ZeroRecordT returns a zero value of the RecordT type. +func ZeroRecordT[T TlvType, V any]() RecordT[T, V] { + var v V + return RecordT[T, V]{ + Val: v, + } +} diff --git a/tlv/record_type_test.go b/tlv/record_type_test.go new file mode 100644 index 000000000..29d2fc7b6 --- /dev/null +++ b/tlv/record_type_test.go @@ -0,0 +1,93 @@ +package tlv + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/require" +) + +const ( + fakeCsvDelayType = 1 + + fakeIsCoolType = 2 +) + +type fakeWireMsg struct { + CsvDelay RecordT[TlvType1, uint16] + + IsCool RecordT[TlvType2, bool] +} + +// TestRecordTFromPrimitive tests the RecordT type. We should be able to create +// types of both record types, and also primitive types, and encode/decode them +// as normal. +func TestRecordTFromPrimitive(t *testing.T) { + t.Parallel() + + wireMsg := fakeWireMsg{ + CsvDelay: NewPrimitiveRecord[TlvType1](uint16(5)), + IsCool: NewPrimitiveRecord[TlvType2](true), + } + + encodeStream, err := NewStream( + wireMsg.CsvDelay.Record(), wireMsg.IsCool.Record(), + ) + require.NoError(t, err) + + var b bytes.Buffer + err = encodeStream.Encode(&b) + require.NoError(t, err) + + var newWireMsg fakeWireMsg + + decodeStream, err := NewStream( + newWireMsg.CsvDelay.Record(), + newWireMsg.IsCool.Record(), + ) + require.NoError(t, err) + + err = decodeStream.Decode(&b) + require.NoError(t, err) + + require.Equal(t, wireMsg, newWireMsg) +} + +type wireCsv uint16 + +func (w *wireCsv) Record() Record { + return MakeStaticRecord(fakeCsvDelayType, (*uint16)(w), 2, EUint16, DUint16) +} + +type coolWireMsg struct { + CsvDelay RecordT[TlvType1, wireCsv] +} + +// TestRecordTFromRecord tests that we can create a RecordT type from an +// existing record type and encode/decode as normal. +func TestRecordTFromRecord(t *testing.T) { + t.Parallel() + + val := wireCsv(5) + + wireMsg := coolWireMsg{ + CsvDelay: NewRecordT[TlvType1](val), + } + + encodeStream, err := NewStream(wireMsg.CsvDelay.Record()) + require.NoError(t, err) + + var b bytes.Buffer + err = encodeStream.Encode(&b) + require.NoError(t, err) + + var wireMsg2 coolWireMsg + + decodeStream, err := NewStream(wireMsg2.CsvDelay.Record()) + require.NoError(t, err) + + err = decodeStream.Decode(&b) + require.NoError(t, err) + + require.Equal(t, wireMsg, wireMsg2) +}