Skip to content

Commit 7853e36

Browse files
committed
graph+discovery: calculate funding tx script in gossiper
In preparation for an upcoming commit which will move all channel funding tx validation to the gossiper, we first move the helper method which helps build the expected funding transaction script based on the fields in the channel announcement. We will still want this script later on in the builder for updating the ChainView though, and so we pass this field along with the ChannelEdgeInfo. With this change, we can remove the TapscriptRoot field from the ChannelEdgeInfo since the only reason it was there was so that the builder could reconstruct the full funding script.
1 parent 8a07bb0 commit 7853e36

File tree

3 files changed

+89
-85
lines changed

3 files changed

+89
-85
lines changed

discovery/gossiper.go

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/lightningnetwork/lnd/graph"
2424
graphdb "github.com/lightningnetwork/lnd/graph/db"
2525
"github.com/lightningnetwork/lnd/graph/db/models"
26+
"github.com/lightningnetwork/lnd/input"
2627
"github.com/lightningnetwork/lnd/keychain"
2728
"github.com/lightningnetwork/lnd/lnpeer"
2829
"github.com/lightningnetwork/lnd/lnutils"
@@ -2619,6 +2620,7 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
26192620

26202621
// If there were any optional message fields provided, we'll include
26212622
// them in its serialized disk representation now.
2623+
var tapscriptRoot fn.Option[chainhash.Hash]
26222624
if nMsg.optionalMsgFields != nil {
26232625
if nMsg.optionalMsgFields.capacity != nil {
26242626
edge.Capacity = *nMsg.optionalMsgFields.capacity
@@ -2629,7 +2631,24 @@ func (d *AuthenticatedGossiper) handleChanAnnouncement(nMsg *networkMsg,
26292631
}
26302632

26312633
// Optional tapscript root for custom channels.
2632-
edge.TapscriptRoot = nMsg.optionalMsgFields.tapscriptRoot
2634+
tapscriptRoot = nMsg.optionalMsgFields.tapscriptRoot
2635+
}
2636+
2637+
// We only make use of the funding script later on during funding
2638+
// transaction validation if AssumeChannelValid is not true.
2639+
if !(d.cfg.AssumeChannelValid || d.cfg.IsAlias(scid)) {
2640+
fundingPkScript, err := makeFundingScript(
2641+
ann.BitcoinKey1[:], ann.BitcoinKey2[:], ann.Features,
2642+
tapscriptRoot,
2643+
)
2644+
if err != nil {
2645+
log.Errorf("Unable to make funding script %v", err)
2646+
nMsg.err <- err
2647+
2648+
return nil, false
2649+
}
2650+
2651+
edge.FundingScript = fn.Some(fundingPkScript)
26332652
}
26342653

26352654
log.Debugf("Adding edge for short_chan_id: %v", scid.ToUint64())
@@ -3598,3 +3617,57 @@ func (d *AuthenticatedGossiper) ShouldDisconnect(pubkey *btcec.PublicKey) (
35983617

35993618
return false, nil
36003619
}
3620+
3621+
// makeFundingScript is used to make the funding script for both segwit v0 and
3622+
// segwit v1 (taproot) channels.
3623+
func makeFundingScript(bitcoinKey1, bitcoinKey2 []byte,
3624+
features *lnwire.RawFeatureVector,
3625+
tapscriptRoot fn.Option[chainhash.Hash]) ([]byte, error) {
3626+
3627+
legacyFundingScript := func() ([]byte, error) {
3628+
witnessScript, err := input.GenMultiSigScript(
3629+
bitcoinKey1, bitcoinKey2,
3630+
)
3631+
if err != nil {
3632+
return nil, err
3633+
}
3634+
pkScript, err := input.WitnessScriptHash(witnessScript)
3635+
if err != nil {
3636+
return nil, err
3637+
}
3638+
3639+
return pkScript, nil
3640+
}
3641+
3642+
if features.IsEmpty() {
3643+
return legacyFundingScript()
3644+
}
3645+
3646+
chanFeatureBits := lnwire.NewFeatureVector(features, lnwire.Features)
3647+
if chanFeatureBits.HasFeature(
3648+
lnwire.SimpleTaprootChannelsOptionalStaging,
3649+
) {
3650+
3651+
pubKey1, err := btcec.ParsePubKey(bitcoinKey1)
3652+
if err != nil {
3653+
return nil, err
3654+
}
3655+
pubKey2, err := btcec.ParsePubKey(bitcoinKey2)
3656+
if err != nil {
3657+
return nil, err
3658+
}
3659+
3660+
fundingScript, _, err := input.GenTaprootFundingScript(
3661+
pubKey1, pubKey2, 0, tapscriptRoot,
3662+
)
3663+
if err != nil {
3664+
return nil, err
3665+
}
3666+
3667+
// TODO(roasbeef): add tapscript root to gossip v1.5
3668+
3669+
return fundingScript, nil
3670+
}
3671+
3672+
return legacyFundingScript()
3673+
}

graph/builder.go

Lines changed: 10 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package graph
22

33
import (
4-
"bytes"
54
"fmt"
65
"strings"
76
"sync"
@@ -10,15 +9,12 @@ import (
109

1110
"github.com/btcsuite/btcd/btcec/v2"
1211
"github.com/btcsuite/btcd/btcutil"
13-
"github.com/btcsuite/btcd/chaincfg/chainhash"
1412
"github.com/btcsuite/btcd/wire"
1513
"github.com/go-errors/errors"
1614
"github.com/lightningnetwork/lnd/batch"
1715
"github.com/lightningnetwork/lnd/chainntnfs"
18-
"github.com/lightningnetwork/lnd/fn/v2"
1916
graphdb "github.com/lightningnetwork/lnd/graph/db"
2017
"github.com/lightningnetwork/lnd/graph/db/models"
21-
"github.com/lightningnetwork/lnd/input"
2218
"github.com/lightningnetwork/lnd/kvdb"
2319
"github.com/lightningnetwork/lnd/lnutils"
2420
"github.com/lightningnetwork/lnd/lnwallet"
@@ -1024,72 +1020,6 @@ func (b *Builder) MarkZombieEdge(chanID uint64) error {
10241020
return nil
10251021
}
10261022

1027-
// makeFundingScript is used to make the funding script for both segwit v0 and
1028-
// segwit v1 (taproot) channels.
1029-
//
1030-
// TODO(roasbeef: export and use elsewhere?
1031-
func makeFundingScript(bitcoinKey1, bitcoinKey2 []byte, chanFeatures []byte,
1032-
tapscriptRoot fn.Option[chainhash.Hash]) ([]byte, error) {
1033-
1034-
legacyFundingScript := func() ([]byte, error) {
1035-
witnessScript, err := input.GenMultiSigScript(
1036-
bitcoinKey1, bitcoinKey2,
1037-
)
1038-
if err != nil {
1039-
return nil, err
1040-
}
1041-
pkScript, err := input.WitnessScriptHash(witnessScript)
1042-
if err != nil {
1043-
return nil, err
1044-
}
1045-
1046-
return pkScript, nil
1047-
}
1048-
1049-
if len(chanFeatures) == 0 {
1050-
return legacyFundingScript()
1051-
}
1052-
1053-
// In order to make the correct funding script, we'll need to parse the
1054-
// chanFeatures bytes into a feature vector we can interact with.
1055-
rawFeatures := lnwire.NewRawFeatureVector()
1056-
err := rawFeatures.Decode(bytes.NewReader(chanFeatures))
1057-
if err != nil {
1058-
return nil, fmt.Errorf("unable to parse chan feature "+
1059-
"bits: %w", err)
1060-
}
1061-
1062-
chanFeatureBits := lnwire.NewFeatureVector(
1063-
rawFeatures, lnwire.Features,
1064-
)
1065-
if chanFeatureBits.HasFeature(
1066-
lnwire.SimpleTaprootChannelsOptionalStaging,
1067-
) {
1068-
1069-
pubKey1, err := btcec.ParsePubKey(bitcoinKey1)
1070-
if err != nil {
1071-
return nil, err
1072-
}
1073-
pubKey2, err := btcec.ParsePubKey(bitcoinKey2)
1074-
if err != nil {
1075-
return nil, err
1076-
}
1077-
1078-
fundingScript, _, err := input.GenTaprootFundingScript(
1079-
pubKey1, pubKey2, 0, tapscriptRoot,
1080-
)
1081-
if err != nil {
1082-
return nil, err
1083-
}
1084-
1085-
// TODO(roasbeef): add tapscript root to gossip v1.5
1086-
1087-
return fundingScript, nil
1088-
}
1089-
1090-
return legacyFundingScript()
1091-
}
1092-
10931023
// routingMsg couples a routing related routing topology update to the
10941024
// error channel.
10951025
type routingMsg struct {
@@ -1278,6 +1208,16 @@ func (b *Builder) addEdge(edge *models.ChannelEdgeInfo,
12781208
return nil
12791209
}
12801210

1211+
// If AssumeChannelValid is false, then we expect the funding script to
1212+
// be present on the edge since it would have been fetched when the
1213+
// gossiper validated the announcement.
1214+
fundingPkScript, err := edge.FundingScript.UnwrapOrErr(fmt.Errorf(
1215+
"expected the funding transaction script to be set",
1216+
))
1217+
if err != nil {
1218+
return err
1219+
}
1220+
12811221
// Before we can add the channel to the channel graph, we need to obtain
12821222
// the full funding outpoint that's encoded within the channel ID.
12831223
channelID := lnwire.NewShortChanIDFromInt(edge.ChannelID)
@@ -1317,16 +1257,6 @@ func (b *Builder) addEdge(edge *models.ChannelEdgeInfo,
13171257
return fmt.Errorf("%w: %w", ErrNoFundingTransaction, err)
13181258
}
13191259

1320-
// Recreate witness output to be sure that declared in channel edge
1321-
// bitcoin keys and channel value corresponds to the reality.
1322-
fundingPkScript, err := makeFundingScript(
1323-
edge.BitcoinKey1Bytes[:], edge.BitcoinKey2Bytes[:],
1324-
edge.Features, edge.TapscriptRoot,
1325-
)
1326-
if err != nil {
1327-
return err
1328-
}
1329-
13301260
// Next we'll validate that this channel is actually well formed. If
13311261
// this check fails, then this channel either doesn't exist, or isn't
13321262
// the one that was meant to be created according to the passed channel

graph/db/models/channel_edge_info.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,11 @@ type ChannelEdgeInfo struct {
6363
// the value output in the outpoint that created this channel.
6464
Capacity btcutil.Amount
6565

66-
// TapscriptRoot is the optional Merkle root of the tapscript tree if
67-
// this channel is a taproot channel that also commits to a tapscript
68-
// tree (custom channel).
69-
TapscriptRoot fn.Option[chainhash.Hash]
66+
// FundingScript holds the script of the channel's funding transaction.
67+
//
68+
// NOTE: this is not currently persisted and so will not be present if
69+
// the edge object is loaded from the database.
70+
FundingScript fn.Option[[]byte]
7071

7172
// ExtraOpaqueData is the set of data that was appended to this
7273
// message, some of which we may not actually know how to iterate or

0 commit comments

Comments
 (0)