lnwallet: add reorg test

This commit is contained in:
Alex 2017-06-29 10:48:10 -06:00 committed by Olaoluwa Osuntokun
parent 126c73af59
commit 0994852396

@ -24,6 +24,7 @@ import (
"github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/chaincfg"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/rpcclient"
_ "github.com/roasbeef/btcwallet/walletdb/bdb"
"github.com/roasbeef/btcd/btcec"
@ -144,6 +145,11 @@ func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet,
// Using the mining node, spend from a coinbase output numOutputs to
// give us btcPerOutput with each output.
satoshiPerOutput := int64(btcPerOutput * 1e8)
expectedBalance, err := w.ConfirmedBalance(1, false)
if err != nil {
return err
}
expectedBalance += btcutil.Amount(satoshiPerOutput * int64(numOutputs))
addrs := make([]btcutil.Address, 0, numOutputs)
for i := 0; i < numOutputs; i++ {
// Grab a fresh address from the wallet to house this output.
@ -179,7 +185,6 @@ func loadTestCredits(miner *rpctest.Harness, w *lnwallet.LightningWallet,
// Wait until the wallet has finished syncing up to the main chain.
ticker := time.NewTicker(100 * time.Millisecond)
expectedBalance := btcutil.Amount(satoshiPerOutput * int64(numOutputs))
for range ticker.C {
balance, err := w.ConfirmedBalance(1, false)
@ -1079,6 +1084,155 @@ func testSignOutputUsingTweaks(r *rpctest.Harness,
}
}
func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet,
_ *lnwallet.LightningWallet, t *testing.T) {
// We first mine a few blocks to ensure any transactions still in the
// mempool confirm, and then get the original balance, before a
// reorganization that doesn't invalidate any existing transactions or
// create any new non-coinbase transactions. We'll then check if it's
// the same after the empty reorg.
_, err := r.Node.Generate(5)
if err != nil {
t.Fatalf("unable to generate blocks on passed node: %v", err)
}
// Give wallet time to catch up.
err = waitForWalletSync(w)
if err != nil {
t.Fatalf("unable to sync wallet: %v", err)
}
// Send some money from the miner to the wallet
err = loadTestCredits(r, w, 20, 4)
if err != nil {
t.Fatalf("unable to send money to lnwallet: %v", err)
}
// Send some money from the wallet back to the miner.
// Grab a fresh address from the miner to house this output.
minerAddr, err := r.NewAddress()
if err != nil {
t.Fatalf("unable to generate address for miner: %v", err)
}
script, err := txscript.PayToAddrScript(minerAddr)
if err != nil {
t.Fatalf("unable to create pay to addr script: %v", err)
}
output := &wire.TxOut{
Value: 1e8,
PkScript: script,
}
if _, err = w.SendOutputs([]*wire.TxOut{output}); err != nil {
t.Fatalf("unable to send outputs: %v", err)
}
_, err = r.Node.Generate(50)
if err != nil {
t.Fatalf("unable to generate blocks on passed node: %v", err)
}
// Give wallet time to catch up.
err = waitForWalletSync(w)
if err != nil {
t.Fatalf("unable to sync wallet: %v", err)
}
// Get the original balance.
origBalance, err := w.ConfirmedBalance(1, false)
if err != nil {
t.Fatalf("unable to query for balance: %v", err)
}
// Now we cause a reorganization as follows.
// Step 1: create a new miner and start it.
r2, err := rpctest.New(r.ActiveNet, nil, nil)
if err != nil {
t.Fatalf("unable to create mining node: %v", err)
}
err = r2.SetUp(false, 0)
if err != nil {
t.Fatalf("unable to set up mining node: %v", err)
}
defer r2.TearDown()
newBalance, err := w.ConfirmedBalance(1, false)
if err != nil {
t.Fatalf("unable to query for balance: %v", err)
}
if origBalance != newBalance {
t.Fatalf("wallet balance incorrect, should have %v, "+
"instead have %v", origBalance, newBalance)
}
// Step 2: connect the miner to the passed miner and wait for
// synchronization.
err = r2.Node.AddNode(r.P2PAddress(), rpcclient.ANAdd)
if err != nil {
t.Fatalf("unable to connect mining nodes together: %v", err)
}
err = rpctest.JoinNodes([]*rpctest.Harness{r2, r}, rpctest.Blocks)
if err != nil {
t.Fatalf("unable to synchronize mining nodes: %v", err)
}
// Step 3: Do a set of reorgs by disconecting the two miners, mining
// one block on the passed miner and two on the created miner,
// connecting them, and waiting for them to sync.
for i := 0; i < 5; i++ {
err = r2.Node.AddNode(r.P2PAddress(), rpcclient.ANRemove)
if err != nil {
t.Fatalf("unable to disconnect mining nodes: %v", err)
}
// Wait for disconnection
for true {
peers, err := r2.Node.GetPeerInfo()
if err != nil {
t.Fatalf("unable to get peer info: %v", err)
}
if len(peers) == 0 {
break
}
time.Sleep(100 * time.Millisecond)
}
_, err = r.Node.Generate(2)
if err != nil {
t.Fatalf("unable to generate blocks on passed node: %v",
err)
}
_, err = r2.Node.Generate(3)
if err != nil {
t.Fatalf("unable to generate blocks on created node: %v",
err)
}
// Step 5: Reconnect the miners and wait for them to synchronize.
err = r2.Node.AddNode(r.P2PAddress(), rpcclient.ANAdd)
if err != nil {
t.Fatalf("unable to connect mining nodes together: %v",
err)
}
err = rpctest.JoinNodes([]*rpctest.Harness{r2, r},
rpctest.Blocks)
if err != nil {
t.Fatalf("unable to synchronize mining nodes: %v", err)
}
// Give wallet time to catch up.
err = waitForWalletSync(w)
if err != nil {
t.Fatalf("unable to sync wallet: %v", err)
}
}
// Now we check that the wallet balance stays the same.
newBalance, err = w.ConfirmedBalance(1, false)
if err != nil {
t.Fatalf("unable to query for balance: %v", err)
}
if origBalance != newBalance {
t.Fatalf("wallet balance incorrect, should have %v, "+
"instead have %v", origBalance, newBalance)
}
}
type walletTestCase struct {
name string
test func(miner *rpctest.Harness, alice, bob *lnwallet.LightningWallet,
@ -1118,6 +1272,10 @@ var walletTests = []walletTestCase{
name: "test cancel non-existent reservation",
test: testCancelNonExistantReservation,
},
{
name: "reorg wallet balance",
test: testReorgWalletBalance,
},
}
func clearWalletStates(a, b *lnwallet.LightningWallet) error {
@ -1131,6 +1289,25 @@ func clearWalletStates(a, b *lnwallet.LightningWallet) error {
return b.Cfg.Database.Wipe()
}
func waitForWalletSync(w *lnwallet.LightningWallet) error {
var synced bool
var err error
timeout := time.After(10 * time.Second)
for !synced {
synced, err = w.IsSynced()
if err != nil {
return err
}
select {
case <-timeout:
return fmt.Errorf("timeout after 10s")
default:
}
time.Sleep(100 * time.Millisecond)
}
return nil
}
// TestInterfaces tests all registered interfaces with a unified set of tests
// which excersie each of the required methods found within the WalletController
// interface.