Skip to content

Commit

Permalink
Merge pull request #4344 from matheusdtech/lntest-improvs
Browse files Browse the repository at this point in the history
lntest+itest improvements
  • Loading branch information
Roasbeef committed Jul 16, 2020
2 parents b907035 + 0e73d2d commit 26cff10
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 73 deletions.
2 changes: 2 additions & 0 deletions lnrpc/walletrpc/walletkit_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -678,6 +678,8 @@ func (w *WalletKit) BumpFee(ctx context.Context,
return nil, err
}

log.Debugf("Attempting to CPFP outpoint %s", op)

// Since we're unable to perform a bump through RBF, we'll assume the
// user is attempting to bump an unconfirmed transaction's fee rate by
// sweeping an output within it under control of the wallet with a
Expand Down
12 changes: 6 additions & 6 deletions lntest/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -1128,7 +1128,8 @@ func (n *NetworkHarness) CloseChannel(ctx context.Context,
// within the network.
closeResp, err := closeRespStream.Recv()
if err != nil {
errChan <- err
errChan <- fmt.Errorf("unable to recv() from close "+
"stream: %v", err)
return
}
pendingClose, ok := closeResp.Update.(*lnrpc.CloseStatusUpdate_ClosePending)
Expand All @@ -1140,11 +1141,13 @@ func (n *NetworkHarness) CloseChannel(ctx context.Context,

closeTxid, err := chainhash.NewHash(pendingClose.ClosePending.Txid)
if err != nil {
errChan <- err
errChan <- fmt.Errorf("unable to decode closeTxid: "+
"%v", err)
return
}
if err := n.WaitForTxBroadcast(ctx, *closeTxid); err != nil {
errChan <- err
errChan <- fmt.Errorf("error while waiting for "+
"broadcast tx: %v", err)
return
}
fin <- closeTxid
Expand All @@ -1153,9 +1156,6 @@ func (n *NetworkHarness) CloseChannel(ctx context.Context,
// Wait until either the deadline for the context expires, an error
// occurs, or the channel close update is received.
select {
case <-ctx.Done():
return nil, nil, fmt.Errorf("timeout reached before channel close " +
"initiated")
case err := <-errChan:
return nil, nil, err
case closeTxid := <-fin:
Expand Down
63 changes: 58 additions & 5 deletions lntest/itest/lnd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,39 @@ func assertTxInBlock(t *harnessTest, block *wire.MsgBlock, txid *chainhash.Hash)
t.Fatalf("tx was not included in block")
}

func assertWalletUnspent(t *harnessTest, node *lntest.HarnessNode, out *lnrpc.OutPoint) {
t.t.Helper()

err := wait.NoError(func() error {
ctxt, cancel := context.WithTimeout(context.Background(), defaultTimeout)
defer cancel()
unspent, err := node.ListUnspent(ctxt, &lnrpc.ListUnspentRequest{})
if err != nil {
return err
}

err = errors.New("tx with wanted txhash never found")
for _, utxo := range unspent.Utxos {
if !bytes.Equal(utxo.Outpoint.TxidBytes, out.TxidBytes) {
continue
}

err = errors.New("wanted output is not a wallet utxo")
if utxo.Outpoint.OutputIndex != out.OutputIndex {
continue
}

return nil
}

return err
}, defaultTimeout)
if err != nil {
t.Fatalf("outpoint %s not unspent by %s's wallet: %v", out,
node.Name(), err)
}
}

func rpcPointToWirePoint(t *harnessTest, chanPoint *lnrpc.ChannelPoint) wire.OutPoint {
txid, err := lnd.GetChanPointFundingTxid(chanPoint)
if err != nil {
Expand Down Expand Up @@ -9055,6 +9088,29 @@ func testRevokedCloseRetributionAltruistWatchtower(net *lntest.NetworkHarness,

davePreSweepBalance := daveBalResp.ConfirmedBalance

// Wait until the backup has been accepted by the watchtower before
// shutting down Dave.
err = wait.NoError(func() error {
ctxt, cancel := context.WithTimeout(ctxb, defaultTimeout)
defer cancel()
bkpStats, err := dave.WatchtowerClient.Stats(ctxt, &wtclientrpc.StatsRequest{})
if err != nil {
return err

}
if bkpStats == nil {
return errors.New("no active backup sessions")
}
if bkpStats.NumBackups == 0 {
return errors.New("no backups accepted")
}

return nil
}, defaultTimeout)
if err != nil {
t.Fatalf("unable to verify backup task completed: %v", err)
}

// Shutdown Dave to simulate going offline for an extended period of
// time. Once he's not watching, Carol will try to breach the channel.
restart, err := net.SuspendNode(dave)
Expand Down Expand Up @@ -9083,9 +9139,6 @@ func testRevokedCloseRetributionAltruistWatchtower(net *lntest.NetworkHarness,
t.Fatalf("db copy failed: %v", carolChan.NumUpdates)
}

// TODO(conner): add hook for backup completion
time.Sleep(3 * time.Second)

// Now force Carol to execute a *force* channel closure by unilaterally
// broadcasting his current channel state. This is actually the
// commitment transaction of a prior *revoked* state, so he'll soon
Expand Down Expand Up @@ -13367,7 +13420,7 @@ func testChannelBackupUpdates(net *lntest.NetworkHarness, t *harnessTest) {
numChans := 2
chanAmt := btcutil.Amount(1000000)
for i := 0; i < numChans; i++ {
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, carol,
lntest.OpenChannelParams{
Expand Down Expand Up @@ -13501,7 +13554,7 @@ func testExportChannelBackup(net *lntest.NetworkHarness, t *harnessTest) {
numChans := 2
chanAmt := btcutil.Amount(1000000)
for i := 0; i < numChans; i++ {
ctxt, _ := context.WithTimeout(ctxb, defaultTimeout)
ctxt, _ := context.WithTimeout(ctxb, channelOpenTimeout)
chanPoint := openChannelAndAssert(
ctxt, t, net, net.Alice, carol,
lntest.OpenChannelParams{
Expand Down
7 changes: 5 additions & 2 deletions lntest/itest/onchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,15 @@ func testCPFP(net *lntest.NetworkHarness, t *harnessTest) {
t.Fatalf("bob's output was not found within the transaction")
}

// We'll attempt to bump the fee of this transaction by performing a
// CPFP from Alice's point of view.
// Wait until bob has seen the tx and considers it as owned.
op := &lnrpc.OutPoint{
TxidBytes: txid[:],
OutputIndex: uint32(bobOutputIdx),
}
assertWalletUnspent(t, net.Bob, op)

// We'll attempt to bump the fee of this transaction by performing a
// CPFP from Alice's point of view.
bumpFeeReq := &walletrpc.BumpFeeRequest{
Outpoint: op,
SatPerByte: uint32(sweep.DefaultMaxFeeRate.FeePerKVByte() / 2000),
Expand Down
108 changes: 48 additions & 60 deletions lntest/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -625,9 +625,10 @@ func (hn *HarnessNode) initLightningClient(conn *grpc.ClientConn) error {
// Launch the watcher that will hook into graph related topology change
// from the PoV of this node.
hn.wg.Add(1)
go hn.lightningNetworkWatcher()
subscribed := make(chan error)
go hn.lightningNetworkWatcher(subscribed)

return nil
return <-subscribed
}

// FetchNodeInfo queries an unlocked node to retrieve its public key.
Expand Down Expand Up @@ -688,63 +689,61 @@ func (hn *HarnessNode) writePidFile() error {
func (hn *HarnessNode) ReadMacaroon(macPath string, timeout time.Duration) (
*macaroon.Macaroon, error) {

// Wait until macaroon file is created before using it.
macTimeout := time.After(timeout)
for !fileExists(macPath) {
select {
case <-macTimeout:
return nil, fmt.Errorf("timeout waiting for macaroon "+
"file %s to be created after %d seconds",
macPath, timeout/time.Second)
case <-time.After(100 * time.Millisecond):
// Wait until macaroon file is created and has valid content before
// using it.
var mac *macaroon.Macaroon
err := wait.NoError(func() error {
macBytes, err := ioutil.ReadFile(macPath)
if err != nil {
return fmt.Errorf("error reading macaroon file: %v", err)
}
}

// Now that we know the file exists, read it and return the macaroon.
macBytes, err := ioutil.ReadFile(macPath)
if err != nil {
return nil, err
}
mac := &macaroon.Macaroon{}
if err = mac.UnmarshalBinary(macBytes); err != nil {
return nil, err
}
return mac, nil
newMac := &macaroon.Macaroon{}
if err = newMac.UnmarshalBinary(macBytes); err != nil {
return fmt.Errorf("error unmarshalling macaroon "+
"file: %v", err)
}
mac = newMac

return nil
}, timeout)

return mac, err
}

// ConnectRPCWithMacaroon uses the TLS certificate and given macaroon to
// create a gRPC client connection.
func (hn *HarnessNode) ConnectRPCWithMacaroon(mac *macaroon.Macaroon) (
*grpc.ClientConn, error) {

// Wait until TLS certificate is created before using it, up to 30 sec.
tlsTimeout := time.After(DefaultTimeout)
for !fileExists(hn.Cfg.TLSCertPath) {
select {
case <-tlsTimeout:
return nil, fmt.Errorf("timeout waiting for TLS cert " +
"file to be created")
case <-time.After(100 * time.Millisecond):
}
// Wait until TLS certificate is created and has valid content before
// using it, up to 30 sec.
var tlsCreds credentials.TransportCredentials
err := wait.NoError(func() error {
var err error
tlsCreds, err = credentials.NewClientTLSFromFile(
hn.Cfg.TLSCertPath, "",
)
return err
}, DefaultTimeout)
if err != nil {
return nil, fmt.Errorf("error reading TLS cert: %v", err)
}

opts := []grpc.DialOption{grpc.WithBlock()}
tlsCreds, err := credentials.NewClientTLSFromFile(
hn.Cfg.TLSCertPath, "",
)
if err != nil {
return nil, err
opts := []grpc.DialOption{
grpc.WithBlock(),
grpc.WithTransportCredentials(tlsCreds),
}
opts = append(opts, grpc.WithTransportCredentials(tlsCreds))

ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()

if mac == nil {
return grpc.Dial(hn.Cfg.RPCAddr(), opts...)
return grpc.DialContext(ctx, hn.Cfg.RPCAddr(), opts...)
}
macCred := macaroons.NewMacaroonCredential(mac)
opts = append(opts, grpc.WithPerRPCCredentials(macCred))

ctx, cancel := context.WithTimeout(context.Background(), DefaultTimeout)
defer cancel()
return grpc.DialContext(ctx, hn.Cfg.RPCAddr(), opts...)
}

Expand Down Expand Up @@ -873,7 +872,7 @@ func getChanPointFundingTxid(chanPoint *lnrpc.ChannelPoint) ([]byte, error) {
// closed or opened within the network. In order to dispatch these
// notifications, the GraphTopologySubscription client exposed as part of the
// gRPC interface is used.
func (hn *HarnessNode) lightningNetworkWatcher() {
func (hn *HarnessNode) lightningNetworkWatcher(subscribed chan error) {
defer hn.wg.Done()

graphUpdates := make(chan *lnrpc.GraphTopologyUpdate)
Expand All @@ -883,16 +882,16 @@ func (hn *HarnessNode) lightningNetworkWatcher() {

req := &lnrpc.GraphTopologySubscription{}
ctx, cancelFunc := context.WithCancel(context.Background())
defer cancelFunc()
topologyClient, err := hn.SubscribeChannelGraph(ctx, req)
if err != nil {
// We panic here in case of an error as failure to
// create the topology client will cause all subsequent
// tests to fail.
panic(fmt.Errorf("unable to create topology "+
"client: %v", err))
msg := fmt.Sprintf("%s(%d): unable to create topology "+
"client: %v (%s)", hn.Name(), hn.NodeID, err,
time.Now().String())
subscribed <- fmt.Errorf(msg)
return
}

defer cancelFunc()
close(subscribed)

for {
update, err := topologyClient.Recv()
Expand Down Expand Up @@ -1157,14 +1156,3 @@ func (hn *HarnessNode) WaitForBalance(expectedBalance btcutil.Amount, confirmed

return nil
}

// fileExists reports whether the named file or directory exists.
// This function is taken from https://github.com/btcsuite/btcd
func fileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}

0 comments on commit 26cff10

Please sign in to comment.