lntemp: expand package to support testBasicChannelFunding

This commit adds more supporting methods to support the test
`testBasicChannelFunding`.
This commit is contained in:
yyforyongyu 2022-07-26 12:11:43 +08:00
parent 85210b947f
commit 19981ac9bd
No known key found for this signature in database
GPG key ID: 9BCD95C4FF296868
5 changed files with 359 additions and 4 deletions

View file

@ -2,6 +2,7 @@ package lntemp
import (
"context"
"encoding/hex"
"fmt"
"testing"
@ -20,6 +21,12 @@ import (
"github.com/stretchr/testify/require"
)
const (
// defaultMinerFeeRate specifies the fee rate in sats when sending
// outputs from the miner.
defaultMinerFeeRate = 7500
)
// TestCase defines a test case that's been used in the integration test.
type TestCase struct {
// Name specifies the test name.
@ -179,10 +186,7 @@ func (h *HarnessTest) SetupStandbyNodes() {
PkScript: addrScript,
Value: 10 * btcutil.SatoshiPerBitcoin,
}
_, err = h.Miner.SendOutputs(
[]*wire.TxOut{output}, 7500,
)
require.NoError(h, err, "send output failed")
h.Miner.SendOutput(output, 7500)
}
}
@ -676,3 +680,81 @@ func (h *HarnessTest) CloseChannel(hn *node.HarnessNode,
return h.assertChannelClosed(hn, cp, false, stream)
}
// IsNeutrinoBackend returns a bool indicating whether the node is using a
// neutrino as its backend. This is useful when we want to skip certain tests
// which cannot be done with a neutrino backend.
func (h *HarnessTest) IsNeutrinoBackend() bool {
return h.manager.chainBackend.Name() == NeutrinoBackendName
}
// fundCoins attempts to send amt satoshis from the internal mining node to the
// targeted lightning node. The confirmed boolean indicates whether the
// transaction that pays to the target should confirm. For neutrino backend,
// the `confirmed` param is ignored.
func (h *HarnessTest) fundCoins(amt btcutil.Amount, target *node.HarnessNode,
addrType lnrpc.AddressType, confirmed bool) {
initialBalance := target.RPC.WalletBalance()
// First, obtain an address from the target lightning node, preferring
// to receive a p2wkh address s.t the output can immediately be used as
// an input to a funding transaction.
req := &lnrpc.NewAddressRequest{Type: addrType}
resp := target.RPC.NewAddress(req)
addr := h.DecodeAddress(resp.Address)
addrScript := h.PayToAddrScript(addr)
// Generate a transaction which creates an output to the target
// pkScript of the desired amount.
output := &wire.TxOut{
PkScript: addrScript,
Value: int64(amt),
}
h.Miner.SendOutput(output, defaultMinerFeeRate)
// Encode the pkScript in hex as this the format that it will be
// returned via rpc.
expPkScriptStr := hex.EncodeToString(addrScript)
// Now, wait for ListUnspent to show the unconfirmed transaction
// containing the correct pkscript.
//
// Since neutrino doesn't support unconfirmed outputs, skip this check.
if !h.IsNeutrinoBackend() {
utxos := h.AssertNumUTXOsUnconfirmed(target, 1)
// Assert that the lone unconfirmed utxo contains the same
// pkscript as the output generated above.
pkScriptStr := utxos[0].PkScript
require.Equal(h, pkScriptStr, expPkScriptStr,
"pkscript mismatch")
}
// If the transaction should remain unconfirmed, then we'll wait until
// the target node's unconfirmed balance reflects the expected balance
// and exit.
if !confirmed && !h.IsNeutrinoBackend() {
expectedBalance := btcutil.Amount(
initialBalance.UnconfirmedBalance,
) + amt
h.WaitForBalanceUnconfirmed(target, expectedBalance)
return
}
// Otherwise, we'll generate 2 new blocks to ensure the output gains a
// sufficient number of confirmations and wait for the balance to
// reflect what's expected.
h.Miner.MineBlocks(2)
expectedBalance := btcutil.Amount(initialBalance.ConfirmedBalance) + amt
h.WaitForBalanceConfirmed(target, expectedBalance)
}
// FundCoins attempts to send amt satoshis from the internal mining node to the
// targeted lightning node using a P2WKH address. 2 blocks are mined after in
// order to confirm the transaction.
func (h *HarnessTest) FundCoins(amt btcutil.Amount, hn *node.HarnessNode) {
h.fundCoins(amt, hn, lnrpc.AddressType_WITNESS_PUBKEY_HASH, true)
}

View file

@ -2,17 +2,24 @@ package lntemp
import (
"context"
"crypto/rand"
"fmt"
"math"
"strings"
"time"
"github.com/btcsuite/btcd/btcutil"
"github.com/btcsuite/btcd/chaincfg/chainhash"
"github.com/btcsuite/btcd/txscript"
"github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntemp/node"
"github.com/lightningnetwork/lnd/lntemp/rpc"
"github.com/lightningnetwork/lnd/lntest/wait"
"github.com/lightningnetwork/lnd/lntypes"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
)
// WaitForBlockchainSync waits until the node is synced to chain.
@ -498,3 +505,185 @@ func (h *HarnessTest) WaitForGraphSync(hn *node.HarnessNode) {
}, DefaultTimeout)
require.NoError(h, err, "%s: timeout while sync to graph", hn.Name())
}
// AssertNumUTXOsWithConf waits for the given number of UTXOs with the
// specified confirmations range to be available or fails if that isn't the
// case before the default timeout.
//
// NOTE: for standby nodes(Alice and Bob), this method takes account of the
// previous state of the node's UTXOs. The previous state is snapshotted when
// finishing a previous test case via the cleanup function in `Subtest`. In
// other words, this assertion only checks the new changes made in the current
// test.
func (h *HarnessTest) AssertNumUTXOsWithConf(hn *node.HarnessNode,
expectedUtxos int, max, min int32) []*lnrpc.Utxo {
var unconfirmed bool
old := hn.State.UTXO.Confirmed
if max == 0 {
old = hn.State.UTXO.Unconfirmed
unconfirmed = true
}
var utxos []*lnrpc.Utxo
err := wait.NoError(func() error {
req := &walletrpc.ListUnspentRequest{
Account: "",
MaxConfs: max,
MinConfs: min,
UnconfirmedOnly: unconfirmed,
}
resp := hn.RPC.ListUnspent(req)
total := len(resp.Utxos)
if total-old == expectedUtxos {
utxos = resp.Utxos[old:]
return nil
}
return errNumNotMatched(hn.Name(), "num of UTXOs",
expectedUtxos, total-old, total, old)
}, DefaultTimeout)
require.NoError(h, err, "timeout waiting for UTXOs")
return utxos
}
// AssertNumUTXOsUnconfirmed asserts the expected num of unconfirmed utxos are
// seen.
//
// NOTE: for standby nodes(Alice and Bob), this method takes account of the
// previous state of the node's UTXOs. Check `AssertNumUTXOsWithConf` for
// details.
func (h *HarnessTest) AssertNumUTXOsUnconfirmed(hn *node.HarnessNode,
num int) []*lnrpc.Utxo {
return h.AssertNumUTXOsWithConf(hn, num, 0, 0)
}
// AssertNumUTXOsConfirmed asserts the expected num of confirmed utxos are
// seen, which means the returned utxos have at least one confirmation.
//
// NOTE: for standby nodes(Alice and Bob), this method takes account of the
// previous state of the node's UTXOs. Check `AssertNumUTXOsWithConf` for
// details.
func (h *HarnessTest) AssertNumUTXOsConfirmed(hn *node.HarnessNode,
num int) []*lnrpc.Utxo {
return h.AssertNumUTXOsWithConf(hn, num, math.MaxInt32, 1)
}
// AssertNumUTXOs asserts the expected num of utxos are seen, including
// confirmed and unconfirmed outputs.
//
// NOTE: for standby nodes(Alice and Bob), this method takes account of the
// previous state of the node's UTXOs. Check `AssertNumUTXOsWithConf` for
// details.
func (h *HarnessTest) AssertNumUTXOs(hn *node.HarnessNode,
num int) []*lnrpc.Utxo {
return h.AssertNumUTXOsWithConf(hn, num, math.MaxInt32, 0)
}
// WaitForBalanceConfirmed waits until the node sees the expected confirmed
// balance in its wallet.
func (h *HarnessTest) WaitForBalanceConfirmed(hn *node.HarnessNode,
expected btcutil.Amount) {
var lastBalance btcutil.Amount
err := wait.NoError(func() error {
resp := hn.RPC.WalletBalance()
lastBalance = btcutil.Amount(resp.ConfirmedBalance)
if lastBalance == expected {
return nil
}
return fmt.Errorf("expected %v, only have %v", expected,
lastBalance)
}, DefaultTimeout)
require.NoError(h, err, "timeout waiting for confirmed balances")
}
// WaitForBalanceUnconfirmed waits until the node sees the expected unconfirmed
// balance in its wallet.
func (h *HarnessTest) WaitForBalanceUnconfirmed(hn *node.HarnessNode,
expected btcutil.Amount) {
var lastBalance btcutil.Amount
err := wait.NoError(func() error {
resp := hn.RPC.WalletBalance()
lastBalance = btcutil.Amount(resp.UnconfirmedBalance)
if lastBalance == expected {
return nil
}
return fmt.Errorf("expected %v, only have %v", expected,
lastBalance)
}, DefaultTimeout)
require.NoError(h, err, "timeout waiting for unconfirmed balances")
}
// Random32Bytes generates a random 32 bytes which can be used as a pay hash,
// preimage, etc.
func (h *HarnessTest) Random32Bytes() []byte {
randBuf := make([]byte, lntypes.HashSize)
_, err := rand.Read(randBuf)
require.NoErrorf(h, err, "internal error, cannot generate random bytes")
return randBuf
}
// DecodeAddress decodes a given address and asserts there's no error.
func (h *HarnessTest) DecodeAddress(addr string) btcutil.Address {
resp, err := btcutil.DecodeAddress(addr, harnessNetParams)
require.NoError(h, err, "DecodeAddress failed")
return resp
}
// PayToAddrScript creates a new script from the given address and asserts
// there's no error.
func (h *HarnessTest) PayToAddrScript(addr btcutil.Address) []byte {
addrScript, err := txscript.PayToAddrScript(addr)
require.NoError(h, err, "PayToAddrScript failed")
return addrScript
}
// AssertChannelBalanceResp makes a ChannelBalance request and checks the
// returned response matches the expected.
func (h *HarnessTest) AssertChannelBalanceResp(hn *node.HarnessNode,
expected *lnrpc.ChannelBalanceResponse) {
resp := hn.RPC.ChannelBalance()
require.True(h, proto.Equal(expected, resp), "balance is incorrect "+
"got: %v, want: %v", resp, expected)
}
// GetChannelByChanPoint tries to find a channel matching the channel point and
// asserts. It returns the channel found.
func (h *HarnessTest) GetChannelByChanPoint(hn *node.HarnessNode,
chanPoint *lnrpc.ChannelPoint) *lnrpc.Channel {
channel, err := h.findChannel(hn, chanPoint)
require.NoErrorf(h, err, "channel not found using %v", chanPoint)
return channel
}
// GetChannelCommitType retrieves the active channel commitment type for the
// given chan point.
func (h *HarnessTest) GetChannelCommitType(hn *node.HarnessNode,
chanPoint *lnrpc.ChannelPoint) lnrpc.CommitmentType {
c := h.GetChannelByChanPoint(hn, chanPoint)
return c.CommitmentType
}

View file

@ -266,3 +266,38 @@ func (h *HarnessMiner) AssertTxInMempool(txid *chainhash.Hash) *wire.MsgTx {
require.NoError(h, err, "timeout checking mempool")
return msgTx
}
// SendOutputsWithoutChange uses the miner to send the given outputs using the
// specified fee rate and returns the txid.
func (h *HarnessMiner) SendOutputsWithoutChange(outputs []*wire.TxOut,
feeRate btcutil.Amount) *chainhash.Hash {
txid, err := h.Harness.SendOutputsWithoutChange(
outputs, feeRate,
)
require.NoErrorf(h, err, "failed to send output")
return txid
}
// CreateTransaction uses the miner to create a transaction using the given
// outputs using the specified fee rate and returns the transaction.
func (h *HarnessMiner) CreateTransaction(outputs []*wire.TxOut,
feeRate btcutil.Amount) *wire.MsgTx {
tx, err := h.Harness.CreateTransaction(outputs, feeRate, false)
require.NoErrorf(h, err, "failed to create transaction")
return tx
}
// SendOutput creates, signs, and finally broadcasts a transaction spending
// the harness' available mature coinbase outputs to create the new output.
func (h *HarnessMiner) SendOutput(newOutput *wire.TxOut,
feeRate btcutil.Amount) *chainhash.Hash {
hash, err := h.Harness.SendOutputs([]*wire.TxOut{newOutput}, feeRate)
require.NoErrorf(h, err, "failed to send outputs")
return hash
}

View file

@ -4,6 +4,7 @@ import (
"context"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/stretchr/testify/require"
)
// =====================
@ -214,3 +215,26 @@ func (h *HarnessRPC) CloseChannel(
return stream
}
// FundingStateStep makes a RPC call to FundingStateStep and asserts.
func (h *HarnessRPC) FundingStateStep(
msg *lnrpc.FundingTransitionMsg) *lnrpc.FundingStateStepResp {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.LN.FundingStateStep(ctxt, msg)
h.NoError(err, "FundingStateStep")
return resp
}
// FundingStateStepAssertErr makes a RPC call to FundingStateStep and asserts
// there's an error.
func (h *HarnessRPC) FundingStateStepAssertErr(m *lnrpc.FundingTransitionMsg) {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
_, err := h.LN.FundingStateStep(ctxt, m)
require.Error(h, err, "expected an error from FundingStateStep")
}

View file

@ -3,6 +3,7 @@ package rpc
import (
"context"
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
)
@ -22,3 +23,27 @@ func (h *HarnessRPC) ListUnspent(
return resp
}
// DeriveKey makes a RPC call to the DeriveKey and asserts.
func (h *HarnessRPC) DeriveKey(kl *signrpc.KeyLocator) *signrpc.KeyDescriptor {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
key, err := h.WalletKit.DeriveKey(ctxt, kl)
h.NoError(err, "DeriveKey")
return key
}
// SendOutputs makes a RPC call to the node's WalletKitClient and asserts.
func (h *HarnessRPC) SendOutputs(
req *walletrpc.SendOutputsRequest) *walletrpc.SendOutputsResponse {
ctxt, cancel := context.WithTimeout(h.runCtx, DefaultTimeout)
defer cancel()
resp, err := h.WalletKit.SendOutputs(ctxt, req)
h.NoError(err, "SendOutputs")
return resp
}