mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-19 21:31:04 +02:00
itest+lntest: add helper methods to manage temp miners
This commit adds several methods to easily spawn a temp miner and connect/disconnect it from the original miner.
This commit is contained in:
parent
bacb49ddba
commit
29124dd3b4
@ -5,17 +5,14 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||||||
"github.com/btcsuite/btcd/integration/rpctest"
|
|
||||||
"github.com/lightningnetwork/lnd/chainreg"
|
"github.com/lightningnetwork/lnd/chainreg"
|
||||||
"github.com/lightningnetwork/lnd/funding"
|
"github.com/lightningnetwork/lnd/funding"
|
||||||
"github.com/lightningnetwork/lnd/lnrpc"
|
"github.com/lightningnetwork/lnd/lnrpc"
|
||||||
"github.com/lightningnetwork/lnd/lntest"
|
"github.com/lightningnetwork/lnd/lntest"
|
||||||
"github.com/lightningnetwork/lnd/lntest/node"
|
"github.com/lightningnetwork/lnd/lntest/node"
|
||||||
"github.com/lightningnetwork/lnd/lntest/rpc"
|
"github.com/lightningnetwork/lnd/lntest/rpc"
|
||||||
"github.com/lightningnetwork/lnd/lntest/wait"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -30,44 +27,12 @@ func testOpenChannelAfterReorg(ht *lntest.HarnessTest) {
|
|||||||
ht.Skipf("skipping reorg test for neutrino backend")
|
ht.Skipf("skipping reorg test for neutrino backend")
|
||||||
}
|
}
|
||||||
|
|
||||||
temp := "temp"
|
// Create a temp miner.
|
||||||
|
tempMiner := ht.Miner.SpawnTempMiner()
|
||||||
// Set up a new miner that we can use to cause a reorg.
|
|
||||||
tempLogDir := ".tempminerlogs"
|
|
||||||
logFilename := "output-open_channel_reorg-temp_miner.log"
|
|
||||||
tempMiner := lntest.NewTempMiner(
|
|
||||||
ht.Context(), ht.T, tempLogDir, logFilename,
|
|
||||||
)
|
|
||||||
defer tempMiner.Stop()
|
|
||||||
|
|
||||||
// Setup the temp miner
|
|
||||||
require.NoError(ht, tempMiner.SetUp(false, 0),
|
|
||||||
"unable to set up mining node")
|
|
||||||
|
|
||||||
miner := ht.Miner
|
miner := ht.Miner
|
||||||
alice, bob := ht.Alice, ht.Bob
|
alice, bob := ht.Alice, ht.Bob
|
||||||
|
|
||||||
// We start by connecting the new miner to our original miner,
|
|
||||||
// such that it will sync to our original chain.
|
|
||||||
err := miner.Client.Node(
|
|
||||||
btcjson.NConnect, tempMiner.P2PAddress(), &temp,
|
|
||||||
)
|
|
||||||
require.NoError(ht, err, "unable to connect miners")
|
|
||||||
|
|
||||||
nodeSlice := []*rpctest.Harness{miner.Harness, tempMiner.Harness}
|
|
||||||
err = rpctest.JoinNodes(nodeSlice, rpctest.Blocks)
|
|
||||||
require.NoError(ht, err, "unable to join node on blocks")
|
|
||||||
|
|
||||||
// The two miners should be on the same blockheight.
|
|
||||||
assertMinerBlockHeightDelta(ht, miner, tempMiner, 0)
|
|
||||||
|
|
||||||
// We disconnect the two miners, such that we can mine two different
|
|
||||||
// chains and can cause a reorg later.
|
|
||||||
err = miner.Client.Node(
|
|
||||||
btcjson.NDisconnect, tempMiner.P2PAddress(), &temp,
|
|
||||||
)
|
|
||||||
require.NoError(ht, err, "unable to disconnect miners")
|
|
||||||
|
|
||||||
// Create a new channel that requires 1 confs before it's considered
|
// Create a new channel that requires 1 confs before it's considered
|
||||||
// open, then broadcast the funding transaction
|
// open, then broadcast the funding transaction
|
||||||
params := lntest.OpenChannelParams{
|
params := lntest.OpenChannelParams{
|
||||||
@ -98,7 +63,7 @@ func testOpenChannelAfterReorg(ht *lntest.HarnessTest) {
|
|||||||
|
|
||||||
// Ensure the chain lengths are what we expect, with the temp miner
|
// Ensure the chain lengths are what we expect, with the temp miner
|
||||||
// being 5 blocks ahead.
|
// being 5 blocks ahead.
|
||||||
assertMinerBlockHeightDelta(ht, miner, tempMiner, 5)
|
miner.AssertMinerBlockHeightDelta(tempMiner, 5)
|
||||||
|
|
||||||
chanPoint := &lnrpc.ChannelPoint{
|
chanPoint := &lnrpc.ChannelPoint{
|
||||||
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
FundingTxid: &lnrpc.ChannelPoint_FundingTxidBytes{
|
||||||
@ -125,22 +90,14 @@ func testOpenChannelAfterReorg(ht *lntest.HarnessTest) {
|
|||||||
|
|
||||||
// Connecting to the temporary miner should now cause our original
|
// Connecting to the temporary miner should now cause our original
|
||||||
// chain to be re-orged out.
|
// chain to be re-orged out.
|
||||||
err = miner.Client.Node(btcjson.NConnect, tempMiner.P2PAddress(), &temp)
|
miner.ConnectMiner(tempMiner)
|
||||||
require.NoError(ht, err, "unable to connect temp miner")
|
|
||||||
|
|
||||||
nodes := []*rpctest.Harness{tempMiner.Harness, miner.Harness}
|
|
||||||
err = rpctest.JoinNodes(nodes, rpctest.Blocks)
|
|
||||||
require.NoError(ht, err, "unable to join node on blocks")
|
|
||||||
|
|
||||||
// Once again they should be on the same chain.
|
// Once again they should be on the same chain.
|
||||||
assertMinerBlockHeightDelta(ht, miner, tempMiner, 0)
|
miner.AssertMinerBlockHeightDelta(tempMiner, 0)
|
||||||
|
|
||||||
// Now we disconnect the two miners, and connect our original miner to
|
// Now we disconnect the two miners, and connect our original miner to
|
||||||
// our chain backend once again.
|
// our chain backend once again.
|
||||||
err = miner.Client.Node(
|
miner.DisconnectMiner(tempMiner)
|
||||||
btcjson.NDisconnect, tempMiner.P2PAddress(), &temp,
|
|
||||||
)
|
|
||||||
require.NoError(ht, err, "unable to disconnect temp miner")
|
|
||||||
|
|
||||||
ht.ConnectMiner()
|
ht.ConnectMiner()
|
||||||
|
|
||||||
@ -531,36 +488,6 @@ func runBasicChannelCreationAndUpdates(ht *lntest.HarnessTest,
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// assertMinerBlockHeightDelta ensures that tempMiner is 'delta' blocks ahead
|
|
||||||
// of miner.
|
|
||||||
func assertMinerBlockHeightDelta(ht *lntest.HarnessTest,
|
|
||||||
miner, tempMiner *lntest.HarnessMiner, delta int32) {
|
|
||||||
|
|
||||||
// Ensure the chain lengths are what we expect.
|
|
||||||
err := wait.NoError(func() error {
|
|
||||||
_, tempMinerHeight, err := tempMiner.Client.GetBestBlock()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to get current "+
|
|
||||||
"blockheight %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, minerHeight, err := miner.Client.GetBestBlock()
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("unable to get current "+
|
|
||||||
"blockheight %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if tempMinerHeight != minerHeight+delta {
|
|
||||||
return fmt.Errorf("expected new miner(%d) to be %d "+
|
|
||||||
"blocks ahead of original miner(%d)",
|
|
||||||
tempMinerHeight, delta, minerHeight)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}, defaultTimeout)
|
|
||||||
require.NoError(ht, err, "failed to assert block height delta")
|
|
||||||
}
|
|
||||||
|
|
||||||
// verifyCloseUpdate is used to verify that a closed channel update is of the
|
// verifyCloseUpdate is used to verify that a closed channel update is of the
|
||||||
// expected type.
|
// expected type.
|
||||||
func verifyCloseUpdate(chanUpdate *lnrpc.ChannelEventUpdate,
|
func verifyCloseUpdate(chanUpdate *lnrpc.ChannelEventUpdate,
|
||||||
|
@ -5,9 +5,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/btcjson"
|
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/integration/rpctest"
|
|
||||||
"github.com/go-errors/errors"
|
"github.com/go-errors/errors"
|
||||||
"github.com/lightningnetwork/lnd/aliasmgr"
|
"github.com/lightningnetwork/lnd/aliasmgr"
|
||||||
"github.com/lightningnetwork/lnd/chainreg"
|
"github.com/lightningnetwork/lnd/chainreg"
|
||||||
@ -906,8 +904,6 @@ func testZeroConfReorg(ht *lntest.HarnessTest) {
|
|||||||
ht.Skipf("skipping zero-conf reorg test for neutrino backend")
|
ht.Skipf("skipping zero-conf reorg test for neutrino backend")
|
||||||
}
|
}
|
||||||
|
|
||||||
var temp = "temp"
|
|
||||||
|
|
||||||
// Since zero-conf is opt in, the harness nodes provided won't be able
|
// Since zero-conf is opt in, the harness nodes provided won't be able
|
||||||
// to open zero-conf channels. In that case, we just spin up new nodes.
|
// to open zero-conf channels. In that case, we just spin up new nodes.
|
||||||
zeroConfArgs := []string{
|
zeroConfArgs := []string{
|
||||||
@ -963,37 +959,7 @@ func testZeroConfReorg(ht *lntest.HarnessTest) {
|
|||||||
// exists in the graph.
|
// exists in the graph.
|
||||||
//
|
//
|
||||||
// First, we'll setup a new miner that we can use to cause a reorg.
|
// First, we'll setup a new miner that we can use to cause a reorg.
|
||||||
tempLogDir := ".tempminerlogs"
|
tempMiner := ht.Miner.SpawnTempMiner()
|
||||||
logFilename := "output-open_channel_reorg-temp_miner.log"
|
|
||||||
tempMiner := lntest.NewTempMiner(
|
|
||||||
ht.Context(), ht.T, tempLogDir, logFilename,
|
|
||||||
)
|
|
||||||
defer tempMiner.Stop()
|
|
||||||
|
|
||||||
require.NoError(
|
|
||||||
ht.T, tempMiner.SetUp(false, 0), "unable to setup mining node",
|
|
||||||
)
|
|
||||||
|
|
||||||
// We start by connecting the new miner to our original miner, such
|
|
||||||
// that it will sync to our original chain.
|
|
||||||
err := ht.Miner.Client.Node(
|
|
||||||
btcjson.NConnect, tempMiner.P2PAddress(), &temp,
|
|
||||||
)
|
|
||||||
require.NoError(ht.T, err, "unable to connect node")
|
|
||||||
|
|
||||||
nodeSlice := []*rpctest.Harness{ht.Miner.Harness, tempMiner.Harness}
|
|
||||||
err = rpctest.JoinNodes(nodeSlice, rpctest.Blocks)
|
|
||||||
require.NoError(ht.T, err, "unable to join node on blocks")
|
|
||||||
|
|
||||||
// The two miners should be on the same block height.
|
|
||||||
assertMinerBlockHeightDelta(ht, ht.Miner, tempMiner, 0)
|
|
||||||
|
|
||||||
// We disconnect the two miners, such that we can mine two chains and
|
|
||||||
// cause a reorg later.
|
|
||||||
err = ht.Miner.Client.Node(
|
|
||||||
btcjson.NDisconnect, tempMiner.P2PAddress(), &temp,
|
|
||||||
)
|
|
||||||
require.NoError(ht.T, err, "unable to remove node")
|
|
||||||
|
|
||||||
// We now cause a fork, by letting our original miner mine 1 block and
|
// We now cause a fork, by letting our original miner mine 1 block and
|
||||||
// our new miner will mine 2. We also expect the funding transition to
|
// our new miner will mine 2. We also expect the funding transition to
|
||||||
@ -1002,7 +968,7 @@ func testZeroConfReorg(ht *lntest.HarnessTest) {
|
|||||||
tempMiner.MineEmptyBlocks(2)
|
tempMiner.MineEmptyBlocks(2)
|
||||||
|
|
||||||
// Ensure the temp miner is one block ahead.
|
// Ensure the temp miner is one block ahead.
|
||||||
assertMinerBlockHeightDelta(ht, ht.Miner, tempMiner, 1)
|
ht.Miner.AssertMinerBlockHeightDelta(tempMiner, 1)
|
||||||
|
|
||||||
// Wait for Carol to sync to the original miner's chain.
|
// Wait for Carol to sync to the original miner's chain.
|
||||||
_, minerHeight := ht.Miner.GetBestBlock()
|
_, minerHeight := ht.Miner.GetBestBlock()
|
||||||
@ -1015,24 +981,14 @@ func testZeroConfReorg(ht *lntest.HarnessTest) {
|
|||||||
|
|
||||||
// Connecting to the temporary miner should cause the original miner to
|
// Connecting to the temporary miner should cause the original miner to
|
||||||
// reorg to the longer chain.
|
// reorg to the longer chain.
|
||||||
err = ht.Miner.Client.Node(
|
ht.Miner.ConnectMiner(tempMiner)
|
||||||
btcjson.NConnect, tempMiner.P2PAddress(), &temp,
|
|
||||||
)
|
|
||||||
require.NoError(ht.T, err, "unable to remove node")
|
|
||||||
|
|
||||||
nodes := []*rpctest.Harness{tempMiner.Harness, ht.Miner.Harness}
|
|
||||||
err = rpctest.JoinNodes(nodes, rpctest.Blocks)
|
|
||||||
require.NoError(ht.T, err, "unable to join node on blocks")
|
|
||||||
|
|
||||||
// They should now be on the same chain.
|
// They should now be on the same chain.
|
||||||
assertMinerBlockHeightDelta(ht, ht.Miner, tempMiner, 0)
|
ht.Miner.AssertMinerBlockHeightDelta(tempMiner, 0)
|
||||||
|
|
||||||
// Now we disconnect the two miners and reconnect our original chain
|
// Now we disconnect the two miners and reconnect our original chain
|
||||||
// backend.
|
// backend.
|
||||||
err = ht.Miner.Client.Node(
|
ht.Miner.DisconnectMiner(tempMiner)
|
||||||
btcjson.NDisconnect, tempMiner.P2PAddress(), &temp,
|
|
||||||
)
|
|
||||||
require.NoError(ht.T, err, "unable to remove node")
|
|
||||||
|
|
||||||
ht.ConnectMiner()
|
ht.ConnectMiner()
|
||||||
|
|
||||||
|
@ -21,12 +21,6 @@ import (
|
|||||||
// logDirPattern is the pattern of the name of the temporary log directory.
|
// logDirPattern is the pattern of the name of the temporary log directory.
|
||||||
const logDirPattern = "%s/.backendlogs"
|
const logDirPattern = "%s/.backendlogs"
|
||||||
|
|
||||||
// temp is used to signal we want to establish a temporary connection using the
|
|
||||||
// btcd Node API.
|
|
||||||
//
|
|
||||||
// NOTE: Cannot be const, since the node API expects a reference.
|
|
||||||
var temp = "temp"
|
|
||||||
|
|
||||||
// BtcdBackendConfig is an implementation of the BackendConfig interface
|
// BtcdBackendConfig is an implementation of the BackendConfig interface
|
||||||
// backed by a btcd node.
|
// backed by a btcd node.
|
||||||
type BtcdBackendConfig struct {
|
type BtcdBackendConfig struct {
|
||||||
|
@ -34,7 +34,15 @@ const (
|
|||||||
slowMineDelay = 100 * time.Millisecond
|
slowMineDelay = 100 * time.Millisecond
|
||||||
)
|
)
|
||||||
|
|
||||||
var harnessNetParams = &chaincfg.RegressionNetParams
|
var (
|
||||||
|
harnessNetParams = &chaincfg.RegressionNetParams
|
||||||
|
|
||||||
|
// temp is used to signal we want to establish a temporary connection
|
||||||
|
// using the btcd Node API.
|
||||||
|
//
|
||||||
|
// NOTE: Cannot be const, since the node API expects a reference.
|
||||||
|
temp = "temp"
|
||||||
|
)
|
||||||
|
|
||||||
type HarnessMiner struct {
|
type HarnessMiner struct {
|
||||||
*testing.T
|
*testing.T
|
||||||
@ -487,3 +495,89 @@ func (h *HarnessMiner) MineEmptyBlocks(num int) []*wire.MsgBlock {
|
|||||||
|
|
||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpawnTempMiner creates a temp miner and syncs it with the current miner.
|
||||||
|
// Once miners are synced, the temp miner is disconnected from the original
|
||||||
|
// miner and returned.
|
||||||
|
func (h *HarnessMiner) SpawnTempMiner() *HarnessMiner {
|
||||||
|
require := require.New(h.T)
|
||||||
|
|
||||||
|
// Setup a temp miner.
|
||||||
|
tempLogDir := ".tempminerlogs"
|
||||||
|
logFilename := "output-temp_miner.log"
|
||||||
|
tempMiner := NewTempMiner(h.runCtx, h.T, tempLogDir, logFilename)
|
||||||
|
|
||||||
|
// Make sure to clean the miner when the test ends.
|
||||||
|
h.T.Cleanup(tempMiner.Stop)
|
||||||
|
|
||||||
|
// Setup the miner.
|
||||||
|
require.NoError(tempMiner.SetUp(false, 0), "unable to setup miner")
|
||||||
|
|
||||||
|
// Connect the temp miner to the original miner.
|
||||||
|
err := h.Client.Node(btcjson.NConnect, tempMiner.P2PAddress(), &temp)
|
||||||
|
require.NoError(err, "unable to connect node")
|
||||||
|
|
||||||
|
// Sync the blocks.
|
||||||
|
nodeSlice := []*rpctest.Harness{h.Harness, tempMiner.Harness}
|
||||||
|
err = rpctest.JoinNodes(nodeSlice, rpctest.Blocks)
|
||||||
|
require.NoError(err, "unable to join node on blocks")
|
||||||
|
|
||||||
|
// The two miners should be on the same block height.
|
||||||
|
h.AssertMinerBlockHeightDelta(tempMiner, 0)
|
||||||
|
|
||||||
|
// Once synced, we now disconnect the temp miner so it'll be
|
||||||
|
// independent from the original miner.
|
||||||
|
err = h.Client.Node(btcjson.NDisconnect, tempMiner.P2PAddress(), &temp)
|
||||||
|
require.NoError(err, "unable to disconnect miners")
|
||||||
|
|
||||||
|
return tempMiner
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConnectMiner connects the miner to a temp miner.
|
||||||
|
func (h *HarnessMiner) ConnectMiner(tempMiner *HarnessMiner) {
|
||||||
|
require := require.New(h.T)
|
||||||
|
|
||||||
|
// Connect the current miner to the temporary miner.
|
||||||
|
err := h.Client.Node(btcjson.NConnect, tempMiner.P2PAddress(), &temp)
|
||||||
|
require.NoError(err, "unable to connect temp miner")
|
||||||
|
|
||||||
|
nodes := []*rpctest.Harness{tempMiner.Harness, h.Harness}
|
||||||
|
err = rpctest.JoinNodes(nodes, rpctest.Blocks)
|
||||||
|
require.NoError(err, "unable to join node on blocks")
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisconnectMiner disconnects the miner from the temp miner.
|
||||||
|
func (h *HarnessMiner) DisconnectMiner(tempMiner *HarnessMiner) {
|
||||||
|
err := h.Client.Node(btcjson.NDisconnect, tempMiner.P2PAddress(), &temp)
|
||||||
|
require.NoError(h.T, err, "unable to disconnect temp miner")
|
||||||
|
}
|
||||||
|
|
||||||
|
// AssertMinerBlockHeightDelta ensures that tempMiner is 'delta' blocks ahead
|
||||||
|
// of miner.
|
||||||
|
func (h *HarnessMiner) AssertMinerBlockHeightDelta(tempMiner *HarnessMiner,
|
||||||
|
delta int32) {
|
||||||
|
|
||||||
|
// Ensure the chain lengths are what we expect.
|
||||||
|
err := wait.NoError(func() error {
|
||||||
|
_, tempMinerHeight, err := tempMiner.Client.GetBestBlock()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to get current "+
|
||||||
|
"blockheight %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, minerHeight, err := h.Client.GetBestBlock()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to get current "+
|
||||||
|
"blockheight %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tempMinerHeight != minerHeight+delta {
|
||||||
|
return fmt.Errorf("expected new miner(%d) to be %d "+
|
||||||
|
"blocks ahead of original miner(%d)",
|
||||||
|
tempMinerHeight, delta, minerHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}, DefaultTimeout)
|
||||||
|
require.NoError(h.T, err, "failed to assert block height delta")
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user