graph+discovery: move funding tx validation to gossiper

This commit is a pure refactor. We move the transaction validation
(existence, spentness, correctness) from the `graph.Builder` to the
gossiper since this is where all protocol level checks should happen.
All tests involved are also updated/moved.
This commit is contained in:
Elle Mouton committed 2025-02-12 15:48:08 +02:00
1 parent 39bb23ea5e
commit e5db0d6314
7 files changed
+499 -418

No files matched your search

+22 -116
View File
@@ -2,13 +2,11 @@ package graph
import (
"fmt"
"strings"
"sync"
"sync/atomic"
"time"
"github.com/btcsuite/btcd/btcec/v2"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/wire"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/batch"
@@ -18,8 +16,6 @@ import (
"github.com/lightningnetwork/lnd/kvdb"
"github.com/lightningnetwork/lnd/lnutils"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwallet/chanvalidate"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/multimutex"
"github.com/lightningnetwork/lnd/netann"
@@ -91,9 +87,9 @@ type Config struct {
// blocked by this job.
FirstTimePruneDelay time.Duration
// AssumeChannelValid toggles whether the router will check for
// spentness of channel outpoints. For neutrino, this saves long rescans
// from blocking initial usage of the daemon.
// AssumeChannelValid toggles whether the builder will prune channels
// based on their spentness vs using the fact that they are considered
// zombies.
AssumeChannelValid bool
// StrictZombiePruning determines if we attempt to prune zombie
@@ -1188,29 +1184,33 @@ func (b *Builder) addEdge(edge *models.ChannelEdgeInfo,
edge.ChannelID)
}
// If AssumeChannelValid is present, then we are unable to perform any
// of the expensive checks below, so we'll short-circuit our path
// straight to adding the edge to our graph. If the passed
// ShortChannelID is an alias, then we'll skip validation as it will
// not map to a legitimate tx. This is not a DoS vector as only we can
// add an alias ChannelAnnouncement from the gossiper.
if err := b.cfg.Graph.AddChannelEdge(edge, op...); err != nil {
return fmt.Errorf("unable to add edge: %w", err)
}
b.stats.incNumEdgesDiscovered()
// If AssumeChannelValid is present, of if the SCID is an alias, then
// the gossiper would not have done the expensive work of fetching
// a funding transaction and validating it. So we won't have the channel
// capacity nor the funding script. So we just log and return here.
scid := lnwire.NewShortChanIDFromInt(edge.ChannelID)
if b.cfg.AssumeChannelValid || b.cfg.IsAlias(scid) {
err := b.cfg.Graph.AddChannelEdge(edge, op...)
if err != nil {
return fmt.Errorf("unable to add edge: %w", err)
}
log.Tracef("New channel discovered! Link connects %x and %x "+
"with ChannelID(%v)", edge.NodeKey1Bytes,
edge.NodeKey2Bytes, edge.ChannelID)
b.stats.incNumEdgesDiscovered()
return nil
}
// If AssumeChannelValid is false, then we expect the funding script to
// be present on the edge since it would have been fetched when the
// gossiper validated the announcement.
log.Debugf("New channel discovered! Link connects %x and %x with "+
"ChannelPoint(%v): chan_id=%v, capacity=%v", edge.NodeKey1Bytes,
edge.NodeKey2Bytes, edge.ChannelPoint, edge.ChannelID,
edge.Capacity)
// Otherwise, then we expect the funding script to be present on the
// edge since it would have been fetched when the gossiper validated the
// announcement.
fundingPkScript, err := edge.FundingScript.UnwrapOrErr(fmt.Errorf(
"expected the funding transaction script to be set",
))
@@ -1218,107 +1218,13 @@ func (b *Builder) addEdge(edge *models.ChannelEdgeInfo,
return err
}
// Before we can add the channel to the channel graph, we need to obtain
// the full funding outpoint that's encoded within the channel ID.
channelID := lnwire.NewShortChanIDFromInt(edge.ChannelID)
fundingTx, err := lnwallet.FetchFundingTxWrapper(
b.cfg.Chain, &channelID, b.quit,
)
if err != nil {
//nolint:ll
//
// In order to ensure we don't erroneously mark a channel as a
// zombie due to an RPC failure, we'll attempt to string match
// for the relevant errors.
//
// * btcd:
// * https://github.com/btcsuite/btcd/blob/master/rpcserver.go#L1316
// * https://github.com/btcsuite/btcd/blob/master/rpcserver.go#L1086
// * bitcoind:
// * https://github.com/bitcoin/bitcoin/blob/7fcf53f7b4524572d1d0c9a5fdc388e87eb02416/src/rpc/blockchain.cpp#L770
// * https://github.com/bitcoin/bitcoin/blob/7fcf53f7b4524572d1d0c9a5fdc388e87eb02416/src/rpc/blockchain.cpp#L954
switch {
case strings.Contains(err.Error(), "not found"):
fallthrough
case strings.Contains(err.Error(), "out of range"):
// If the funding transaction isn't found at all, then
// we'll mark the edge itself as a zombie so we don't
// continue to request it. We use the "zero key" for
// both node pubkeys so this edge can't be resurrected.
zErr := b.MarkZombieEdge(edge.ChannelID)
if zErr != nil {
return zErr
}
default:
}
return fmt.Errorf("%w: %w", ErrNoFundingTransaction, err)
}
// Next we'll validate that this channel is actually well formed. If
// this check fails, then this channel either doesn't exist, or isn't
// the one that was meant to be created according to the passed channel
// proofs.
fundingPoint, err := chanvalidate.Validate(
&chanvalidate.Context{
Locator: &chanvalidate.ShortChanIDChanLocator{
ID: channelID,
},
MultiSigPkScript: fundingPkScript,
FundingTx: fundingTx,
},
)
if err != nil {
// Mark the edge as a zombie so we won't try to re-validate it
// on start up.
if err := b.MarkZombieEdge(edge.ChannelID); err != nil {
return err
}
return fmt.Errorf("%w: %w", ErrInvalidFundingOutput, err)
}
// Now that we have the funding outpoint of the channel, ensure
// that it hasn't yet been spent. If so, then this channel has
// been closed so we'll ignore it.
chanUtxo, err := b.cfg.Chain.GetUtxo(
fundingPoint, fundingPkScript, channelID.BlockHeight, b.quit,
)
if err != nil {
if errors.Is(err, btcwallet.ErrOutputSpent) {
zErr := b.MarkZombieEdge(edge.ChannelID)
if zErr != nil {
return zErr
}
}
return fmt.Errorf("%w: unable to fetch utxo for chan_id=%v, "+
"chan_point=%v: %w", ErrChannelSpent, scid.ToUint64(),
fundingPoint, err)
}
// TODO(roasbeef): this is a hack, needs to be removed after commitment
// fees are dynamic.
edge.Capacity = btcutil.Amount(chanUtxo.Value)
edge.ChannelPoint = *fundingPoint
if err := b.cfg.Graph.AddChannelEdge(edge, op...); err != nil {
return errors.Errorf("unable to add edge: %v", err)
}
log.Debugf("New channel discovered! Link connects %x and %x with "+
"ChannelPoint(%v): chan_id=%v, capacity=%v", edge.NodeKey1Bytes,
edge.NodeKey2Bytes, fundingPoint, edge.ChannelID, edge.Capacity)
b.stats.incNumEdgesDiscovered()
// As a new edge has been added to the channel graph, we'll update the
// current UTXO filter within our active FilteredChainView so we are
// notified if/when this channel is closed.
filterUpdate := []graphdb.EdgePoint{
{
FundingPkScript: fundingPkScript,
OutPoint: *fundingPoint,
OutPoint: edge.ChannelPoint,
},
}
+43 -133
View File
@@ -20,6 +20,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/go-errors/errors"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/fn/v2"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/htlcswitch"
@@ -52,8 +53,8 @@ func TestAddProof(t *testing.T) {
// In order to be able to add the edge we should have a valid funding
// UTXO within the blockchain.
fundingTx, _, chanID, err := createChannelEdge(
ctx, bitcoinKey1.SerializeCompressed(),
script, fundingTx, _, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(), 100, 0,
)
require.NoError(t, err, "unable create channel edge")
@@ -68,6 +69,7 @@ func TestAddProof(t *testing.T) {
NodeKey1Bytes: node1.PubKeyBytes,
NodeKey2Bytes: node2.PubKeyBytes,
AuthProof: nil,
FundingScript: fn.Some(script),
}
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
@@ -136,8 +138,8 @@ func TestIgnoreChannelEdgePolicyForUnknownChannel(t *testing.T) {
// Add the edge between the two unknown nodes to the graph, and check
// that the nodes are found after the fact.
fundingTx, _, chanID, err := createChannelEdge(
ctx, bitcoinKey1.SerializeCompressed(),
script, fundingTx, _, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(), 10000, 500,
)
require.NoError(t, err, "unable to create channel edge")
@@ -153,6 +155,7 @@ func TestIgnoreChannelEdgePolicyForUnknownChannel(t *testing.T) {
BitcoinKey1Bytes: pub1,
BitcoinKey2Bytes: pub2,
AuthProof: nil,
FundingScript: fn.Some(script),
}
edgePolicy := &models.ChannelEdgePolicy{
SigBytes: testSig.Serialize(),
@@ -199,22 +202,25 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
var chanID2 uint64
// Create 10 common blocks, confirming chanID1.
var fundingScript1 []byte
for i := uint32(1); i <= 10; i++ {
block := &wire.MsgBlock{
Transactions: []*wire.MsgTx{},
}
height := startingBlockHeight + i
if i == 5 {
fundingTx, _, chanID, err := createChannelEdge(ctx,
script, fundingTx, _, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(),
chanValue, height)
chanValue, height,
)
if err != nil {
t.Fatalf("unable create channel edge: %v", err)
}
block.Transactions = append(block.Transactions,
fundingTx)
chanID1 = chanID.ToUint64()
fundingScript1 = script
}
ctx.chain.addBlock(block, height, rand.Uint32())
ctx.chain.setBestBlock(int32(height))
@@ -229,13 +235,14 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
require.NoError(t, err, "unable to ge best block")
// Create 10 blocks on the minority chain, confirming chanID2.
var fundingScript2 []byte
for i := uint32(1); i <= 10; i++ {
block := &wire.MsgBlock{
Transactions: []*wire.MsgTx{},
}
height := uint32(forkHeight) + i
if i == 5 {
fundingTx, _, chanID, err := createChannelEdge(ctx,
script, fundingTx, _, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(),
chanValue, height)
@@ -245,6 +252,7 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
block.Transactions = append(block.Transactions,
fundingTx)
chanID2 = chanID.ToUint64()
fundingScript2 = script
}
ctx.chain.addBlock(block, height, rand.Uint32())
ctx.chain.setBestBlock(int32(height))
@@ -269,6 +277,7 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
BitcoinSig1Bytes: testSig.Serialize(),
BitcoinSig2Bytes: testSig.Serialize(),
},
FundingScript: fn.Some(fundingScript1),
}
copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
@@ -287,6 +296,7 @@ func TestWakeUpOnStaleBranch(t *testing.T) {
BitcoinSig1Bytes: testSig.Serialize(),
BitcoinSig2Bytes: testSig.Serialize(),
},
FundingScript: fn.Some(fundingScript2),
}
copy(edge2.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
copy(edge2.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
@@ -406,10 +416,11 @@ func TestDisconnectedBlocks(t *testing.T) {
}
height := startingBlockHeight + i
if i == 5 {
fundingTx, _, chanID, err := createChannelEdge(ctx,
_, fundingTx, _, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(),
chanValue, height)
chanValue, height,
)
if err != nil {
t.Fatalf("unable create channel edge: %v", err)
}
@@ -437,10 +448,11 @@ func TestDisconnectedBlocks(t *testing.T) {
}
height := uint32(forkHeight) + i
if i == 5 {
fundingTx, _, chanID, err := createChannelEdge(ctx,
_, fundingTx, _, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(),
chanValue, height)
chanValue, height,
)
if err != nil {
t.Fatalf("unable create channel edge: %v", err)
}
@@ -474,6 +486,7 @@ func TestDisconnectedBlocks(t *testing.T) {
BitcoinSig1Bytes: testSig.Serialize(),
BitcoinSig2Bytes: testSig.Serialize(),
},
FundingScript: fn.Some([]byte{}),
}
copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
@@ -494,6 +507,7 @@ func TestDisconnectedBlocks(t *testing.T) {
BitcoinSig1Bytes: testSig.Serialize(),
BitcoinSig2Bytes: testSig.Serialize(),
},
FundingScript: fn.Some([]byte{}),
}
copy(edge2.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
copy(edge2.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
@@ -595,10 +609,11 @@ func TestChansClosedOfflinePruneGraph(t *testing.T) {
Transactions: []*wire.MsgTx{},
}
nextHeight := startingBlockHeight + 1
fundingTx1, chanUTXO, chanID1, err := createChannelEdge(ctx,
script, fundingTx1, chanUTXO, chanID1, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(),
chanValue, uint32(nextHeight))
chanValue, uint32(nextHeight),
)
require.NoError(t, err, "unable create channel edge")
block102.Transactions = append(block102.Transactions, fundingTx1)
ctx.chain.addBlock(block102, uint32(nextHeight), rand.Uint32())
@@ -622,6 +637,9 @@ func TestChansClosedOfflinePruneGraph(t *testing.T) {
BitcoinSig1Bytes: testSig.Serialize(),
BitcoinSig2Bytes: testSig.Serialize(),
},
ChannelPoint: *chanUTXO,
Capacity: chanValue,
FundingScript: fn.Some(script),
}
copy(edge1.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
copy(edge1.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
@@ -1007,10 +1025,11 @@ func TestIsStaleNode(t *testing.T) {
copy(pub1[:], priv1.PubKey().SerializeCompressed())
copy(pub2[:], priv2.PubKey().SerializeCompressed())
fundingTx, _, chanID, err := createChannelEdge(ctx,
script, fundingTx, _, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(),
10000, 500)
10000, 500,
)
require.NoError(t, err, "unable to create channel edge")
fundingBlock := &wire.MsgBlock{
Transactions: []*wire.MsgTx{fundingTx},
@@ -1024,6 +1043,7 @@ func TestIsStaleNode(t *testing.T) {
BitcoinKey1Bytes: pub1,
BitcoinKey2Bytes: pub2,
AuthProof: nil,
FundingScript: fn.Some(script),
}
if err := ctx.builder.AddEdge(edge); err != nil {
t.Fatalf("unable to add edge: %v", err)
@@ -1083,10 +1103,11 @@ func TestIsKnownEdge(t *testing.T) {
copy(pub1[:], priv1.PubKey().SerializeCompressed())
copy(pub2[:], priv2.PubKey().SerializeCompressed())
fundingTx, _, chanID, err := createChannelEdge(ctx,
script, fundingTx, _, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(),
10000, 500)
10000, 500,
)
require.NoError(t, err, "unable to create channel edge")
fundingBlock := &wire.MsgBlock{
Transactions: []*wire.MsgTx{fundingTx},
@@ -1100,6 +1121,7 @@ func TestIsKnownEdge(t *testing.T) {
BitcoinKey1Bytes: pub1,
BitcoinKey2Bytes: pub2,
AuthProof: nil,
FundingScript: fn.Some(script),
}
if err := ctx.builder.AddEdge(edge); err != nil {
t.Fatalf("unable to add edge: %v", err)
@@ -1129,10 +1151,11 @@ func TestIsStaleEdgePolicy(t *testing.T) {
copy(pub1[:], priv1.PubKey().SerializeCompressed())
copy(pub2[:], priv2.PubKey().SerializeCompressed())
fundingTx, _, chanID, err := createChannelEdge(ctx,
script, fundingTx, _, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(),
10000, 500)
10000, 500,
)
require.NoError(t, err, "unable to create channel edge")
fundingBlock := &wire.MsgBlock{
Transactions: []*wire.MsgTx{fundingTx},
@@ -1156,6 +1179,7 @@ func TestIsStaleEdgePolicy(t *testing.T) {
BitcoinKey1Bytes: pub1,
BitcoinKey2Bytes: pub2,
AuthProof: nil,
FundingScript: fn.Some(script),
}
if err := ctx.builder.AddEdge(edge); err != nil {
t.Fatalf("unable to add edge: %v", err)
@@ -1210,120 +1234,6 @@ func TestIsStaleEdgePolicy(t *testing.T) {
}
}
// edgeCreationModifier is an enum-like type used to modify steps that are
// skipped when creating a channel in the test context.
type edgeCreationModifier uint8
const (
// edgeCreationNoFundingTx is used to skip adding the funding
// transaction of an edge to the chain.
edgeCreationNoFundingTx edgeCreationModifier = iota
// edgeCreationNoUTXO is used to skip adding the UTXO of a channel to
// the UTXO set.
edgeCreationNoUTXO
// edgeCreationBadScript is used to create the edge, but use the wrong
// scrip which should cause it to fail output validation.
edgeCreationBadScript
)
// newChannelEdgeInfo is a helper function used to create a new channel edge,
// possibly skipping adding it to parts of the chain/state as well.
func newChannelEdgeInfo(t *testing.T, ctx *testCtx, fundingHeight uint32,
ecm edgeCreationModifier) (*models.ChannelEdgeInfo, error) {
node1 := createTestNode(t)
node2 := createTestNode(t)
fundingTx, _, chanID, err := createChannelEdge(
ctx, bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(), 100, fundingHeight,
)
if err != nil {
return nil, fmt.Errorf("unable to create edge: %w", err)
}
edge := &models.ChannelEdgeInfo{
ChannelID: chanID.ToUint64(),
NodeKey1Bytes: node1.PubKeyBytes,
NodeKey2Bytes: node2.PubKeyBytes,
}
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
if ecm == edgeCreationNoFundingTx {
return edge, nil
}
fundingBlock := &wire.MsgBlock{
Transactions: []*wire.MsgTx{fundingTx},
}
ctx.chain.addBlock(fundingBlock, chanID.BlockHeight, chanID.BlockHeight)
if ecm == edgeCreationNoUTXO {
ctx.chain.delUtxo(wire.OutPoint{
Hash: fundingTx.TxHash(),
})
}
if ecm == edgeCreationBadScript {
fundingTx.TxOut[0].PkScript[0] ^= 1
}
return edge, nil
}
func assertChanChainRejection(t *testing.T, ctx *testCtx,
edge *models.ChannelEdgeInfo, expectedErr error) {
t.Helper()
err := ctx.builder.AddEdge(edge)
require.ErrorIs(t, err, expectedErr)
// This channel should now be present in the zombie channel index.
_, _, _, isZombie, err := ctx.graph.HasChannelEdge(
edge.ChannelID,
)
require.Nil(t, err)
require.True(t, isZombie, "edge should be marked as zombie")
}
// TestChannelOnChainRejectionZombie tests that if we fail validating a channel
// due to some sort of on-chain rejection (no funding transaction, or invalid
// UTXO), then we'll mark the channel as a zombie.
func TestChannelOnChainRejectionZombie(t *testing.T) {
t.Parallel()
ctx := createTestCtxSingleNode(t, 0)
// To start, we'll make an edge for the channel, but we won't add the
// funding transaction to the mock blockchain, which should cause the
// validation to fail below.
edge, err := newChannelEdgeInfo(t, ctx, 1, edgeCreationNoFundingTx)
require.Nil(t, err)
// We expect this to fail as the transaction isn't present in the
// chain (nor the block).
assertChanChainRejection(t, ctx, edge, ErrNoFundingTransaction)
// Next, we'll make another channel edge, but actually add it to the
// graph this time.
edge, err = newChannelEdgeInfo(t, ctx, 2, edgeCreationNoUTXO)
require.Nil(t, err)
// Instead now, we'll remove it from the set of UTXOs which should
// cause the spentness validation to fail.
assertChanChainRejection(t, ctx, edge, ErrChannelSpent)
// If we cause the funding transaction the chain to fail validation, we
// should see similar behavior.
edge, err = newChannelEdgeInfo(t, ctx, 3, edgeCreationBadScript)
require.Nil(t, err)
assertChanChainRejection(t, ctx, edge, ErrInvalidFundingOutput)
}
// TestBlockDifferenceFix tests if when the router is behind on blocks, the
// router catches up to the best block head.
func TestBlockDifferenceFix(t *testing.T) {
-19
View File
@@ -2,25 +2,6 @@ package graph
import "github.com/go-errors/errors"
var (
// ErrNoFundingTransaction is returned when we are unable to find the
// funding transaction described by the short channel ID on chain.
ErrNoFundingTransaction = errors.New(
"unable to find the funding transaction",
)
// ErrInvalidFundingOutput is returned if the channel funding output
// fails validation.
ErrInvalidFundingOutput = errors.New(
"channel funding output validation failed",
)
// ErrChannelSpent is returned when we go to validate a channel, but
// the purported funding output has actually already been spent on
// chain.
ErrChannelSpent = errors.New("channel output has been spent")
)
// ErrorCode is used to represent the various errors that can occur within this
// package.
type ErrorCode uint8
+34 -52
View File
@@ -17,6 +17,7 @@ import (
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/chainntnfs"
"github.com/lightningnetwork/lnd/fn/v2"
graphdb "github.com/lightningnetwork/lnd/graph/db"
"github.com/lightningnetwork/lnd/graph/db/models"
"github.com/lightningnetwork/lnd/htlcswitch"
@@ -24,7 +25,6 @@ import (
"github.com/lightningnetwork/lnd/kvdb"
lnmock "github.com/lightningnetwork/lnd/lntest/mock"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwallet/btcwallet"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/chainview"
"github.com/lightningnetwork/lnd/routing/route"
@@ -126,18 +126,18 @@ func randEdgePolicy(chanID *lnwire.ShortChannelID,
}, nil
}
func createChannelEdge(ctx *testCtx, bitcoinKey1, bitcoinKey2 []byte,
chanValue btcutil.Amount, fundingHeight uint32) (*wire.MsgTx, *wire.OutPoint,
*lnwire.ShortChannelID, error) {
func createChannelEdge(bitcoinKey1, bitcoinKey2 []byte,
chanValue btcutil.Amount, fundingHeight uint32) ([]byte, *wire.MsgTx,
*wire.OutPoint, *lnwire.ShortChannelID, error) {
fundingTx := wire.NewMsgTx(2)
_, tx, err := input.GenFundingPkScript(
script, tx, err := input.GenFundingPkScript(
bitcoinKey1,
bitcoinKey2,
int64(chanValue),
)
if err != nil {
return nil, nil, nil, err
return nil, nil, nil, nil, err
}
fundingTx.TxOut = append(fundingTx.TxOut, tx)
@@ -146,9 +146,6 @@ func createChannelEdge(ctx *testCtx, bitcoinKey1, bitcoinKey2 []byte,
Index: 0,
}
// With the utxo constructed, we'll mark it as closed.
ctx.chain.addUtxo(chanUtxo, tx)
// Our fake channel will be "confirmed" at height 101.
chanID := &lnwire.ShortChannelID{
BlockHeight: fundingHeight,
@@ -156,16 +153,16 @@ func createChannelEdge(ctx *testCtx, bitcoinKey1, bitcoinKey2 []byte,
TxPosition: 0,
}
return fundingTx, &chanUtxo, chanID, nil
return script, fundingTx, &chanUtxo, chanID, nil
}
type mockChain struct {
lnwallet.BlockChainIO
blocks map[chainhash.Hash]*wire.MsgBlock
blockIndex map[uint32]chainhash.Hash
blockHeightIndex map[chainhash.Hash]uint32
utxos map[wire.OutPoint]wire.TxOut
bestHeight int32
sync.RWMutex
@@ -179,7 +176,6 @@ func newMockChain(currentHeight uint32) *mockChain {
chain := &mockChain{
bestHeight: int32(currentHeight),
blocks: make(map[chainhash.Hash]*wire.MsgBlock),
utxos: make(map[wire.OutPoint]wire.TxOut),
blockIndex: make(map[uint32]chainhash.Hash),
blockHeightIndex: make(map[chainhash.Hash]uint32),
}
@@ -213,10 +209,6 @@ func (m *mockChain) GetBestBlock() (*chainhash.Hash, int32, error) {
return &blockHash, m.bestHeight, nil
}
func (m *mockChain) GetTransaction(txid *chainhash.Hash) (*wire.MsgTx, error) {
return nil, nil
}
func (m *mockChain) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
m.RLock()
defer m.RUnlock()
@@ -230,31 +222,6 @@ func (m *mockChain) GetBlockHash(blockHeight int64) (*chainhash.Hash, error) {
return &hash, nil
}
func (m *mockChain) addUtxo(op wire.OutPoint, out *wire.TxOut) {
m.Lock()
m.utxos[op] = *out
m.Unlock()
}
func (m *mockChain) delUtxo(op wire.OutPoint) {
m.Lock()
delete(m.utxos, op)
m.Unlock()
}
func (m *mockChain) GetUtxo(op *wire.OutPoint, _ []byte, _ uint32,
_ <-chan struct{}) (*wire.TxOut, error) {
m.RLock()
defer m.RUnlock()
utxo, ok := m.utxos[*op]
if !ok {
return nil, btcwallet.ErrOutputSpent
}
return &utxo, nil
}
func (m *mockChain) addBlock(block *wire.MsgBlock, height uint32, nonce uint32) {
m.Lock()
block.Header.Nonce = nonce
@@ -459,9 +426,10 @@ func TestEdgeUpdateNotification(t *testing.T) {
// First we'll create the utxo for the channel to be "closed"
const chanValue = 10000
fundingTx, chanPoint, chanID, err := createChannelEdge(ctx,
bitcoinKey1.SerializeCompressed(), bitcoinKey2.SerializeCompressed(),
chanValue, 0)
script, fundingTx, chanPoint, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(), chanValue, 0,
)
require.NoError(t, err, "unable create channel edge")
// We'll also add a record for the block that included our funding
@@ -488,6 +456,9 @@ func TestEdgeUpdateNotification(t *testing.T) {
BitcoinSig1Bytes: testSig.Serialize(),
BitcoinSig2Bytes: testSig.Serialize(),
},
ChannelPoint: *chanPoint,
Capacity: chanValue,
FundingScript: fn.Some(script),
}
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
@@ -643,10 +614,11 @@ func TestNodeUpdateNotification(t *testing.T) {
// We only accept node announcements from nodes having a known channel,
// so create one now.
const chanValue = 10000
fundingTx, _, chanID, err := createChannelEdge(ctx,
script, fundingTx, _, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(),
chanValue, startingBlockHeight)
chanValue, startingBlockHeight,
)
require.NoError(t, err, "unable create channel edge")
// We'll also add a record for the block that included our funding
@@ -675,6 +647,7 @@ func TestNodeUpdateNotification(t *testing.T) {
BitcoinSig1Bytes: testSig.Serialize(),
BitcoinSig2Bytes: testSig.Serialize(),
},
FundingScript: fn.Some(script),
}
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
@@ -825,10 +798,11 @@ func TestNotificationCancellation(t *testing.T) {
// We'll create the utxo for a new channel.
const chanValue = 10000
fundingTx, _, chanID, err := createChannelEdge(ctx,
script, fundingTx, chanPoint, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(),
chanValue, startingBlockHeight)
chanValue, startingBlockHeight,
)
require.NoError(t, err, "unable create channel edge")
// We'll also add a record for the block that included our funding
@@ -859,6 +833,9 @@ func TestNotificationCancellation(t *testing.T) {
BitcoinSig1Bytes: testSig.Serialize(),
BitcoinSig2Bytes: testSig.Serialize(),
},
ChannelPoint: *chanPoint,
Capacity: chanValue,
FundingScript: fn.Some(script),
}
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())
@@ -899,9 +876,11 @@ func TestChannelCloseNotification(t *testing.T) {
// First we'll create the utxo for the channel to be "closed"
const chanValue = 10000
fundingTx, chanUtxo, chanID, err := createChannelEdge(ctx,
bitcoinKey1.SerializeCompressed(), bitcoinKey2.SerializeCompressed(),
chanValue, startingBlockHeight)
script, fundingTx, chanUtxo, chanID, err := createChannelEdge(
bitcoinKey1.SerializeCompressed(),
bitcoinKey2.SerializeCompressed(), chanValue,
startingBlockHeight,
)
require.NoError(t, err, "unable create channel edge")
// We'll also add a record for the block that included our funding
@@ -928,6 +907,9 @@ func TestChannelCloseNotification(t *testing.T) {
BitcoinSig1Bytes: testSig.Serialize(),
BitcoinSig2Bytes: testSig.Serialize(),
},
ChannelPoint: *chanUtxo,
Capacity: chanValue,
FundingScript: fn.Some(script),
}
copy(edge.BitcoinKey1Bytes[:], bitcoinKey1.SerializeCompressed())
copy(edge.BitcoinKey2Bytes[:], bitcoinKey2.SerializeCompressed())