mirror of
https://github.com/lightningnetwork/lnd.git
synced 2024-11-19 09:53:54 +01:00
8240a87c2b
`MineBlocksAndAssertNumTxes`
333 lines
11 KiB
Go
333 lines
11 KiB
Go
package lntest
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/btcsuite/btcd/blockchain"
|
|
"github.com/btcsuite/btcd/btcutil"
|
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/lightningnetwork/lnd/lntest/miner"
|
|
"github.com/lightningnetwork/lnd/lntest/node"
|
|
"github.com/lightningnetwork/lnd/lntest/wait"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// Miner returns the miner instance.
|
|
//
|
|
// NOTE: Caller should keep in mind that when using this private instance,
|
|
// certain states won't be managed by the HarnessTest anymore. For instance,
|
|
// when mining directly, the nodes managed by the HarnessTest can be out of
|
|
// sync, and the `HarnessTest.CurrentHeight()` won't be accurate.
|
|
func (h *HarnessTest) Miner() *miner.HarnessMiner {
|
|
return h.miner
|
|
}
|
|
|
|
// MineBlocks mines blocks and asserts all active nodes have synced to the
|
|
// chain. It assumes no txns are expected in the blocks.
|
|
//
|
|
// NOTE: Use `MineBlocksAndAssertNumTxes` if you expect txns in the blocks. Use
|
|
// `MineEmptyBlocks` if you want to make sure that txns stay unconfirmed.
|
|
func (h *HarnessTest) MineBlocks(num int) {
|
|
require.Less(h, num, maxBlocksAllowed, "too many blocks to mine")
|
|
|
|
// Update the harness's current height.
|
|
defer h.updateCurrentHeight()
|
|
|
|
// Mine num of blocks.
|
|
for i := 0; i < num; i++ {
|
|
block := h.miner.MineBlocks(1)[0]
|
|
|
|
// Check the block doesn't have any txns except the coinbase.
|
|
if len(block.Transactions) <= 1 {
|
|
// Make sure all the active nodes are synced.
|
|
h.AssertActiveNodesSyncedTo(block)
|
|
|
|
// Mine the next block.
|
|
continue
|
|
}
|
|
|
|
// Create a detailed description.
|
|
desc := fmt.Sprintf("block %v has %d txns:\n",
|
|
block.BlockHash(), len(block.Transactions)-1)
|
|
|
|
// Print all the txns except the coinbase.
|
|
for _, tx := range block.Transactions {
|
|
if blockchain.IsCoinBaseTx(tx) {
|
|
continue
|
|
}
|
|
|
|
desc += fmt.Sprintf("%v\n", tx.TxHash())
|
|
}
|
|
|
|
desc += "Consider using `MineBlocksAndAssertNumTxes` if you " +
|
|
"expect txns, or `MineEmptyBlocks` if you want to " +
|
|
"keep the txns unconfirmed."
|
|
|
|
// Raise an error if the block has txns.
|
|
require.Fail(h, "MineBlocks", desc)
|
|
}
|
|
}
|
|
|
|
// MineEmptyBlocks mines a given number of empty blocks.
|
|
//
|
|
// NOTE: this differs from miner's `MineEmptyBlocks` as it requires the nodes
|
|
// to be synced.
|
|
func (h *HarnessTest) MineEmptyBlocks(num int) []*wire.MsgBlock {
|
|
require.Less(h, num, maxBlocksAllowed, "too many blocks to mine")
|
|
|
|
// Update the harness's current height.
|
|
defer h.updateCurrentHeight()
|
|
|
|
blocks := h.miner.MineEmptyBlocks(num)
|
|
|
|
// Finally, make sure all the active nodes are synced.
|
|
h.AssertActiveNodesSynced()
|
|
|
|
return blocks
|
|
}
|
|
|
|
// MineBlocksAndAssertNumTxes mines blocks and asserts the number of
|
|
// transactions are found in the first block. It also asserts all active nodes
|
|
// have synced to the chain.
|
|
//
|
|
// NOTE: this differs from miner's `MineBlocks` as it requires the nodes to be
|
|
// synced.
|
|
func (h *HarnessTest) MineBlocksAndAssertNumTxes(num uint32,
|
|
numTxs int) []*wire.MsgBlock {
|
|
|
|
// Update the harness's current height.
|
|
defer h.updateCurrentHeight()
|
|
|
|
// If we expect transactions to be included in the blocks we'll mine,
|
|
// we wait here until they are seen in the miner's mempool.
|
|
txids := h.AssertNumTxsInMempool(numTxs)
|
|
|
|
// Mine blocks.
|
|
blocks := h.miner.MineBlocks(num)
|
|
|
|
// Assert that all the transactions were included in the first block.
|
|
for _, txid := range txids {
|
|
h.miner.AssertTxInBlock(blocks[0], txid)
|
|
}
|
|
|
|
// Make sure the mempool has been updated.
|
|
for _, txid := range txids {
|
|
h.miner.AssertTxNotInMempool(*txid)
|
|
}
|
|
|
|
// Finally, make sure all the active nodes are synced.
|
|
bestBlock := blocks[len(blocks)-1]
|
|
h.AssertActiveNodesSyncedTo(bestBlock)
|
|
|
|
return blocks
|
|
}
|
|
|
|
// ConnectMiner connects the miner with the chain backend in the network.
|
|
func (h *HarnessTest) ConnectMiner() {
|
|
err := h.manager.chainBackend.ConnectMiner()
|
|
require.NoError(h, err, "failed to connect miner")
|
|
}
|
|
|
|
// DisconnectMiner removes the connection between the miner and the chain
|
|
// backend in the network.
|
|
func (h *HarnessTest) DisconnectMiner() {
|
|
err := h.manager.chainBackend.DisconnectMiner()
|
|
require.NoError(h, err, "failed to disconnect miner")
|
|
}
|
|
|
|
// cleanMempool mines blocks till the mempool is empty and asserts all active
|
|
// nodes have synced to the chain.
|
|
func (h *HarnessTest) cleanMempool() {
|
|
_, startHeight := h.GetBestBlock()
|
|
|
|
// Mining the blocks slow to give `lnd` more time to sync.
|
|
var bestBlock *wire.MsgBlock
|
|
err := wait.NoError(func() error {
|
|
// If mempool is empty, exit.
|
|
mem := h.miner.GetRawMempool()
|
|
if len(mem) == 0 {
|
|
_, height := h.GetBestBlock()
|
|
h.Logf("Mined %d blocks when cleanup the mempool",
|
|
height-startHeight)
|
|
|
|
return nil
|
|
}
|
|
|
|
// Otherwise mine a block.
|
|
blocks := h.miner.MineBlocksSlow(1)
|
|
bestBlock = blocks[len(blocks)-1]
|
|
|
|
// Make sure all the active nodes are synced.
|
|
h.AssertActiveNodesSyncedTo(bestBlock)
|
|
|
|
return fmt.Errorf("still have %d txes in mempool", len(mem))
|
|
}, wait.MinerMempoolTimeout)
|
|
require.NoError(h, err, "timeout cleaning up mempool")
|
|
}
|
|
|
|
// mineTillForceCloseResolved asserts that the number of pending close channels
|
|
// are zero. Each time it checks, a new block is mined using MineBlocksSlow to
|
|
// give the node some time to catch up the chain.
|
|
//
|
|
// NOTE: this method is a workaround to make sure we have a clean mempool at
|
|
// the end of a channel force closure. We cannot directly mine blocks and
|
|
// assert channels being fully closed because the subsystems in lnd don't share
|
|
// the same block height. This is especially the case when blocks are produced
|
|
// too fast.
|
|
// TODO(yy): remove this workaround when syncing blocks are unified in all the
|
|
// subsystems.
|
|
func (h *HarnessTest) mineTillForceCloseResolved(hn *node.HarnessNode) {
|
|
_, startHeight := h.GetBestBlock()
|
|
|
|
err := wait.NoError(func() error {
|
|
resp := hn.RPC.PendingChannels()
|
|
total := len(resp.PendingForceClosingChannels)
|
|
if total != 0 {
|
|
h.MineBlocks(1)
|
|
|
|
return fmt.Errorf("expected num of pending force " +
|
|
"close channel to be zero")
|
|
}
|
|
|
|
_, height := h.GetBestBlock()
|
|
h.Logf("Mined %d blocks while waiting for force closed "+
|
|
"channel to be resolved", height-startHeight)
|
|
|
|
return nil
|
|
}, DefaultTimeout)
|
|
|
|
require.NoErrorf(h, err, "assert force close resolved timeout")
|
|
}
|
|
|
|
// AssertTxInMempool asserts a given transaction can be found in the mempool.
|
|
func (h *HarnessTest) AssertTxInMempool(txid *chainhash.Hash) *wire.MsgTx {
|
|
return h.miner.AssertTxInMempool(txid)
|
|
}
|
|
|
|
// AssertTxNotInMempool asserts a given transaction cannot be found in the
|
|
// mempool. It assumes the mempool is not empty.
|
|
//
|
|
// NOTE: this should be used after `AssertTxInMempool` to ensure the tx has
|
|
// entered the mempool before. Otherwise it might give false positive and the
|
|
// tx may enter the mempool after the check.
|
|
func (h *HarnessTest) AssertTxNotInMempool(txid chainhash.Hash) *wire.MsgTx {
|
|
return h.miner.AssertTxNotInMempool(txid)
|
|
}
|
|
|
|
// AssertNumTxsInMempool polls until finding the desired number of transactions
|
|
// in the provided miner's mempool. It will asserrt if this number is not met
|
|
// after the given timeout.
|
|
func (h *HarnessTest) AssertNumTxsInMempool(n int) []*chainhash.Hash {
|
|
return h.miner.AssertNumTxsInMempool(n)
|
|
}
|
|
|
|
// AssertOutpointInMempool asserts a given outpoint can be found in the mempool.
|
|
func (h *HarnessTest) AssertOutpointInMempool(op wire.OutPoint) *wire.MsgTx {
|
|
return h.miner.AssertOutpointInMempool(op)
|
|
}
|
|
|
|
// AssertTxInBlock asserts that a given txid can be found in the passed block.
|
|
func (h *HarnessTest) AssertTxInBlock(block *wire.MsgBlock,
|
|
txid *chainhash.Hash) {
|
|
|
|
h.miner.AssertTxInBlock(block, txid)
|
|
}
|
|
|
|
// GetNumTxsFromMempool polls until finding the desired number of transactions
|
|
// in the miner's mempool and returns the full transactions to the caller.
|
|
func (h *HarnessTest) GetNumTxsFromMempool(n int) []*wire.MsgTx {
|
|
return h.miner.GetNumTxsFromMempool(n)
|
|
}
|
|
|
|
// GetBestBlock makes a RPC request to miner and asserts.
|
|
func (h *HarnessTest) GetBestBlock() (*chainhash.Hash, int32) {
|
|
return h.miner.GetBestBlock()
|
|
}
|
|
|
|
// MineBlockWithTx mines a single block to include the specifies tx only.
|
|
func (h *HarnessTest) MineBlockWithTx(tx *wire.MsgTx) *wire.MsgBlock {
|
|
return h.miner.MineBlockWithTx(tx)
|
|
}
|
|
|
|
// ConnectToMiner connects the miner to a temp miner.
|
|
func (h *HarnessTest) ConnectToMiner(tempMiner *miner.HarnessMiner) {
|
|
h.miner.ConnectMiner(tempMiner)
|
|
}
|
|
|
|
// DisconnectFromMiner disconnects the miner from the temp miner.
|
|
func (h *HarnessTest) DisconnectFromMiner(tempMiner *miner.HarnessMiner) {
|
|
h.miner.DisconnectMiner(tempMiner)
|
|
}
|
|
|
|
// GetRawMempool makes a RPC call to the miner's GetRawMempool and
|
|
// asserts.
|
|
func (h *HarnessTest) GetRawMempool() []*chainhash.Hash {
|
|
return h.miner.GetRawMempool()
|
|
}
|
|
|
|
// GetRawTransaction makes a RPC call to the miner's GetRawTransaction and
|
|
// asserts.
|
|
func (h *HarnessTest) GetRawTransaction(txid *chainhash.Hash) *btcutil.Tx {
|
|
return h.miner.GetRawTransaction(txid)
|
|
}
|
|
|
|
// NewMinerAddress creates a new address for the miner and asserts.
|
|
func (h *HarnessTest) NewMinerAddress() btcutil.Address {
|
|
return h.miner.NewMinerAddress()
|
|
}
|
|
|
|
// 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 *HarnessTest) SpawnTempMiner() *miner.HarnessMiner {
|
|
return h.miner.SpawnTempMiner()
|
|
}
|
|
|
|
// CreateTransaction uses the miner to create a transaction using the given
|
|
// outputs using the specified fee rate and returns the transaction.
|
|
func (h *HarnessTest) CreateTransaction(outputs []*wire.TxOut,
|
|
feeRate btcutil.Amount) *wire.MsgTx {
|
|
|
|
return h.miner.CreateTransaction(outputs, feeRate)
|
|
}
|
|
|
|
// SendOutputsWithoutChange uses the miner to send the given outputs using the
|
|
// specified fee rate and returns the txid.
|
|
func (h *HarnessTest) SendOutputsWithoutChange(outputs []*wire.TxOut,
|
|
feeRate btcutil.Amount) *chainhash.Hash {
|
|
|
|
return h.miner.SendOutputsWithoutChange(outputs, feeRate)
|
|
}
|
|
|
|
// AssertMinerBlockHeightDelta ensures that tempMiner is 'delta' blocks ahead
|
|
// of miner.
|
|
func (h *HarnessTest) AssertMinerBlockHeightDelta(
|
|
tempMiner *miner.HarnessMiner, delta int32) {
|
|
|
|
h.miner.AssertMinerBlockHeightDelta(tempMiner, delta)
|
|
}
|
|
|
|
// SendRawTransaction submits the encoded transaction to the server which will
|
|
// then relay it to the network.
|
|
func (h *HarnessTest) SendRawTransaction(tx *wire.MsgTx,
|
|
allowHighFees bool) (chainhash.Hash, error) {
|
|
|
|
txid, err := h.miner.Client.SendRawTransaction(tx, allowHighFees)
|
|
require.NoError(h, err)
|
|
|
|
return *txid, nil
|
|
}
|
|
|
|
// CurrentHeight returns the current block height.
|
|
func (h *HarnessTest) CurrentHeight() uint32 {
|
|
return h.currentHeight
|
|
}
|
|
|
|
// updateCurrentHeight set the harness's current height to the best known
|
|
// height.
|
|
func (h *HarnessTest) updateCurrentHeight() {
|
|
_, height := h.GetBestBlock()
|
|
h.currentHeight = uint32(height)
|
|
}
|