lnwallet: add tests for SubscribeTransactions and ListTransactionDetails
This commit is contained in:
parent
628224a5ee
commit
bea555e61d
@ -340,6 +340,8 @@ func createTestWallet(tempTestDir string, miningNode *rpctest.Harness,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testDualFundingReservationWorkflow(miner *rpctest.Harness, wallet *lnwallet.LightningWallet, t *testing.T) {
|
func testDualFundingReservationWorkflow(miner *rpctest.Harness, wallet *lnwallet.LightningWallet, t *testing.T) {
|
||||||
|
t.Log("Running dual reservation workflow test")
|
||||||
|
|
||||||
// Create the bob-test wallet which will be the other side of our funding
|
// Create the bob-test wallet which will be the other side of our funding
|
||||||
// channel.
|
// channel.
|
||||||
fundingAmount := btcutil.Amount(5 * 1e8)
|
fundingAmount := btcutil.Amount(5 * 1e8)
|
||||||
@ -506,6 +508,8 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness, wallet *lnwallet
|
|||||||
func testFundingTransactionLockedOutputs(miner *rpctest.Harness,
|
func testFundingTransactionLockedOutputs(miner *rpctest.Harness,
|
||||||
wallet *lnwallet.LightningWallet, t *testing.T) {
|
wallet *lnwallet.LightningWallet, t *testing.T) {
|
||||||
|
|
||||||
|
t.Log("Running funding txn locked outputs test")
|
||||||
|
|
||||||
// Create a single channel asking for 16 BTC total.
|
// Create a single channel asking for 16 BTC total.
|
||||||
fundingAmount := btcutil.Amount(8 * 1e8)
|
fundingAmount := btcutil.Amount(8 * 1e8)
|
||||||
_, err := wallet.InitChannelReservation(fundingAmount, fundingAmount,
|
_, err := wallet.InitChannelReservation(fundingAmount, fundingAmount,
|
||||||
@ -534,6 +538,8 @@ func testFundingTransactionLockedOutputs(miner *rpctest.Harness,
|
|||||||
func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness,
|
func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness,
|
||||||
wallet *lnwallet.LightningWallet, t *testing.T) {
|
wallet *lnwallet.LightningWallet, t *testing.T) {
|
||||||
|
|
||||||
|
t.Log("Running funding insufficient funds tests")
|
||||||
|
|
||||||
// Create a reservation for 44 BTC.
|
// Create a reservation for 44 BTC.
|
||||||
fundingAmount := btcutil.Amount(44 * 1e8)
|
fundingAmount := btcutil.Amount(44 * 1e8)
|
||||||
chanReservation, err := wallet.InitChannelReservation(fundingAmount,
|
chanReservation, err := wallet.InitChannelReservation(fundingAmount,
|
||||||
@ -583,6 +589,8 @@ func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness,
|
|||||||
func testCancelNonExistantReservation(miner *rpctest.Harness,
|
func testCancelNonExistantReservation(miner *rpctest.Harness,
|
||||||
wallet *lnwallet.LightningWallet, t *testing.T) {
|
wallet *lnwallet.LightningWallet, t *testing.T) {
|
||||||
|
|
||||||
|
t.Log("Running cancel reservation tests")
|
||||||
|
|
||||||
// Create our own reservation, give it some ID.
|
// Create our own reservation, give it some ID.
|
||||||
res := lnwallet.NewChannelReservation(1000, 1000, 5000, wallet, 22, numReqConfs)
|
res := lnwallet.NewChannelReservation(1000, 1000, 5000, wallet, 22, numReqConfs)
|
||||||
|
|
||||||
@ -596,6 +604,8 @@ func testCancelNonExistantReservation(miner *rpctest.Harness,
|
|||||||
func testSingleFunderReservationWorkflowInitiator(miner *rpctest.Harness,
|
func testSingleFunderReservationWorkflowInitiator(miner *rpctest.Harness,
|
||||||
lnwallet *lnwallet.LightningWallet, t *testing.T) {
|
lnwallet *lnwallet.LightningWallet, t *testing.T) {
|
||||||
|
|
||||||
|
t.Log("Running single funder workflow initiator test")
|
||||||
|
|
||||||
// For this scenario, we (lnwallet) will be the channel initiator while bob
|
// For this scenario, we (lnwallet) will be the channel initiator while bob
|
||||||
// will be the recipient.
|
// will be the recipient.
|
||||||
|
|
||||||
@ -727,6 +737,8 @@ func testSingleFunderReservationWorkflowInitiator(miner *rpctest.Harness,
|
|||||||
func testSingleFunderReservationWorkflowResponder(miner *rpctest.Harness,
|
func testSingleFunderReservationWorkflowResponder(miner *rpctest.Harness,
|
||||||
wallet *lnwallet.LightningWallet, t *testing.T) {
|
wallet *lnwallet.LightningWallet, t *testing.T) {
|
||||||
|
|
||||||
|
t.Log("Running single funder workflow responder test")
|
||||||
|
|
||||||
// For this scenario, bob will initiate the channel, while we simply act as
|
// For this scenario, bob will initiate the channel, while we simply act as
|
||||||
// the responder.
|
// the responder.
|
||||||
capacity := btcutil.Amount(4 * 1e8)
|
capacity := btcutil.Amount(4 * 1e8)
|
||||||
@ -878,6 +890,216 @@ func testFundingReservationInvalidCounterpartySigs(miner *rpctest.Harness, lnwal
|
|||||||
func testFundingTransactionTxFees(miner *rpctest.Harness, lnwallet *lnwallet.LightningWallet, t *testing.T) {
|
func testFundingTransactionTxFees(miner *rpctest.Harness, lnwallet *lnwallet.LightningWallet, t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testListTransactionDetails(miner *rpctest.Harness, wallet *lnwallet.LightningWallet, t *testing.T) {
|
||||||
|
t.Log("Running list transaction details test")
|
||||||
|
|
||||||
|
// Create 5 new outputs spendable by the wallet.
|
||||||
|
const numTxns = 5
|
||||||
|
const outputAmt = btcutil.SatoshiPerBitcoin
|
||||||
|
txids := make(map[wire.ShaHash]struct{})
|
||||||
|
for i := 0; i < numTxns; i++ {
|
||||||
|
addr, err := wallet.NewAddress(lnwallet.WitnessPubKey, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create new address: %v", err)
|
||||||
|
}
|
||||||
|
script, err := txscript.PayToAddrScript(addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create output script: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &wire.TxOut{outputAmt, script}
|
||||||
|
txid, err := miner.CoinbaseSpend([]*wire.TxOut{output})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to send coinbase: %v", err)
|
||||||
|
}
|
||||||
|
txids[*txid] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate 10 blocks to mine all the transactions created above.
|
||||||
|
const numBlocksMined = 10
|
||||||
|
blocks, err := miner.Node.Generate(numBlocksMined)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to mine blocks: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, fetch all the current transaction details.
|
||||||
|
// TODO(roasbeef): use ntfn client here instead?
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
txDetails, err := wallet.ListTransactionDetails()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to fetch tx details: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Each of the transactions created above should be found with the
|
||||||
|
// proper details populated.
|
||||||
|
for _, txDetail := range txDetails {
|
||||||
|
if _, ok := txids[txDetail.Hash]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if txDetail.NumConfirmations != numBlocksMined {
|
||||||
|
t.Fatalf("num confs incorrect, got %v expected %v",
|
||||||
|
txDetail.NumConfirmations, numBlocksMined)
|
||||||
|
}
|
||||||
|
if txDetail.Value != outputAmt {
|
||||||
|
t.Fatalf("tx value incorrect, got %v expected %v",
|
||||||
|
txDetail.Value, outputAmt)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(txDetail.BlockHash[:], blocks[0][:]) {
|
||||||
|
t.Fatalf("block hash mismatch, got %v expected %v",
|
||||||
|
txDetail.BlockHash, blocks[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
delete(txids, txDetail.Hash)
|
||||||
|
}
|
||||||
|
if len(txids) != 0 {
|
||||||
|
t.Fatalf("all transactions not found in details!")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next create a transaction paying to an output which isn't under the
|
||||||
|
// wallet's control.
|
||||||
|
b := txscript.NewScriptBuilder()
|
||||||
|
b.AddOp(txscript.OP_0)
|
||||||
|
outputScript, err := b.Script()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to make output script: %v", err)
|
||||||
|
}
|
||||||
|
burnOutput := wire.NewTxOut(outputAmt, outputScript)
|
||||||
|
burnTXID, err := wallet.SendOutputs([]*wire.TxOut{burnOutput})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create burn tx: %v", err)
|
||||||
|
}
|
||||||
|
burnBlock, err := miner.Node.Generate(1)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to mine block: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the transaction details again, the new transaction should be
|
||||||
|
// shown as debiting from the wallet's balance.
|
||||||
|
time.Sleep(time.Second * 2)
|
||||||
|
txDetails, err = wallet.ListTransactionDetails()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to fetch tx details: %v", err)
|
||||||
|
}
|
||||||
|
var burnTxFound bool
|
||||||
|
for _, txDetail := range txDetails {
|
||||||
|
if !bytes.Equal(txDetail.Hash[:], burnTXID[:]) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
burnTxFound = true
|
||||||
|
if txDetail.NumConfirmations != 1 {
|
||||||
|
t.Fatalf("num confs incorrect, got %v expected %v",
|
||||||
|
txDetail.NumConfirmations, 1)
|
||||||
|
}
|
||||||
|
if txDetail.Value >= -outputAmt {
|
||||||
|
t.Fatalf("tx value incorrect, got %v expected %v",
|
||||||
|
txDetail.Value, -outputAmt)
|
||||||
|
}
|
||||||
|
if !bytes.Equal(txDetail.BlockHash[:], burnBlock[0][:]) {
|
||||||
|
t.Fatalf("block hash mismatch, got %v expected %v",
|
||||||
|
txDetail.BlockHash, burnBlock[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !burnTxFound {
|
||||||
|
t.Fatalf("tx burning btc not found")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTransactionSubscriptions(miner *rpctest.Harness, w *lnwallet.LightningWallet, t *testing.T) {
|
||||||
|
t.Log("Running transaction subscriptions test")
|
||||||
|
|
||||||
|
// First, check to see if this wallet meets the TransactionNotifier
|
||||||
|
// interface, if not then we'll skip this test for this particular
|
||||||
|
// implementation of the WalletController.
|
||||||
|
txClient, err := w.SubscribeTransactions()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to generate tx subscription: %v")
|
||||||
|
}
|
||||||
|
defer txClient.Cancel()
|
||||||
|
|
||||||
|
const (
|
||||||
|
outputAmt = btcutil.SatoshiPerBitcoin
|
||||||
|
numTxns = 3
|
||||||
|
)
|
||||||
|
unconfirmedNtfns := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < numTxns; i++ {
|
||||||
|
txDetail := <-txClient.UnconfirmedTransactions()
|
||||||
|
if txDetail.NumConfirmations != 0 {
|
||||||
|
t.Fatalf("incorrect number of confs, expected %v got %v",
|
||||||
|
0, txDetail.NumConfirmations)
|
||||||
|
}
|
||||||
|
if txDetail.Value != outputAmt {
|
||||||
|
t.Fatalf("incorrect output amt, expected %v got %v",
|
||||||
|
outputAmt, txDetail.Value)
|
||||||
|
}
|
||||||
|
if txDetail.BlockHash != nil {
|
||||||
|
t.Fatalf("block hash should be nil, is instead %v",
|
||||||
|
txDetail.BlockHash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
close(unconfirmedNtfns)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Next, fetch a fresh address from the wallet, create 3 new outputs
|
||||||
|
// with the pkScript.
|
||||||
|
for i := 0; i < numTxns; i++ {
|
||||||
|
addr, err := w.NewAddress(lnwallet.WitnessPubKey, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create new address: %v", err)
|
||||||
|
}
|
||||||
|
script, err := txscript.PayToAddrScript(addr)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to create output script: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
output := &wire.TxOut{outputAmt, script}
|
||||||
|
if _, err := miner.CoinbaseSpend([]*wire.TxOut{output}); err != nil {
|
||||||
|
t.Fatalf("unable to send coinbase: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should receive a notification for all three transactions
|
||||||
|
// generated above.
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Second * 5):
|
||||||
|
t.Fatalf("transactions not received after 3 seconds")
|
||||||
|
case <-unconfirmedNtfns: // Fall through on successs
|
||||||
|
}
|
||||||
|
|
||||||
|
confirmedNtfns := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
for i := 0; i < numTxns; i++ {
|
||||||
|
txDetail := <-txClient.ConfirmedTransactions()
|
||||||
|
if txDetail.NumConfirmations != 1 {
|
||||||
|
t.Fatalf("incorrect number of confs, expected %v got %v",
|
||||||
|
0, txDetail.NumConfirmations)
|
||||||
|
}
|
||||||
|
if txDetail.Value != outputAmt {
|
||||||
|
t.Fatalf("incorrect output amt, expected %v got %v",
|
||||||
|
outputAmt, txDetail.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
close(confirmedNtfns)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Next mine a single block, all the transactions generated above
|
||||||
|
// should be included.
|
||||||
|
if _, err := miner.Node.Generate(1); err != nil {
|
||||||
|
t.Fatalf("unable to generate block: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We should receive a notification for all three transactions
|
||||||
|
// since they should be mined in the next block.
|
||||||
|
select {
|
||||||
|
case <-time.After(time.Second * 5):
|
||||||
|
t.Fatalf("transactions not received after 3 seconds")
|
||||||
|
case <-confirmedNtfns: // Fall through on successs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var walletTests = []func(miner *rpctest.Harness, w *lnwallet.LightningWallet, test *testing.T){
|
var walletTests = []func(miner *rpctest.Harness, w *lnwallet.LightningWallet, test *testing.T){
|
||||||
testDualFundingReservationWorkflow,
|
testDualFundingReservationWorkflow,
|
||||||
testSingleFunderReservationWorkflowInitiator,
|
testSingleFunderReservationWorkflowInitiator,
|
||||||
@ -885,6 +1107,8 @@ var walletTests = []func(miner *rpctest.Harness, w *lnwallet.LightningWallet, te
|
|||||||
testFundingTransactionLockedOutputs,
|
testFundingTransactionLockedOutputs,
|
||||||
testFundingCancellationNotEnoughFunds,
|
testFundingCancellationNotEnoughFunds,
|
||||||
testFundingReservationInvalidCounterpartySigs,
|
testFundingReservationInvalidCounterpartySigs,
|
||||||
|
testTransactionSubscriptions,
|
||||||
|
testListTransactionDetails,
|
||||||
}
|
}
|
||||||
|
|
||||||
type testLnWallet struct {
|
type testLnWallet struct {
|
||||||
@ -893,7 +1117,6 @@ type testLnWallet struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func clearWalletState(w *lnwallet.LightningWallet) error {
|
func clearWalletState(w *lnwallet.LightningWallet) error {
|
||||||
// TODO(roasbeef): should also restore outputs to original state.
|
|
||||||
w.ResetReservations()
|
w.ResetReservations()
|
||||||
|
|
||||||
return w.ChannelDB.Wipe()
|
return w.ChannelDB.Wipe()
|
||||||
|
Loading…
Reference in New Issue
Block a user