1
1
package graph
2
2
3
3
import (
4
- "bytes"
5
4
"fmt"
6
- "strings"
7
5
"sync"
8
6
"sync/atomic"
9
7
"time"
10
8
11
9
"github.com/btcsuite/btcd/btcec/v2"
12
- "github.com/btcsuite/btcd/btcutil"
13
- "github.com/btcsuite/btcd/chaincfg/chainhash"
14
10
"github.com/btcsuite/btcd/wire"
15
11
"github.com/go-errors/errors"
16
12
"github.com/lightningnetwork/lnd/batch"
17
13
"github.com/lightningnetwork/lnd/chainntnfs"
18
- "github.com/lightningnetwork/lnd/fn/v2"
19
14
graphdb "github.com/lightningnetwork/lnd/graph/db"
20
15
"github.com/lightningnetwork/lnd/graph/db/models"
21
- "github.com/lightningnetwork/lnd/input"
22
16
"github.com/lightningnetwork/lnd/kvdb"
23
17
"github.com/lightningnetwork/lnd/lnutils"
24
18
"github.com/lightningnetwork/lnd/lnwallet"
25
- "github.com/lightningnetwork/lnd/lnwallet/btcwallet"
26
- "github.com/lightningnetwork/lnd/lnwallet/chanvalidate"
27
19
"github.com/lightningnetwork/lnd/lnwire"
28
20
"github.com/lightningnetwork/lnd/multimutex"
29
21
"github.com/lightningnetwork/lnd/netann"
@@ -95,9 +87,9 @@ type Config struct {
95
87
// blocked by this job.
96
88
FirstTimePruneDelay time.Duration
97
89
98
- // AssumeChannelValid toggles whether the router will check for
99
- // spentness of channel outpoints. For neutrino, this saves long rescans
100
- // from blocking initial usage of the daemon .
90
+ // AssumeChannelValid toggles whether the builder will prune channels
91
+ // based on their spentness vs using the fact that they are considered
92
+ // zombies .
101
93
AssumeChannelValid bool
102
94
103
95
// StrictZombiePruning determines if we attempt to prune zombie
@@ -1008,9 +1000,9 @@ func (b *Builder) assertNodeAnnFreshness(node route.Vertex,
1008
1000
return nil
1009
1001
}
1010
1002
1011
- // addZombieEdge adds a channel that failed complete validation into the zombie
1003
+ // MarkZombieEdge adds a channel that failed complete validation into the zombie
1012
1004
// index so we can avoid having to re-validate it in the future.
1013
- func (b * Builder ) addZombieEdge (chanID uint64 ) error {
1005
+ func (b * Builder ) MarkZombieEdge (chanID uint64 ) error {
1014
1006
// If the edge fails validation we'll mark the edge itself as a zombie
1015
1007
// so we don't continue to request it. We use the "zero key" for both
1016
1008
// node pubkeys so this edge can't be resurrected.
@@ -1024,72 +1016,6 @@ func (b *Builder) addZombieEdge(chanID uint64) error {
1024
1016
return nil
1025
1017
}
1026
1018
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
-
1093
1019
// routingMsg couples a routing related routing topology update to the
1094
1020
// error channel.
1095
1021
type routingMsg struct {
@@ -1258,137 +1184,47 @@ func (b *Builder) addEdge(edge *models.ChannelEdgeInfo,
1258
1184
edge .ChannelID )
1259
1185
}
1260
1186
1261
- // If AssumeChannelValid is present, then we are unable to perform any
1262
- // of the expensive checks below, so we'll short-circuit our path
1263
- // straight to adding the edge to our graph. If the passed
1264
- // ShortChannelID is an alias, then we'll skip validation as it will
1265
- // not map to a legitimate tx. This is not a DoS vector as only we can
1266
- // add an alias ChannelAnnouncement from the gossiper.
1187
+ if err := b .cfg .Graph .AddChannelEdge (edge , op ... ); err != nil {
1188
+ return fmt .Errorf ("unable to add edge: %w" , err )
1189
+ }
1190
+
1191
+ b .stats .incNumEdgesDiscovered ()
1192
+
1193
+ // If AssumeChannelValid is present, of if the SCID is an alias, then
1194
+ // the gossiper would not have done the expensive work of fetching
1195
+ // a funding transaction and validating it. So we won't have the channel
1196
+ // capacity nor the funding script. So we just log and return here.
1267
1197
scid := lnwire .NewShortChanIDFromInt (edge .ChannelID )
1268
1198
if b .cfg .AssumeChannelValid || b .cfg .IsAlias (scid ) {
1269
- err := b .cfg .Graph .AddChannelEdge (edge , op ... )
1270
- if err != nil {
1271
- return fmt .Errorf ("unable to add edge: %w" , err )
1272
- }
1273
1199
log .Tracef ("New channel discovered! Link connects %x and %x " +
1274
1200
"with ChannelID(%v)" , edge .NodeKey1Bytes ,
1275
1201
edge .NodeKey2Bytes , edge .ChannelID )
1276
- b .stats .incNumEdgesDiscovered ()
1277
1202
1278
1203
return nil
1279
1204
}
1280
1205
1281
- // Before we can add the channel to the channel graph, we need to obtain
1282
- // the full funding outpoint that's encoded within the channel ID.
1283
- channelID := lnwire .NewShortChanIDFromInt (edge .ChannelID )
1284
- fundingTx , err := lnwallet .FetchFundingTxWrapper (
1285
- b .cfg .Chain , & channelID , b .quit ,
1286
- )
1287
- if err != nil {
1288
- //nolint:ll
1289
- //
1290
- // In order to ensure we don't erroneously mark a channel as a
1291
- // zombie due to an RPC failure, we'll attempt to string match
1292
- // for the relevant errors.
1293
- //
1294
- // * btcd:
1295
- // * https://github.com/btcsuite/btcd/blob/master/rpcserver.go#L1316
1296
- // * https://github.com/btcsuite/btcd/blob/master/rpcserver.go#L1086
1297
- // * bitcoind:
1298
- // * https://github.com/bitcoin/bitcoin/blob/7fcf53f7b4524572d1d0c9a5fdc388e87eb02416/src/rpc/blockchain.cpp#L770
1299
- // * https://github.com/bitcoin/bitcoin/blob/7fcf53f7b4524572d1d0c9a5fdc388e87eb02416/src/rpc/blockchain.cpp#L954
1300
- switch {
1301
- case strings .Contains (err .Error (), "not found" ):
1302
- fallthrough
1303
-
1304
- case strings .Contains (err .Error (), "out of range" ):
1305
- // If the funding transaction isn't found at all, then
1306
- // we'll mark the edge itself as a zombie so we don't
1307
- // continue to request it. We use the "zero key" for
1308
- // both node pubkeys so this edge can't be resurrected.
1309
- zErr := b .addZombieEdge (edge .ChannelID )
1310
- if zErr != nil {
1311
- return zErr
1312
- }
1313
-
1314
- default :
1315
- }
1316
-
1317
- return fmt .Errorf ("%w: %w" , ErrNoFundingTransaction , err )
1318
- }
1319
-
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
- )
1206
+ log .Debugf ("New channel discovered! Link connects %x and %x with " +
1207
+ "ChannelPoint(%v): chan_id=%v, capacity=%v" , edge .NodeKey1Bytes ,
1208
+ edge .NodeKey2Bytes , edge .ChannelPoint , edge .ChannelID ,
1209
+ edge .Capacity )
1210
+
1211
+ // Otherwise, then we expect the funding script to be present on the
1212
+ // edge since it would have been fetched when the gossiper validated the
1213
+ // announcement.
1214
+ fundingPkScript , err := edge .FundingScript .UnwrapOrErr (fmt .Errorf (
1215
+ "expected the funding transaction script to be set" ,
1216
+ ))
1326
1217
if err != nil {
1327
1218
return err
1328
1219
}
1329
1220
1330
- // Next we'll validate that this channel is actually well formed. If
1331
- // this check fails, then this channel either doesn't exist, or isn't
1332
- // the one that was meant to be created according to the passed channel
1333
- // proofs.
1334
- fundingPoint , err := chanvalidate .Validate (
1335
- & chanvalidate.Context {
1336
- Locator : & chanvalidate.ShortChanIDChanLocator {
1337
- ID : channelID ,
1338
- },
1339
- MultiSigPkScript : fundingPkScript ,
1340
- FundingTx : fundingTx ,
1341
- },
1342
- )
1343
- if err != nil {
1344
- // Mark the edge as a zombie so we won't try to re-validate it
1345
- // on start up.
1346
- if err := b .addZombieEdge (edge .ChannelID ); err != nil {
1347
- return err
1348
- }
1349
-
1350
- return fmt .Errorf ("%w: %w" , ErrInvalidFundingOutput , err )
1351
- }
1352
-
1353
- // Now that we have the funding outpoint of the channel, ensure
1354
- // that it hasn't yet been spent. If so, then this channel has
1355
- // been closed so we'll ignore it.
1356
- chanUtxo , err := b .cfg .Chain .GetUtxo (
1357
- fundingPoint , fundingPkScript , channelID .BlockHeight , b .quit ,
1358
- )
1359
- if err != nil {
1360
- if errors .Is (err , btcwallet .ErrOutputSpent ) {
1361
- zErr := b .addZombieEdge (edge .ChannelID )
1362
- if zErr != nil {
1363
- return zErr
1364
- }
1365
- }
1366
-
1367
- return fmt .Errorf ("%w: unable to fetch utxo for chan_id=%v, " +
1368
- "chan_point=%v: %w" , ErrChannelSpent , scid .ToUint64 (),
1369
- fundingPoint , err )
1370
- }
1371
-
1372
- // TODO(roasbeef): this is a hack, needs to be removed after commitment
1373
- // fees are dynamic.
1374
- edge .Capacity = btcutil .Amount (chanUtxo .Value )
1375
- edge .ChannelPoint = * fundingPoint
1376
- if err := b .cfg .Graph .AddChannelEdge (edge , op ... ); err != nil {
1377
- return errors .Errorf ("unable to add edge: %v" , err )
1378
- }
1379
-
1380
- log .Debugf ("New channel discovered! Link connects %x and %x with " +
1381
- "ChannelPoint(%v): chan_id=%v, capacity=%v" , edge .NodeKey1Bytes ,
1382
- edge .NodeKey2Bytes , fundingPoint , edge .ChannelID , edge .Capacity )
1383
- b .stats .incNumEdgesDiscovered ()
1384
-
1385
1221
// As a new edge has been added to the channel graph, we'll update the
1386
1222
// current UTXO filter within our active FilteredChainView so we are
1387
1223
// notified if/when this channel is closed.
1388
1224
filterUpdate := []graphdb.EdgePoint {
1389
1225
{
1390
1226
FundingPkScript : fundingPkScript ,
1391
- OutPoint : * fundingPoint ,
1227
+ OutPoint : edge . ChannelPoint ,
1392
1228
},
1393
1229
}
1394
1230
@@ -1630,6 +1466,16 @@ func (b *Builder) IsKnownEdge(chanID lnwire.ShortChannelID) bool {
1630
1466
return exists || isZombie
1631
1467
}
1632
1468
1469
+ // IsZombieEdge returns true if the graph source has marked the given channel ID
1470
+ // as a zombie edge.
1471
+ //
1472
+ // NOTE: This method is part of the ChannelGraphSource interface.
1473
+ func (b * Builder ) IsZombieEdge (chanID lnwire.ShortChannelID ) (bool , error ) {
1474
+ _ , _ , _ , isZombie , err := b .cfg .Graph .HasChannelEdge (chanID .ToUint64 ())
1475
+
1476
+ return isZombie , err
1477
+ }
1478
+
1633
1479
// IsStaleEdgePolicy returns true if the graph source has a channel edge for
1634
1480
// the passed channel ID (and flags) that have a more recent timestamp.
1635
1481
//
0 commit comments