lnwire: add OutPoint type with TLV encoding support
What changed, and why it matters
This commit adds a new helper data type for encoding transaction outpoints (a reference to a specific output of a Bitcoin transaction) in a special message format called TLV, which is used inside Lightning Network protocol messages. It is purely additive: it introduces a new wrapper type, its encoder/decoder, and unit tests. There is no bug fix, no change to existing logic, and no security-relevant behavior described in the commit or diff.
No security action needed. Review as normal code-quality/feature addition if this type is intended for use in a future protocol change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The commit creates lnwire/outpoint.go defining an OutPoint alias of wire.OutPoint that implements tlv.RecordProducer. It provides static-length (34-byte) TLV encoding via existing lnwire.WriteOutPoint and decoding via ReadElement, plus tests covering fixed, random, and zero-value round-trips. No existing code paths are modified, no vulnerability is patched, and no security context is supplied.
Changed components
lnwire/outpoint.golnwire/outpoint_test.goInspect captured patch +160 / −0
diff --git a/lnwire/outpoint.go b/lnwire/outpoint.go
new file mode 100644
index 0000000..ec893ee
--- /dev/null
+++ b/lnwire/outpoint.go
@@ -0,0 +1,53 @@
+package lnwire
+
+import (
+ "bytes"
+ "io"
+
+ "github.com/btcsuite/btcd/wire"
+ "github.com/lightningnetwork/lnd/tlv"
+)
+
+// OutPoint describes an outpoint of a transaction via its transaction hash
+// and the outpoint index. This is a thin wrapper around the wire.OutPoint to
+// provide TLV encoding/decoding.
+type OutPoint wire.OutPoint
+
+// Record returns a TLV record that can be used to encode/decode the OutPoint.
+//
+// NOTE: this is part of the tlv.RecordProducer interface.
+func (o *OutPoint) Record() tlv.Record {
+ return tlv.MakeStaticRecord(0, o, 34, outpointEncoder, outpointDecoder)
+}
+
+// outpointEncoder is a TLV encoder for OutPoint.
+func outpointEncoder(w io.Writer, val any, _ *[8]byte) error {
+ if v, ok := val.(*OutPoint); ok {
+ buf := bytes.NewBuffer(nil)
+ err := WriteOutPoint(buf, wire.OutPoint(*v))
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(buf.Bytes())
+
+ return err
+ }
+
+ return tlv.NewTypeForEncodingErr(val, "OutPoint")
+}
+
+// outpointDecoder is a TLV decoder for OutPoint.
+func outpointDecoder(r io.Reader, val any, _ *[8]byte, l uint64) error {
+ if v, ok := val.(*OutPoint); ok {
+ var o wire.OutPoint
+ if err := ReadElement(r, &o); err != nil {
+ return err
+ }
+
+ *v = OutPoint(o)
+
+ return nil
+ }
+
+ return tlv.NewTypeForDecodingErr(val, "OutPoint", l, 34)
+}
diff --git a/lnwire/outpoint_test.go b/lnwire/outpoint_test.go
new file mode 100644
index 0000000..b8ac1a7
--- /dev/null
+++ b/lnwire/outpoint_test.go
@@ -0,0 +1,107 @@
+package lnwire
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/btcsuite/btcd/chaincfg/chainhash"
+ "github.com/btcsuite/btcd/wire"
+ "github.com/lightningnetwork/lnd/tlv"
+ "github.com/stretchr/testify/require"
+ "pgregory.net/rapid"
+)
+
+// TestOutPointTLVEncoding tests the TLV encoding and decoding of OutPoint
+// structs using the Record interface.
+func TestOutPointTLVEncoding(t *testing.T) {
+ t.Parallel()
+
+ testOutPoint := OutPoint(wire.OutPoint{
+ Hash: chainhash.Hash{
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
+ 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ },
+ Index: 12345,
+ })
+
+ var extraData ExtraOpaqueData
+ require.NoError(t, extraData.PackRecords(&testOutPoint))
+
+ var decodedOutPoint OutPoint
+ tlvs, err := extraData.ExtractRecords(&decodedOutPoint)
+ require.NoError(t, err)
+
+ require.Contains(t, tlvs, tlv.Type(0))
+ require.Equal(t, testOutPoint, decodedOutPoint)
+}
+
+// TestOutPointRecord tests the TLV Record interface of OutPoint
+// by directly encoding and decoding using the Record method.
+func TestOutPointRecord(t *testing.T) {
+ t.Parallel()
+
+ testOutPoint := OutPoint(wire.OutPoint{
+ Hash: chainhash.Hash{
+ 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8,
+ 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0,
+ 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8,
+ 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0,
+ },
+ Index: 65535,
+ })
+
+ var buf bytes.Buffer
+ record := testOutPoint.Record()
+ require.NoError(t, record.Encode(&buf))
+
+ var decodedOutPoint OutPoint
+ decodedRecord := decodedOutPoint.Record()
+ require.NoError(t, decodedRecord.Decode(&buf, uint64(buf.Len())))
+
+ require.Equal(t, testOutPoint, decodedOutPoint)
+}
+
+// TestOutPointProperty uses property-based testing to verify that OutPoint
+// TLV encoding and decoding is correct for random OutPoint values.
+func TestOutPointProperty(t *testing.T) {
+ t.Parallel()
+
+ scenario := func(t *rapid.T) {
+ wireOutPoint := RandOutPoint(t)
+ lnOutPoint := OutPoint(wireOutPoint)
+
+ var buf bytes.Buffer
+ record := lnOutPoint.Record()
+ err := record.Encode(&buf)
+ require.NoError(t, err)
+
+ var decodedOutPoint OutPoint
+ decodedRecord := decodedOutPoint.Record()
+ err = decodedRecord.Decode(&buf, uint64(buf.Len()))
+ require.NoError(t, err)
+
+ require.Equal(t, lnOutPoint, decodedOutPoint)
+ require.Equal(t, wireOutPoint, wire.OutPoint(decodedOutPoint))
+ }
+
+ rapid.Check(t, scenario)
+}
+
+// TestOutPointZeroValues tests that OutPoint handles zero values correctly.
+func TestOutPointZeroValues(t *testing.T) {
+ t.Parallel()
+
+ zeroOutPoint := OutPoint(wire.OutPoint{})
+
+ var buf bytes.Buffer
+ record := zeroOutPoint.Record()
+ require.NoError(t, record.Encode(&buf))
+
+ var decodedOutPoint OutPoint
+ decodedRecord := decodedOutPoint.Record()
+ require.NoError(t, decodedRecord.Decode(&buf, uint64(buf.Len())))
+
+ require.Equal(t, zeroOutPoint, decodedOutPoint)
+}
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.