Skip to content

Commit 6334689

Browse files
committed
graph/db/models: add channel wire conversion helpers
Add ChannelAuthProofFromWireAnnouncement and ChannelAuthProofFromAnnounceSignatures for constructing auth proofs from wire messages. Add ChannelEdgeInfoFromWireAnnouncement for building a ChannelEdgeInfo from a ChannelAnnouncement interface. Also add ChannelEdgePolicyFromWireUpdate for constructing a ChannelEdgePolicy from a ChannelUpdate interface, and update the KV and SQL stores to use the new ChannelEdgeInfo.ChanProofBytes() accessor.
1 parent 4408c7a commit 6334689

7 files changed

Lines changed: 299 additions & 21 deletions

File tree

graph/db/kv_store.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3471,7 +3471,7 @@ func updateEdgePolicy(tx kvdb.RwTx, edge *models.ChannelEdgePolicy) (
34713471
// or second edge policy is being updated.
34723472
var fromNode, toNode []byte
34733473
var isUpdate1 bool
3474-
if edge.ChannelFlags&lnwire.ChanUpdateDirection == 0 {
3474+
if edge.IsNode1() {
34753475
fromNode = nodeInfo[:33]
34763476
toNode = nodeInfo[33:66]
34773477
isUpdate1 = true
@@ -5341,7 +5341,7 @@ func putChanEdgePolicy(edges kvdb.RwBucket, edge *models.ChannelEdgePolicy,
53415341

53425342
err = updateEdgePolicyDisabledIndex(
53435343
edges, edge.ChannelID,
5344-
edge.ChannelFlags&lnwire.ChanUpdateDirection > 0,
5344+
!edge.IsNode1(),
53455345
edge.IsDisabled(),
53465346
)
53475347
if err != nil {

graph/db/models/channel_auth_proof.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
package models
22

33
import (
4+
"errors"
5+
"fmt"
6+
47
"github.com/lightningnetwork/lnd/fn/v2"
58
"github.com/lightningnetwork/lnd/lnwire"
69
)
710

11+
var (
12+
// ErrV2AnnSigProofAssemblyPending is returned when trying to derive a
13+
// channel auth proof from v2 announce signatures. This will be
14+
// supported once v2 proof assembly from announce_signatures_2 halves is
15+
// implemented.
16+
ErrV2AnnSigProofAssemblyPending = errors.New("v2 announce signatures " +
17+
"proof assembly not yet implemented")
18+
)
19+
820
// ChannelAuthProof is the authentication proof (the signature portion) for a
921
// channel.
1022
//
@@ -115,3 +127,74 @@ func (c *ChannelAuthProof) BitcoinSig2() []byte {
115127
func (c *ChannelAuthProof) Sig() []byte {
116128
return c.Signature.UnwrapOr(nil)
117129
}
130+
131+
// ChannelAuthProofFromWireAnnouncement constructs a channel auth proof from a
132+
// wire channel announcement message.
133+
func ChannelAuthProofFromWireAnnouncement(
134+
ann lnwire.ChannelAnnouncement) (*ChannelAuthProof, error) {
135+
136+
switch ann := ann.(type) {
137+
case *lnwire.ChannelAnnouncement1:
138+
return NewV1ChannelAuthProof(
139+
ann.NodeSig1.ToSignatureBytes(),
140+
ann.NodeSig2.ToSignatureBytes(),
141+
ann.BitcoinSig1.ToSignatureBytes(),
142+
ann.BitcoinSig2.ToSignatureBytes(),
143+
), nil
144+
145+
case *lnwire.ChannelAnnouncement2:
146+
return NewV2ChannelAuthProof(
147+
ann.Signature.Val.ToSignatureBytes(),
148+
), nil
149+
150+
default:
151+
return nil, fmt.Errorf("unsupported channel announcement: %T",
152+
ann)
153+
}
154+
}
155+
156+
// ChannelAuthProofFromAnnounceSignatures derives a channel auth proof from two
157+
// opposing announce signatures messages.
158+
func ChannelAuthProofFromAnnounceSignatures(ann, oppositeAnn lnwire.AnnounceSignatures,
159+
isFirstNode bool) (*ChannelAuthProof, error) {
160+
161+
if ann == nil || oppositeAnn == nil {
162+
return nil, fmt.Errorf("announce signatures cannot be nil")
163+
}
164+
165+
if ann.GossipVersion() != oppositeAnn.GossipVersion() {
166+
return nil, fmt.Errorf("announce signatures version mismatch: %v "+
167+
"!= %v", ann.GossipVersion(), oppositeAnn.GossipVersion())
168+
}
169+
170+
switch annSig := ann.(type) {
171+
case *lnwire.AnnounceSignatures1:
172+
oppSig, ok := oppositeAnn.(*lnwire.AnnounceSignatures1)
173+
if !ok {
174+
return nil, fmt.Errorf("unexpected opposite announce "+
175+
"signatures type: %T", oppositeAnn)
176+
}
177+
178+
if isFirstNode {
179+
return NewV1ChannelAuthProof(
180+
annSig.NodeSignature.ToSignatureBytes(),
181+
oppSig.NodeSignature.ToSignatureBytes(),
182+
annSig.BitcoinSignature.ToSignatureBytes(),
183+
oppSig.BitcoinSignature.ToSignatureBytes(),
184+
), nil
185+
}
186+
187+
return NewV1ChannelAuthProof(
188+
oppSig.NodeSignature.ToSignatureBytes(),
189+
annSig.NodeSignature.ToSignatureBytes(),
190+
oppSig.BitcoinSignature.ToSignatureBytes(),
191+
annSig.BitcoinSignature.ToSignatureBytes(),
192+
), nil
193+
194+
case *lnwire.AnnounceSignatures2:
195+
return nil, ErrV2AnnSigProofAssemblyPending
196+
197+
default:
198+
return nil, fmt.Errorf("unsupported announce signatures: %T", ann)
199+
}
200+
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package models
2+
3+
import (
4+
"bytes"
5+
"testing"
6+
7+
"github.com/lightningnetwork/lnd/lnwire"
8+
"github.com/stretchr/testify/require"
9+
)
10+
11+
func TestChannelAuthProofFromAnnounceSignaturesV1(t *testing.T) {
12+
t.Parallel()
13+
14+
sig := func(b byte) lnwire.Sig {
15+
raw := bytes.Repeat([]byte{b}, 64)
16+
s, err := lnwire.NewSigFromWireECDSA(raw)
17+
require.NoError(t, err)
18+
return s
19+
}
20+
21+
ann1 := &lnwire.AnnounceSignatures1{
22+
NodeSignature: sig(1),
23+
BitcoinSignature: sig(2),
24+
}
25+
ann2 := &lnwire.AnnounceSignatures1{
26+
NodeSignature: sig(3),
27+
BitcoinSignature: sig(4),
28+
}
29+
30+
proof, err := ChannelAuthProofFromAnnounceSignatures(ann1, ann2, true)
31+
require.NoError(t, err)
32+
require.Equal(t, ann1.NodeSignature.ToSignatureBytes(), proof.NodeSig1())
33+
require.Equal(t, ann2.NodeSignature.ToSignatureBytes(), proof.NodeSig2())
34+
require.Equal(t, ann1.BitcoinSignature.ToSignatureBytes(),
35+
proof.BitcoinSig1())
36+
require.Equal(t, ann2.BitcoinSignature.ToSignatureBytes(),
37+
proof.BitcoinSig2())
38+
}
39+
40+
func TestChannelAuthProofFromAnnounceSignaturesV2Pending(t *testing.T) {
41+
t.Parallel()
42+
43+
ann := lnwire.NewAnnSigs2(
44+
lnwire.ChannelID{}, lnwire.ShortChannelID{}, lnwire.PartialSig{},
45+
)
46+
47+
proof, err := ChannelAuthProofFromAnnounceSignatures(ann, ann, true)
48+
require.ErrorIs(t, err, ErrV2AnnSigProofAssemblyPending)
49+
require.Nil(t, proof)
50+
}
51+
52+
func TestChannelAuthProofFromAnnounceSignaturesVersionMismatch(t *testing.T) {
53+
t.Parallel()
54+
55+
ann1 := &lnwire.AnnounceSignatures1{}
56+
ann2 := lnwire.NewAnnSigs2(
57+
lnwire.ChannelID{}, lnwire.ShortChannelID{}, lnwire.PartialSig{},
58+
)
59+
60+
proof, err := ChannelAuthProofFromAnnounceSignatures(ann1, ann2, true)
61+
require.Error(t, err)
62+
require.Contains(t, err.Error(), "version mismatch")
63+
require.Nil(t, proof)
64+
}

graph/db/models/channel_edge_info.go

Lines changed: 137 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/lightningnetwork/lnd/input"
1414
"github.com/lightningnetwork/lnd/lnwire"
1515
"github.com/lightningnetwork/lnd/routing/route"
16+
"github.com/lightningnetwork/lnd/tlv"
1617
)
1718

1819
// ChannelEdgeInfo represents a fully authenticated channel along with all its
@@ -256,6 +257,59 @@ func NewV2Channel(chanID uint64, chainHash chainhash.Hash, node1,
256257
return edge, nil
257258
}
258259

260+
// ChannelEdgeInfoFromWireAnnouncement constructs a ChannelEdgeInfo from a wire
261+
// channel announcement message.
262+
func ChannelEdgeInfoFromWireAnnouncement(msg lnwire.ChannelAnnouncement,
263+
proof *ChannelAuthProof) (*ChannelEdgeInfo, error) {
264+
265+
switch msg := msg.(type) {
266+
case *lnwire.ChannelAnnouncement1:
267+
return NewV1Channel(
268+
msg.ShortChannelID.ToUint64(), msg.ChainHash,
269+
msg.NodeID1, msg.NodeID2, &ChannelV1Fields{
270+
BitcoinKey1Bytes: msg.BitcoinKey1,
271+
BitcoinKey2Bytes: msg.BitcoinKey2,
272+
ExtraOpaqueData: msg.ExtraOpaqueData,
273+
},
274+
WithChanProof(proof), WithFeatures(msg.Features),
275+
)
276+
277+
case *lnwire.ChannelAnnouncement2:
278+
bitcoinKey1 := fn.MapOption(
279+
func(key [33]byte) route.Vertex {
280+
return key
281+
},
282+
)(msg.BitcoinKey1.ValOpt())
283+
bitcoinKey2 := fn.MapOption(
284+
func(key [33]byte) route.Vertex {
285+
return key
286+
},
287+
)(msg.BitcoinKey2.ValOpt())
288+
merkleRootHash := fn.MapOption(
289+
func(hash [32]byte) chainhash.Hash {
290+
return hash
291+
},
292+
)(msg.MerkleRootHash.ValOpt())
293+
294+
return NewV2Channel(
295+
msg.ShortChannelID.Val.ToUint64(), msg.ChainHash.Val,
296+
msg.NodeID1.Val, msg.NodeID2.Val, &ChannelV2Fields{
297+
BitcoinKey1Bytes: bitcoinKey1,
298+
BitcoinKey2Bytes: bitcoinKey2,
299+
MerkleRootHash: merkleRootHash,
300+
ExtraSignedFields: msg.ExtraSignedFields,
301+
},
302+
WithChanProof(proof), WithFeatures(&msg.Features.Val),
303+
WithCapacity(btcutil.Amount(msg.Capacity.Val)),
304+
WithChannelPoint(wire.OutPoint(msg.Outpoint.Val)),
305+
)
306+
307+
default:
308+
return nil, fmt.Errorf("unsupported channel announcement: %T",
309+
msg)
310+
}
311+
}
312+
259313
// NodeKey1 is the identity public key of the "first" node that was involved in
260314
// the creation of this channel. A node is considered "first" if the
261315
// lexicographical ordering the its serialized public key is "smaller" than
@@ -377,10 +431,10 @@ func (c *ChannelEdgeInfo) FundingPKScript() ([]byte, error) {
377431
}
378432
}
379433

380-
// ToChannelAnnouncement converts the ChannelEdgeInfo to a
434+
// toChannelAnnouncement1 converts the ChannelEdgeInfo to a
381435
// lnwire.ChannelAnnouncement1 message. Returns an error if AuthProof is nil
382436
// or if the version is not v1.
383-
func (c *ChannelEdgeInfo) ToChannelAnnouncement() (
437+
func (c *ChannelEdgeInfo) toChannelAnnouncement1() (
384438
*lnwire.ChannelAnnouncement1, error) {
385439

386440
// We currently only support v1 channel announcements.
@@ -452,3 +506,84 @@ func (c *ChannelEdgeInfo) ToChannelAnnouncement() (
452506

453507
return chanAnn, nil
454508
}
509+
510+
// ToWireAnnouncement converts the ChannelEdgeInfo to a version-aware wire
511+
// channel announcement.
512+
func (c *ChannelEdgeInfo) ToWireAnnouncement() (
513+
lnwire.ChannelAnnouncement, error) {
514+
515+
switch c.Version {
516+
case lnwire.GossipVersion1:
517+
return c.toChannelAnnouncement1()
518+
519+
case lnwire.GossipVersion2:
520+
return c.toChannelAnnouncement2()
521+
522+
default:
523+
return nil, fmt.Errorf("unsupported channel version: %d",
524+
c.Version)
525+
}
526+
}
527+
528+
// toChannelAnnouncement2 converts the ChannelEdgeInfo to a
529+
// lnwire.ChannelAnnouncement2 message.
530+
func (c *ChannelEdgeInfo) toChannelAnnouncement2() (
531+
*lnwire.ChannelAnnouncement2, error) {
532+
533+
// If there's no auth proof, we can't create a full channel
534+
// announcement.
535+
if c.AuthProof == nil {
536+
return nil, fmt.Errorf("cannot create channel announcement " +
537+
"without auth proof")
538+
}
539+
540+
if c.AuthProof.Version != lnwire.GossipVersion2 {
541+
return nil, fmt.Errorf("invalid channel auth proof version: %d",
542+
c.AuthProof.Version)
543+
}
544+
545+
sigBytes := c.AuthProof.Sig()
546+
if len(sigBytes) == 0 {
547+
return nil, fmt.Errorf("missing signature for v2 channel " +
548+
"announcement")
549+
}
550+
551+
sig, err := lnwire.NewSigFromSchnorrRawSignature(sigBytes)
552+
if err != nil {
553+
return nil, err
554+
}
555+
556+
features := lnwire.RawFeatureVector{}
557+
if c.Features != nil && c.Features.RawFeatureVector != nil {
558+
features = *c.Features.RawFeatureVector
559+
}
560+
561+
var chanAnn lnwire.ChannelAnnouncement2
562+
chanAnn.ChainHash.Val = c.ChainHash
563+
chanAnn.Features.Val = features
564+
chanAnn.ShortChannelID.Val = lnwire.NewShortChanIDFromInt(c.ChannelID)
565+
chanAnn.Capacity.Val = uint64(c.Capacity)
566+
chanAnn.NodeID1.Val = [33]byte(c.NodeKey1Bytes)
567+
chanAnn.NodeID2.Val = [33]byte(c.NodeKey2Bytes)
568+
chanAnn.Outpoint.Val = lnwire.OutPoint(c.ChannelPoint)
569+
chanAnn.Signature.Val = sig
570+
chanAnn.ExtraSignedFields = c.ExtraSignedFields
571+
572+
c.BitcoinKey1Bytes.WhenSome(func(key route.Vertex) {
573+
btcKey1 := tlv.ZeroRecordT[tlv.TlvType12, [33]byte]()
574+
btcKey1.Val = [33]byte(key)
575+
chanAnn.BitcoinKey1 = tlv.SomeRecordT(btcKey1)
576+
})
577+
c.BitcoinKey2Bytes.WhenSome(func(key route.Vertex) {
578+
btcKey2 := tlv.ZeroRecordT[tlv.TlvType14, [33]byte]()
579+
btcKey2.Val = [33]byte(key)
580+
chanAnn.BitcoinKey2 = tlv.SomeRecordT(btcKey2)
581+
})
582+
c.MerkleRootHash.WhenSome(func(hash chainhash.Hash) {
583+
merkleRoot := tlv.ZeroRecordT[tlv.TlvType16, [32]byte]()
584+
merkleRoot.Val = [32]byte(hash)
585+
chanAnn.MerkleRootHash = tlv.SomeRecordT(merkleRoot)
586+
})
587+
588+
return &chanAnn, nil
589+
}

graph/db/models/channel_edge_policy.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,11 +151,11 @@ func ChanEdgePolicyFromWire(scid uint64,
151151

152152
// IsNode1 returns true if this policy was announced by the channel's node_1.
153153
func (c *ChannelEdgePolicy) IsNode1() bool {
154-
if c.Version == lnwire.GossipVersion1 {
155-
return c.ChannelFlags&lnwire.ChanUpdateDirection == 0
154+
if c.Version == lnwire.GossipVersion2 {
155+
return !c.SecondPeer
156156
}
157157

158-
return !c.SecondPeer
158+
return c.ChannelFlags&lnwire.ChanUpdateDirection == 0
159159
}
160160

161161
// IsDisabled determines whether the edge has the disabled bit set.

graph/db/notifications.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -424,7 +424,7 @@ func (c *ChannelGraph) addToTopologyChange(ctx context.Context,
424424
// the second node.
425425
sourceNode := edgeInfo.NodeKey1
426426
connectingNode := edgeInfo.NodeKey2
427-
if m.ChannelFlags&lnwire.ChanUpdateDirection == 1 {
427+
if !m.IsNode1() {
428428
sourceNode = edgeInfo.NodeKey2
429429
connectingNode = edgeInfo.NodeKey1
430430
}
@@ -449,7 +449,7 @@ func (c *ChannelGraph) addToTopologyChange(ctx context.Context,
449449
FeeRate: m.FeeProportionalMillionths,
450450
AdvertisingNode: aNode,
451451
ConnectingNode: cNode,
452-
Disabled: m.ChannelFlags&lnwire.ChanUpdateDisabled != 0,
452+
Disabled: m.IsDisabled(),
453453
InboundFee: m.InboundFee,
454454
ExtraOpaqueData: m.ExtraOpaqueData,
455455
}

0 commit comments

Comments
 (0)