Merge pull request #5227 from Roasbeef/spent-channs-zombie-index

routing: add chans rejected due to failed chain validation to zombie index
This commit is contained in:
Olaoluwa Osuntokun
2021-05-05 18:32:13 -07:00
committed by GitHub
6 changed files with 263 additions and 10 deletions

View File

@@ -3138,3 +3138,129 @@ func TestBuildRoute(t *testing.T) {
t.Fatalf("unexpected no channel error node")
}
}
// 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(ctx *testCtx, fundingHeight uint32,
ecm edgeCreationModifier) (*channeldb.ChannelEdgeInfo, error) {
node1, err := createTestNode()
if err != nil {
return nil, err
}
node2, err := createTestNode()
if err != nil {
return nil, err
}
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 := &channeldb.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 *channeldb.ChannelEdgeInfo, failCode errorCode) {
t.Helper()
err := ctx.router.AddEdge(edge)
if !IsError(err, failCode) {
t.Fatalf("validation should have failed: %v", err)
}
// 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, cleanup, err := createTestCtxSingleNode(0)
if err != nil {
t.Fatal(err)
}
defer cleanup()
// 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(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(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(ctx, 3, edgeCreationBadScript)
require.Nil(t, err)
assertChanChainRejection(t, ctx, edge, ErrInvalidFundingOutput)
}