From 0f98030186d09e47a2302013a54904bce2cf8706 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:01:32 -0700 Subject: [PATCH 01/41] chainntnfs: update RegisterConfirmationsNtfn to also pass in pkScript of output In this commit, we prep for an upcoming final change to BIP 158. The change results in the txid no longer being included in the regular filter. As a result, neutrino will now need to match based on the output script of the transaction that we wish to receive confirmation notifications for. --- chainntnfs/interface.go | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/chainntnfs/interface.go b/chainntnfs/interface.go index 57870f64..37c370e4 100644 --- a/chainntnfs/interface.go +++ b/chainntnfs/interface.go @@ -20,19 +20,21 @@ import ( // resource type ChainNotifier interface { // RegisterConfirmationsNtfn registers an intent to be notified once - // txid reaches numConfs confirmations. The returned ConfirmationEvent - // should properly notify the client once the specified number of - // confirmations has been reached for the txid, as well as if the - // original tx gets re-org'd out of the mainchain. The heightHint - // parameter is provided as a convenience to light clients. The - // heightHint denotes the earliest height in the blockchain in which the - // target txid _could_ have been included in the chain. This can be - // used to bound the search space when checking to see if a - // notification can immediately be dispatched due to historical data. + // txid reaches numConfs confirmations. We also pass in the pkScript as + // the default light client instead needs to match on scripts created + // in the block. The returned ConfirmationEvent should properly notify + // the client once the specified number of confirmations has been + // reached for the txid, as well as if the original tx gets re-org'd + // out of the mainchain. The heightHint parameter is provided as a + // convenience to light clients. The heightHint denotes the earliest + // height in the blockchain in which the target txid _could_ have been + // included in the chain. This can be used to bound the search space + // when checking to see if a notification can immediately be dispatched + // due to historical data. // // NOTE: Dispatching notifications to multiple clients subscribed to // the same (txid, numConfs) tuple MUST be supported. - RegisterConfirmationsNtfn(txid *chainhash.Hash, numConfs, + RegisterConfirmationsNtfn(txid *chainhash.Hash, pkScript []byte, numConfs, heightHint uint32) (*ConfirmationEvent, error) // RegisterSpendNtfn registers an intent to be notified once the target From 6387c9e085aee5146c7762fe595a419220a5db47 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:02:17 -0700 Subject: [PATCH 02/41] chainntnfs: update interface to use pkScript for conf notifications --- chainntnfs/interface_test.go | 85 +++++++++++++++++++++--------------- 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/chainntnfs/interface_test.go b/chainntnfs/interface_test.go index c9c83c20..e4fe3fbd 100644 --- a/chainntnfs/interface_test.go +++ b/chainntnfs/interface_test.go @@ -58,10 +58,10 @@ var ( testAddr = addrPk.AddressPubKeyHash() ) -func getTestTxId(miner *rpctest.Harness) (*chainhash.Hash, error) { +func getTestTxIdAndScript(miner *rpctest.Harness) (*chainhash.Hash, []byte, error) { script, err := txscript.PayToAddrScript(testAddr) if err != nil { - return nil, err + return nil, nil, err } outputs := []*wire.TxOut{ @@ -70,7 +70,13 @@ func getTestTxId(miner *rpctest.Harness) (*chainhash.Hash, error) { PkScript: script, }, } - return miner.SendOutputs(outputs, 10) + + txid, err := miner.SendOutputs(outputs, 10) + if err != nil { + return nil, nil, err + } + + return txid, script, nil } func waitForMempoolTx(r *rpctest.Harness, txid *chainhash.Hash) error { @@ -116,7 +122,7 @@ func testSingleConfirmationNotification(miner *rpctest.Harness, // We're spending from a coinbase output here, so we use the dedicated // function. - txid, err := getTestTxId(miner) + txid, pkScript, err := getTestTxIdAndScript(miner) if err != nil { t.Fatalf("unable to create test tx: %v", err) } @@ -134,8 +140,9 @@ func testSingleConfirmationNotification(miner *rpctest.Harness, // Now that we have a txid, register a confirmation notification with // the chainntfn source. numConfs := uint32(1) - confIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs, - uint32(currentHeight)) + confIntent, err := notifier.RegisterConfirmationsNtfn( + txid, pkScript, numConfs, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -184,7 +191,7 @@ func testMultiConfirmationNotification(miner *rpctest.Harness, // // Again, we'll begin by creating a fresh transaction, so we can obtain // a fresh txid. - txid, err := getTestTxId(miner) + txid, pkScript, err := getTestTxIdAndScript(miner) if err != nil { t.Fatalf("unable to create test addr: %v", err) } @@ -200,8 +207,9 @@ func testMultiConfirmationNotification(miner *rpctest.Harness, } numConfs := uint32(6) - confIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs, - uint32(currentHeight)) + confIntent, err := notifier.RegisterConfirmationsNtfn( + txid, pkScript, numConfs, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -242,12 +250,13 @@ func testBatchConfirmationNotification(miner *rpctest.Harness, // verify they're each notified at the proper number of confirmations // below. for i, numConfs := range confSpread { - txid, err := getTestTxId(miner) + txid, pkScript, err := getTestTxIdAndScript(miner) if err != nil { t.Fatalf("unable to create test addr: %v", err) } - confIntent, err := notifier.RegisterConfirmationsNtfn(txid, - numConfs, uint32(currentHeight)) + confIntent, err := notifier.RegisterConfirmationsNtfn( + txid, pkScript, numConfs, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -303,7 +312,7 @@ func testBatchConfirmationNotification(miner *rpctest.Harness, func createSpendableOutput(miner *rpctest.Harness, t *testing.T) (*wire.OutPoint, []byte) { - txid, err := getTestTxId(miner) + txid, _, err := getTestTxIdAndScript(miner) if err != nil { t.Fatalf("unable to create test addr: %v", err) } @@ -554,7 +563,7 @@ func testMultiClientConfirmationNotification(miner *rpctest.Harness, // We'd like to test the case of a multiple clients registered to // receive a confirmation notification for the same transaction. - txid, err := getTestTxId(miner) + txid, pkScript, err := getTestTxIdAndScript(miner) if err != nil { t.Fatalf("unable to create test tx: %v", err) } @@ -578,8 +587,9 @@ func testMultiClientConfirmationNotification(miner *rpctest.Harness, // Register for a conf notification for the above generated txid with // numConfsClients distinct clients. for i := 0; i < numConfsClients; i++ { - confClient, err := notifier.RegisterConfirmationsNtfn(txid, - numConfs, uint32(currentHeight)) + confClient, err := notifier.RegisterConfirmationsNtfn( + txid, pkScript, numConfs, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register for confirmation: %v", err) } @@ -620,7 +630,7 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness, // spending from a coinbase output here, so we use the dedicated // function. - txid3, err := getTestTxId(miner) + txid3, pkScript3, err := getTestTxIdAndScript(miner) if err != nil { t.Fatalf("unable to create test tx: %v", err) } @@ -640,7 +650,7 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness, t.Fatalf("unable to generate block: %v", err) } - txid1, err := getTestTxId(miner) + txid1, pkScript1, err := getTestTxIdAndScript(miner) if err != nil { t.Fatalf("unable to create test tx: %v", err) } @@ -650,7 +660,7 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness, t.Fatalf("tx not relayed to miner: %v", err) } - txid2, err := getTestTxId(miner) + txid2, pkScript2, err := getTestTxIdAndScript(miner) if err != nil { t.Fatalf("unable to create test tx: %v", err) } @@ -675,8 +685,9 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness, // which is included in the last block. The height hint is the height before // the block is included. This notification should fire immediately since // only 1 confirmation is required. - ntfn1, err := notifier.RegisterConfirmationsNtfn(txid1, 1, - uint32(currentHeight)) + ntfn1, err := notifier.RegisterConfirmationsNtfn( + txid1, pkScript1, 1, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -713,8 +724,9 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness, // Register a confirmation notification for tx2, requiring 3 confirmations. // This transaction is only partially confirmed, so the notification should // not fire yet. - ntfn2, err := notifier.RegisterConfirmationsNtfn(txid2, 3, - uint32(currentHeight)) + ntfn2, err := notifier.RegisterConfirmationsNtfn( + txid2, pkScript2, 3, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -740,8 +752,9 @@ func testTxConfirmedBeforeNtfnRegistration(miner *rpctest.Harness, // Finally register a confirmation notification for tx3, requiring 1 // confirmation. Ensure that conf notifications do not refire on txs // 1 or 2. - ntfn3, err := notifier.RegisterConfirmationsNtfn(txid3, 1, - uint32(currentHeight-1)) + ntfn3, err := notifier.RegisterConfirmationsNtfn( + txid3, pkScript3, 1, uint32(currentHeight-1), + ) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -775,7 +788,7 @@ func testLazyNtfnConsumer(miner *rpctest.Harness, // Create a transaction to be notified about. We'll register for // notifications on this transaction but won't be prompt in checking them - txid, err := getTestTxId(miner) + txid, pkScript, err := getTestTxIdAndScript(miner) if err != nil { t.Fatalf("unable to create test tx: %v", err) } @@ -798,8 +811,9 @@ func testLazyNtfnConsumer(miner *rpctest.Harness, t.Fatalf("unable to generate blocks: %v", err) } - firstConfIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs, - uint32(currentHeight)) + firstConfIntent, err := notifier.RegisterConfirmationsNtfn( + txid, pkScript, numConfs, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -812,7 +826,7 @@ func testLazyNtfnConsumer(miner *rpctest.Harness, // Now make another transaction, just because we haven't checked to see // if the first transaction has confirmed doesn't mean that we shouldn't // be able to see if this transaction confirms first - txid, err = getTestTxId(miner) + txid, pkScript, err = getTestTxIdAndScript(miner) if err != nil { t.Fatalf("unable to create test tx: %v", err) } @@ -829,9 +843,9 @@ func testLazyNtfnConsumer(miner *rpctest.Harness, numConfs = 1 - secondConfIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs, - uint32(currentHeight)) - + secondConfIntent, err := notifier.RegisterConfirmationsNtfn( + txid, pkScript, numConfs, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } @@ -1150,7 +1164,7 @@ func testReorgConf(miner *rpctest.Harness, notifier chainntnfs.ChainNotifier, t.Fatalf("unable to remove node: %v", err) } - txid, err := getTestTxId(miner) + txid, pkScript, err := getTestTxIdAndScript(miner) if err != nil { t.Fatalf("unable to create test tx: %v", err) } @@ -1168,8 +1182,9 @@ func testReorgConf(miner *rpctest.Harness, notifier chainntnfs.ChainNotifier, // Now that we have a txid, register a confirmation notification with // the chainntfn source. numConfs := uint32(2) - confIntent, err := notifier.RegisterConfirmationsNtfn(txid, numConfs, - uint32(currentHeight)) + confIntent, err := notifier.RegisterConfirmationsNtfn( + txid, pkScript, numConfs, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register ntfn: %v", err) } From f45e41afc146716157340528b97a725f4a88b837 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:03:05 -0700 Subject: [PATCH 03/41] chainntnfs/bitcoindnotify: update RegisterConfirmationsNtfn to take pkScript --- chainntnfs/bitcoindnotify/bitcoind.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chainntnfs/bitcoindnotify/bitcoind.go b/chainntnfs/bitcoindnotify/bitcoind.go index 452a9025..cd34e52a 100644 --- a/chainntnfs/bitcoindnotify/bitcoind.go +++ b/chainntnfs/bitcoindnotify/bitcoind.go @@ -784,7 +784,7 @@ type confirmationNotification struct { // which will be triggered once the txid reaches numConfs number of // confirmations. func (b *BitcoindNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, - numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) { + _ []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) { ntfn := &confirmationNotification{ ConfNtfn: chainntnfs.ConfNtfn{ From e93149e576105a91eecdda1759b8c6463a212971 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:03:23 -0700 Subject: [PATCH 04/41] chainntnfs/btcdnotify: update RegisterConfirmationsNtfn to take pkScript --- chainntnfs/btcdnotify/btcd.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chainntnfs/btcdnotify/btcd.go b/chainntnfs/btcdnotify/btcd.go index fa4f1826..30ceee99 100644 --- a/chainntnfs/btcdnotify/btcd.go +++ b/chainntnfs/btcdnotify/btcd.go @@ -822,7 +822,7 @@ type confirmationNotification struct { // RegisterConfirmationsNtfn registers a notification with BtcdNotifier // which will be triggered once the txid reaches numConfs number of // confirmations. -func (b *BtcdNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, +func (b *BtcdNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, _ []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) { ntfn := &confirmationNotification{ From 21847dc691d5263cce4e3e0bd6151850375fc008 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:07:17 -0700 Subject: [PATCH 05/41] chainntnfs/neutrinonotify: update conf notifications to use pkScript In this commit, we update the implementation of conf notifications for neutrino to use the output script rather than the txid when matching blocks for relevant items. The change itself is rather minor as we just pass in the script, yet match based on the txid as normal when we go to dispatch notifications. --- chainntnfs/neutrinonotify/neutrino.go | 54 ++++++++++++++++----------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/chainntnfs/neutrinonotify/neutrino.go b/chainntnfs/neutrinonotify/neutrino.go index 7d4dc5d0..c81bfc3d 100644 --- a/chainntnfs/neutrinonotify/neutrino.go +++ b/chainntnfs/neutrinonotify/neutrino.go @@ -339,7 +339,7 @@ func (n *NeutrinoNotifier) notificationDispatcher() { // filter so we can be notified of its // future initial confirmation. rescanUpdate := []neutrino.UpdateOption{ - neutrino.AddTxIDs(*msg.TxID), + neutrino.AddAddrs(addrs...), neutrino.Rewind(currentHeight), } err = n.chainView.Update(rescanUpdate...) @@ -410,9 +410,11 @@ func (n *NeutrinoNotifier) notificationDispatcher() { } } -// historicalConfDetails looks up whether a transaction is already included in a -// block in the active chain and, if so, returns details about the confirmation. +// historicalConfDetails looks up whether a transaction is already included in +// a block in the active chain and, if so, returns details about the +// confirmation. func (n *NeutrinoNotifier) historicalConfDetails(targetHash *chainhash.Hash, + pkScript []byte, currentHeight, heightHint uint32) (*chainntnfs.TxConfirmation, error) { // Starting from the height hint, we'll walk forwards in the chain to @@ -437,14 +439,15 @@ func (n *NeutrinoNotifier) historicalConfDetails(targetHash *chainhash.Hash, // With the hash computed, we can now fetch the basic filter // for this height. - regFilter, err := n.p2pNode.GetCFilter(blockHash, - wire.GCSFilterRegular) + regFilter, err := n.p2pNode.GetCFilter( + blockHash, wire.GCSFilterRegular, + ) if err != nil { return nil, fmt.Errorf("unable to retrieve regular filter for "+ "height=%v: %v", scanHeight, err) } - // If the block has no transactions other than the coinbase + // If the block has no transactions other than the Coinbase // transaction, then the filter may be nil, so we'll continue // forward int that case. if regFilter == nil { @@ -452,9 +455,9 @@ func (n *NeutrinoNotifier) historicalConfDetails(targetHash *chainhash.Hash, } // In the case that the filter exists, we'll attempt to see if - // any element in it match our target txid. + // any element in it matches our target public key script. key := builder.DeriveKey(&blockHash) - match, err := regFilter.Match(key, targetHash[:]) + match, err := regFilter.Match(key, pkScript) if err != nil { return nil, fmt.Errorf("unable to query filter: %v", err) } @@ -504,16 +507,15 @@ func (n *NeutrinoNotifier) handleBlockConnected(newBlock *filteredBlock) error { for i, txIn := range mtx.TxIn { prevOut := txIn.PreviousOutPoint - // If this transaction indeed does spend an output which we have a - // registered notification for, then create a spend summary, finally - // sending off the details to the notification subscriber. + // If this transaction indeed does spend an output + // which we have a registered notification for, then + // create a spend summary, finally sending off the + // details to the notification subscriber. clients, ok := n.spendNotifications[prevOut] if !ok { continue } - // TODO(roasbeef): many integration tests expect spend to be - // notified within the mempool. spendDetails := &chainntnfs.SpendDetail{ SpentOutPoint: &prevOut, SpenderTxHash: &txSha, @@ -523,13 +525,16 @@ func (n *NeutrinoNotifier) handleBlockConnected(newBlock *filteredBlock) error { } for _, ntfn := range clients { - chainntnfs.Log.Infof("Dispatching spend notification for "+ - "outpoint=%v", ntfn.targetOutpoint) + chainntnfs.Log.Infof("Dispatching spend "+ + "notification for outpoint=%v", + ntfn.targetOutpoint) + ntfn.spendChan <- spendDetails - // Close spendChan to ensure that any calls to Cancel will not - // block. This is safe to do since the channel is buffered, and - // the message can still be read by the receiver. + // Close spendChan to ensure that any calls to + // Cancel will not block. This is safe to do + // since the channel is buffered, and the + // message can still be read by the receiver. close(ntfn.spendChan) } @@ -537,10 +542,12 @@ func (n *NeutrinoNotifier) handleBlockConnected(newBlock *filteredBlock) error { } } - // A new block has been connected to the main chain. - // Send out any N confirmation notifications which may - // have been triggered by this new block. - n.txConfNotifier.ConnectTip(&newBlock.hash, newBlock.height, newBlock.txns) + // A new block has been connected to the main chain. Send out any N + // confirmation notifications which may have been triggered by this new + // block. + n.txConfNotifier.ConnectTip( + &newBlock.hash, newBlock.height, newBlock.txns, + ) return nil } @@ -712,12 +719,14 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, type confirmationsNotification struct { chainntnfs.ConfNtfn heightHint uint32 + pkScript []byte } // RegisterConfirmationsNtfn registers a notification with NeutrinoNotifier // which will be triggered once the txid reaches numConfs number of // confirmations. func (n *NeutrinoNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, + pkScript []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) { ntfn := &confirmationsNotification{ @@ -728,6 +737,7 @@ func (n *NeutrinoNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, Event: chainntnfs.NewConfirmationEvent(numConfs), }, heightHint: heightHint, + pkScript: pkScript, } if err := n.txConfNotifier.Register(&ntfn.ConfNtfn); err != nil { From c707577e99b78f7f96560de74300c63ab9ee3d0f Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:07:54 -0700 Subject: [PATCH 06/41] contractcourt: update conf ntfn registartions to use pkScript not txid --- contractcourt/chain_arbitrator.go | 6 +++--- contractcourt/chain_watcher_test.go | 2 +- contractcourt/contract_resolvers.go | 12 ++++++++---- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index f13f65fd..6656ef22 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -5,13 +5,13 @@ import ( "sync" "sync/atomic" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" ) // ResolutionMsg is a message sent by resolvers to outside sub-systems once an diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go index a99fcd31..5928be66 100644 --- a/contractcourt/chain_watcher_test.go +++ b/contractcourt/chain_watcher_test.go @@ -17,7 +17,7 @@ type mockNotifier struct { spendChan chan *chainntnfs.SpendDetail } -func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, numConfs, +func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, _ []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) { return nil, nil } diff --git a/contractcourt/contract_resolvers.go b/contractcourt/contract_resolvers.go index f81e050d..541b40fb 100644 --- a/contractcourt/contract_resolvers.go +++ b/contractcourt/contract_resolvers.go @@ -211,8 +211,9 @@ func (h *htlcTimeoutResolver) Resolve() (ContractResolver, error) { // Otherwise, this is our commitment, so we'll watch for the // second-level transaction to be sufficiently confirmed. secondLevelTXID := h.htlcResolution.SignedTimeoutTx.TxHash() + sweepScript := h.htlcResolution.SignedTimeoutTx.TxOut[0].PkScript confNtfn, err := h.Notifier.RegisterConfirmationsNtfn( - &secondLevelTXID, 1, h.broadcastHeight, + &secondLevelTXID, sweepScript, 1, h.broadcastHeight, ) if err != nil { return nil, err @@ -521,8 +522,9 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) { // With the sweep transaction broadcast, we'll wait for its // confirmation. sweepTXID := h.sweepTx.TxHash() + sweepScript := h.sweepTx.TxOut[0].PkScript confNtfn, err := h.Notifier.RegisterConfirmationsNtfn( - &sweepTXID, 1, h.broadcastHeight, + &sweepTXID, sweepScript, 1, h.broadcastHeight, ) if err != nil { return nil, err @@ -1193,8 +1195,9 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) { // // TODO(roasbeef): instead sweep asap if remote commit? yeh commitTXID := c.commitResolution.SelfOutPoint.Hash + sweepScript := c.commitResolution.SelfOutputSignDesc.Output.PkScript confNtfn, err := c.Notifier.RegisterConfirmationsNtfn( - &commitTXID, 1, c.broadcastHeight, + &commitTXID, sweepScript, 1, c.broadcastHeight, ) if err != nil { return nil, err @@ -1329,8 +1332,9 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) { // Now we'll wait until the sweeping transaction has been fully // confirmed. Once it's confirmed, we can mark this contract resolved. sweepTXID := c.sweepTx.TxHash() + sweepingScript := c.sweepTx.TxOut[0].PkScript confNtfn, err = c.Notifier.RegisterConfirmationsNtfn( - &sweepTXID, 1, c.broadcastHeight, + &sweepTXID, sweepingScript, 1, c.broadcastHeight, ) if err != nil { return nil, err From 8dd0b56d3522602c7beab0bb8e81c5a39d7d6ec3 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:10:08 -0700 Subject: [PATCH 07/41] lnwallet: publicy export WitnessScriptHash and GenMultiSigScript In this commit, we export WitnessScriptHash and GenMultiSigScript as external sub-systems may now need to use these methods in order to be able to watch for confirmations based on the script of a transaction. --- lnwallet/channel.go | 2 +- lnwallet/script_utils.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 890a3c92..7651a6aa 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -1427,7 +1427,7 @@ func (lc *LightningChannel) createSignDesc() error { localKey := lc.localChanCfg.MultiSigKey.PubKey.SerializeCompressed() remoteKey := lc.remoteChanCfg.MultiSigKey.PubKey.SerializeCompressed() - multiSigScript, err := genMultiSigScript(localKey, remoteKey) + multiSigScript, err := GenMultiSigScript(localKey, remoteKey) if err != nil { return err } diff --git a/lnwallet/script_utils.go b/lnwallet/script_utils.go index 5afe2839..3c7e08f6 100644 --- a/lnwallet/script_utils.go +++ b/lnwallet/script_utils.go @@ -57,9 +57,9 @@ func WitnessScriptHash(witnessScript []byte) ([]byte, error) { return bldr.Script() } -// genMultiSigScript generates the non-p2sh'd multisig script for 2 of 2 +// GenMultiSigScript generates the non-p2sh'd multisig script for 2 of 2 // pubkeys. -func genMultiSigScript(aPub, bPub []byte) ([]byte, error) { +func GenMultiSigScript(aPub, bPub []byte) ([]byte, error) { if len(aPub) != 33 || len(bPub) != 33 { return nil, fmt.Errorf("Pubkey size error. Compressed pubkeys only") } @@ -91,7 +91,7 @@ func GenFundingPkScript(aPub, bPub []byte, amt int64) ([]byte, *wire.TxOut, erro } // First, create the 2-of-2 multi-sig script itself. - witnessScript, err := genMultiSigScript(aPub, bPub) + witnessScript, err := GenMultiSigScript(aPub, bPub) if err != nil { return nil, nil, err } From 32680193265673133e13dee31a217789939672bb Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:15:59 -0700 Subject: [PATCH 08/41] htlcswitch: update mock notifier due to recent API changes --- htlcswitch/mock.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index b3bcbdb1..dbacaa69 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -781,7 +781,7 @@ type mockNotifier struct { epochChan chan *chainntnfs.BlockEpoch } -func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, +func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, _ []byte, numConfs uint32, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) { return nil, nil } From 8aef43360f9dcd429eccfff4dcdd2ead3183a27a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:16:34 -0700 Subject: [PATCH 09/41] funding: update fundingManager to register for conf's using pkScripts --- fundingmanager.go | 37 +++++++++++++++++++++++++++++++++---- fundingmanager_test.go | 5 +++-- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/fundingmanager.go b/fundingmanager.go index 7753204d..fa414545 100644 --- a/fundingmanager.go +++ b/fundingmanager.go @@ -1752,6 +1752,20 @@ func (f *fundingManager) waitForFundingWithTimeout(completeChan *channeldb.OpenC } } +// makeFundingScript re-creates the funding script for the funding transaction +// of the target channel. +func makeFundingScript(channel *channeldb.OpenChannel) ([]byte, error) { + localKey := channel.LocalChanCfg.MultiSigKey.PubKey.SerializeCompressed() + remoteKey := channel.RemoteChanCfg.MultiSigKey.PubKey.SerializeCompressed() + + multiSigScript, err := lnwallet.GenMultiSigScript(localKey, remoteKey) + if err != nil { + return nil, err + } + + return lnwallet.WitnessScriptHash(multiSigScript) +} + // waitForFundingConfirmation handles the final stages of the channel funding // process once the funding transaction has been broadcast. The primary // function of waitForFundingConfirmation is to wait for blockchain @@ -1767,9 +1781,16 @@ func (f *fundingManager) waitForFundingConfirmation(completeChan *channeldb.Open // Register with the ChainNotifier for a notification once the funding // transaction reaches `numConfs` confirmations. txid := completeChan.FundingOutpoint.Hash + fundingScript, err := makeFundingScript(completeChan) + if err != nil { + fndgLog.Errorf("unable to create funding script for "+ + "ChannelPoint(%v): %v", completeChan.FundingOutpoint, err) + return + } numConfs := uint32(completeChan.NumConfsRequired) - confNtfn, err := f.cfg.Notifier.RegisterConfirmationsNtfn(&txid, - numConfs, completeChan.FundingBroadcastHeight) + confNtfn, err := f.cfg.Notifier.RegisterConfirmationsNtfn( + &txid, fundingScript, numConfs, completeChan.FundingBroadcastHeight, + ) if err != nil { fndgLog.Errorf("Unable to register for confirmation of "+ "ChannelPoint(%v)", completeChan.FundingOutpoint) @@ -2073,8 +2094,16 @@ func (f *fundingManager) annAfterSixConfs(completeChan *channeldb.OpenChannel, shortChanID.ToUint64(), completeChan.FundingOutpoint, numConfs) - confNtfn, err := f.cfg.Notifier.RegisterConfirmationsNtfn(&txid, - numConfs, completeChan.FundingBroadcastHeight) + fundingScript, err := makeFundingScript(completeChan) + if err != nil { + return fmt.Errorf("unable to create funding script for "+ + "ChannelPoint(%v): %v", + completeChan.FundingOutpoint, err) + } + + confNtfn, err := f.cfg.Notifier.RegisterConfirmationsNtfn( + &txid, fundingScript, numConfs, completeChan.FundingBroadcastHeight, + ) if err != nil { return fmt.Errorf("Unable to register for "+ "confirmation of ChannelPoint(%v): %v", diff --git a/fundingmanager_test.go b/fundingmanager_test.go index 7550db4c..fabcf780 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -98,8 +98,9 @@ type mockNotifier struct { epochChan chan *chainntnfs.BlockEpoch } -func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, numConfs, - heightHint uint32) (*chainntnfs.ConfirmationEvent, error) { +func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, + _ []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) { + if numConfs == 6 { return &chainntnfs.ConfirmationEvent{ Confirmed: m.sixConfChannel, From 2d421b8e3c1ff9ed45d6380a4ac1603bac0b19d7 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:16:57 -0700 Subject: [PATCH 10/41] breacharbiter: update breachArbiter to register for conf's using pkScripts --- breacharbiter.go | 14 ++++++++++---- breacharbiter_test.go | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/breacharbiter.go b/breacharbiter.go index 4d31ca36..950fc497 100644 --- a/breacharbiter.go +++ b/breacharbiter.go @@ -196,8 +196,10 @@ func (b *breachArbiter) Start() error { // Register for a notification when the breach transaction is // confirmed on chain. breachTXID := retInfo.commitHash + breachScript := retInfo.breachedOutputs[0].signDesc.Output.PkScript confChan, err := b.cfg.Notifier.RegisterConfirmationsNtfn( - &breachTXID, 1, retInfo.breachHeight) + &breachTXID, breachScript, 1, retInfo.breachHeight, + ) if err != nil { brarLog.Errorf("unable to register for conf updates "+ "for txid: %v, err: %v", breachTXID, err) @@ -556,8 +558,10 @@ justiceTxBroadcast: // notify the caller that initiated the retribution workflow that the // deed has been done. justiceTXID := finalTx.TxHash() + justiceScript := finalTx.TxOut[0].PkScript confChan, err = b.cfg.Notifier.RegisterConfirmationsNtfn( - &justiceTXID, 1, breachConfHeight) + &justiceTXID, justiceScript, 1, breachConfHeight, + ) if err != nil { brarLog.Errorf("unable to register for conf for txid(%v): %v", justiceTXID, err) @@ -720,8 +724,10 @@ func (b *breachArbiter) handleBreachHandoff(breachEvent *ContractBreachEvent) { // confirmed in the chain to ensure we're not dealing with a moving // target. breachTXID := &retInfo.commitHash - cfChan, err := b.cfg.Notifier.RegisterConfirmationsNtfn(breachTXID, 1, - retInfo.breachHeight) + breachScript := retInfo.breachedOutputs[0].signDesc.Output.PkScript + cfChan, err := b.cfg.Notifier.RegisterConfirmationsNtfn( + breachTXID, breachScript, 1, retInfo.breachHeight, + ) if err != nil { brarLog.Errorf("unable to register for conf updates for "+ "txid: %v, err: %v", breachTXID, err) diff --git a/breacharbiter_test.go b/breacharbiter_test.go index 6c13d668..85d10fe9 100644 --- a/breacharbiter_test.go +++ b/breacharbiter_test.go @@ -1012,6 +1012,11 @@ func TestBreachHandoffSuccess(t *testing.T) { ProcessACK: make(chan error, 1), BreachRetribution: &lnwallet.BreachRetribution{ BreachTransaction: bobClose.CloseTx, + LocalOutputSignDesc: &lnwallet.SignDescriptor{ + Output: &wire.TxOut{ + PkScript: breachKeys[0], + }, + }, }, } contractBreaches <- breach @@ -1039,6 +1044,11 @@ func TestBreachHandoffSuccess(t *testing.T) { ProcessACK: make(chan error, 1), BreachRetribution: &lnwallet.BreachRetribution{ BreachTransaction: bobClose.CloseTx, + LocalOutputSignDesc: &lnwallet.SignDescriptor{ + Output: &wire.TxOut{ + PkScript: breachKeys[0], + }, + }, }, } @@ -1083,6 +1093,11 @@ func TestBreachHandoffFail(t *testing.T) { ProcessACK: make(chan error, 1), BreachRetribution: &lnwallet.BreachRetribution{ BreachTransaction: bobClose.CloseTx, + LocalOutputSignDesc: &lnwallet.SignDescriptor{ + Output: &wire.TxOut{ + PkScript: breachKeys[0], + }, + }, }, } contractBreaches <- breach @@ -1130,6 +1145,11 @@ func TestBreachHandoffFail(t *testing.T) { ProcessACK: make(chan error, 1), BreachRetribution: &lnwallet.BreachRetribution{ BreachTransaction: bobClose.CloseTx, + LocalOutputSignDesc: &lnwallet.SignDescriptor{ + Output: &wire.TxOut{ + PkScript: breachKeys[0], + }, + }, }, } From 55d6586a675f71e8e56e8f2eac8b4757fa9478ff Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:17:17 -0700 Subject: [PATCH 11/41] utxonursery: update to register for confs using pkScripts --- utxonursery.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/utxonursery.go b/utxonursery.go index 3aac57ed..ec2424d3 100644 --- a/utxonursery.go +++ b/utxonursery.go @@ -1145,7 +1145,9 @@ func (u *utxoNursery) registerSweepConf(finalTx *wire.MsgTx, finalTxID := finalTx.TxHash() confChan, err := u.cfg.Notifier.RegisterConfirmationsNtfn( - &finalTxID, u.cfg.ConfDepth, heightHint) + &finalTxID, finalTx.TxOut[0].PkScript, u.cfg.ConfDepth, + heightHint, + ) if err != nil { utxnLog.Errorf("unable to register notification for "+ "sweep confirmation: %v", finalTxID) @@ -1251,7 +1253,9 @@ func (u *utxoNursery) registerTimeoutConf(baby *babyOutput, heightHint uint32) e // Register for the confirmation of presigned htlc txn. confChan, err := u.cfg.Notifier.RegisterConfirmationsNtfn( - &birthTxID, u.cfg.ConfDepth, heightHint) + &birthTxID, baby.timeoutTx.TxOut[0].PkScript, u.cfg.ConfDepth, + heightHint, + ) if err != nil { return err } @@ -1316,8 +1320,10 @@ func (u *utxoNursery) registerPreschoolConf(kid *kidOutput, heightHint uint32) e // de-duplicate // * need to do above? - confChan, err := u.cfg.Notifier.RegisterConfirmationsNtfn(&txID, - u.cfg.ConfDepth, heightHint) + pkScript := kid.signDesc.Output.PkScript + confChan, err := u.cfg.Notifier.RegisterConfirmationsNtfn( + &txID, pkScript, u.cfg.ConfDepth, heightHint, + ) if err != nil { return err } From 07defe5f93b5a860d8b53830ad7c4c8ea2d6b95d Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 30 May 2018 22:18:44 -0700 Subject: [PATCH 12/41] rpc+peer: use pkScripts for close ntfns --- mock.go | 4 ++-- peer.go | 9 +++++---- rpcserver.go | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/mock.go b/mock.go index e7b41828..28794bc4 100644 --- a/mock.go +++ b/mock.go @@ -85,8 +85,8 @@ type mockNotfier struct { confChannel chan *chainntnfs.TxConfirmation } -func (m *mockNotfier) RegisterConfirmationsNtfn(txid *chainhash.Hash, numConfs, - heightHint uint32) (*chainntnfs.ConfirmationEvent, error) { +func (m *mockNotfier) RegisterConfirmationsNtfn(txid *chainhash.Hash, + _ []byte, numConfs, heightHint uint32) (*chainntnfs.ConfirmationEvent, error) { return &chainntnfs.ConfirmationEvent{ Confirmed: m.confChannel, }, nil diff --git a/peer.go b/peer.go index 987f147a..c4cad228 100644 --- a/peer.go +++ b/peer.go @@ -1837,7 +1837,7 @@ func (p *peer) finalizeChanClosure(chanCloser *channelCloser) { } go waitForChanToClose(chanCloser.negotiationHeight, notifier, errChan, - chanPoint, &closingTxid, func() { + chanPoint, &closingTxid, closingTx.TxOut[0].PkScript, func() { // Respond to the local subsystem which requested the // channel closure. if closeReq != nil { @@ -1872,15 +1872,16 @@ func (p *peer) finalizeChanClosure(chanCloser *channelCloser) { // the function, then it will be sent over the errChan. func waitForChanToClose(bestHeight uint32, notifier chainntnfs.ChainNotifier, errChan chan error, chanPoint *wire.OutPoint, - closingTxID *chainhash.Hash, cb func()) { + closingTxID *chainhash.Hash, closeScript []byte, cb func()) { peerLog.Infof("Waiting for confirmation of cooperative close of "+ "ChannelPoint(%v) with txid: %v", chanPoint, closingTxID) // TODO(roasbeef): add param for num needed confs - confNtfn, err := notifier.RegisterConfirmationsNtfn(closingTxID, 1, - bestHeight) + confNtfn, err := notifier.RegisterConfirmationsNtfn( + closingTxID, closeScript, 1, bestHeight, + ) if err != nil { if errChan != nil { errChan <- err diff --git a/rpcserver.go b/rpcserver.go index 9261debe..42e27b41 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -1083,7 +1083,7 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest, errChan = make(chan error, 1) notifier := r.server.cc.chainNotifier go waitForChanToClose(uint32(bestHeight), notifier, errChan, chanPoint, - &closingTxid, func() { + &closingTxid, closingTx.TxOut[0].PkScript, func() { // Respond to the local subsystem which // requested the channel closure. updateChan <- &lnrpc.CloseStatusUpdate{ From 7a0b7d2742d0aebf81a0a673798954136fc4bedd Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 18:47:01 -0700 Subject: [PATCH 13/41] chainntnfs: update RegisterSpendNtfn to take the prev output script In this commit, we update the RegisterSpendNtfn method to also take the prev output script of the item that we're attempting to watch for. This change is required due to the recent modifications in the neutrino protocol (BIP 158 + 157). With the new protocol, we'll match on the script, but then dispatch notifications based on the precise outpoint that matches. --- chainntnfs/interface.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/chainntnfs/interface.go b/chainntnfs/interface.go index 37c370e4..053e7ed3 100644 --- a/chainntnfs/interface.go +++ b/chainntnfs/interface.go @@ -38,7 +38,9 @@ type ChainNotifier interface { heightHint uint32) (*ConfirmationEvent, error) // RegisterSpendNtfn registers an intent to be notified once the target - // outpoint is successfully spent within a transaction. The returned + // outpoint is successfully spent within a transaction. The script that + // the outpoint creates must also be specified. This allows this + // interface to be implemented by BIP 158-like filtering. The returned // SpendEvent will receive a send on the 'Spend' transaction once a // transaction spending the input is detected on the blockchain. The // heightHint parameter is provided as a convenience to light clients. @@ -50,8 +52,8 @@ type ChainNotifier interface { // // NOTE: Dispatching notifications to multiple clients subscribed to a // spend of the same outpoint MUST be supported. - RegisterSpendNtfn(outpoint *wire.OutPoint, - heightHint uint32) (*SpendEvent, error) + RegisterSpendNtfn(outpoint *wire.OutPoint, pkScript []byte, + heightHint uint32, mempool bool) (*SpendEvent, error) // RegisterBlockEpochNtfn registers an intent to be notified of each // new block connected to the tip of the main chain. The returned From 6781b17056baca769c0377ad713df70301dbe7f0 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:02:25 -0700 Subject: [PATCH 14/41] chainntnfs: update bitcoind and btcd backends to match new spend ntfn API --- chainntnfs/bitcoindnotify/bitcoind.go | 2 +- chainntnfs/btcdnotify/btcd.go | 3 ++- chainntnfs/interface.go | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/chainntnfs/bitcoindnotify/bitcoind.go b/chainntnfs/bitcoindnotify/bitcoind.go index cd34e52a..a53141e0 100644 --- a/chainntnfs/bitcoindnotify/bitcoind.go +++ b/chainntnfs/bitcoindnotify/bitcoind.go @@ -577,7 +577,7 @@ type spendCancel struct { // across the 'Spend' channel. The heightHint should represent the earliest // height in the chain where the transaction could have been spent in. func (b *BitcoindNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, - heightHint uint32) (*chainntnfs.SpendEvent, error) { + pkScript []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) { ntfn := &spendNotification{ targetOutpoint: outpoint, diff --git a/chainntnfs/btcdnotify/btcd.go b/chainntnfs/btcdnotify/btcd.go index 30ceee99..2fb2f26c 100644 --- a/chainntnfs/btcdnotify/btcd.go +++ b/chainntnfs/btcdnotify/btcd.go @@ -695,7 +695,7 @@ type spendCancel struct { // across the 'Spend' channel. The heightHint should represent the earliest // height in the chain where the transaction could have been spent in. func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, - heightHint uint32) (*chainntnfs.SpendEvent, error) { + pkScript []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) { ntfn := &spendNotification{ targetOutpoint: outpoint, @@ -710,6 +710,7 @@ func (b *BtcdNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, case b.notificationRegistry <- ntfn: } + // TODO(roasbeef): update btcd rescan logic to also use both if err := b.chainConn.NotifySpent([]*wire.OutPoint{outpoint}); err != nil { return nil, err } diff --git a/chainntnfs/interface.go b/chainntnfs/interface.go index 053e7ed3..ff90cc0a 100644 --- a/chainntnfs/interface.go +++ b/chainntnfs/interface.go @@ -53,7 +53,7 @@ type ChainNotifier interface { // NOTE: Dispatching notifications to multiple clients subscribed to a // spend of the same outpoint MUST be supported. RegisterSpendNtfn(outpoint *wire.OutPoint, pkScript []byte, - heightHint uint32, mempool bool) (*SpendEvent, error) + heightHint uint32) (*SpendEvent, error) // RegisterBlockEpochNtfn registers an intent to be notified of each // new block connected to the tip of the main chain. The returned From f87b1a94c5c39869e2e5bad6dc43b3e2c9792f97 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:03:26 -0700 Subject: [PATCH 15/41] chainntnfs/neutrinonotify: update neutrino backend to use new API In this commit, we update the neutrino backend for the ChainNotifier to use the new API which requires that callers pass the outpoint along with the pkScript to be notified of any spends. --- chainntnfs/neutrinonotify/neutrino.go | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/chainntnfs/neutrinonotify/neutrino.go b/chainntnfs/neutrinonotify/neutrino.go index c81bfc3d..17014fe9 100644 --- a/chainntnfs/neutrinonotify/neutrino.go +++ b/chainntnfs/neutrinonotify/neutrino.go @@ -133,7 +133,7 @@ func (n *NeutrinoNotifier) Start() error { // required that a user MUST set an addr/outpoint/txid when creating a // rescan. To get around this, we'll add a "zero" outpoint, that won't // actually be matched. - var zeroHash chainhash.Hash + var zeroInput neutrino.InputWithScript rescanOptions := []neutrino.RescanOption{ neutrino.StartBlock(startingPoint), neutrino.QuitChan(n.quit), @@ -143,11 +143,12 @@ func (n *NeutrinoNotifier) Start() error { OnFilteredBlockDisconnected: n.onFilteredBlockDisconnected, }, ), - neutrino.WatchTxIDs(zeroHash), + neutrino.WatchInputs(zeroInput), } n.txConfNotifier = chainntnfs.NewTxConfNotifier( - bestHeight, reorgSafetyLimit) + bestHeight, reorgSafetyLimit, + ) // Finally, we'll create our rescan struct, start it, and launch all // the goroutines we need to operate this ChainNotifier instance. @@ -599,7 +600,7 @@ type spendCancel struct { // target outpoint has been detected, the details of the spending event will be // sent across the 'Spend' channel. func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, - heightHint uint32) (*chainntnfs.SpendEvent, error) { + pkScript []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) { n.heightMtx.RLock() currentHeight := n.bestHeight @@ -658,10 +659,15 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, break } + inputToWatch := neutrino.InputWithScript{ + OutPoint: *outpoint, + PkScript: pkScript, + } + // Before sending off the notification request, we'll attempt to see if // this output is still spent or not at this point in the chain. spendReport, err := n.p2pNode.GetUtxo( - neutrino.WatchOutPoints(*outpoint), + neutrino.WatchInputs(inputToWatch), neutrino.StartBlock(&waddrmgr.BlockStamp{ Height: int32(heightHint), }), @@ -697,7 +703,7 @@ func (n *NeutrinoNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, // If the output is still unspent, then we'll update our rescan's // filter, and send the request to the dispatcher goroutine. rescanUpdate := []neutrino.UpdateOption{ - neutrino.AddOutPoints(*outpoint), + neutrino.AddInputs(inputToWatch), neutrino.Rewind(currentHeight), } From 1a5b1d97922ead8a31ba37e90cd99799a8595683 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:04:31 -0700 Subject: [PATCH 16/41] chainntnfs: update interface tests to pass the prev script into RegisterSpendNtfn --- chainntnfs/interface_test.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/chainntnfs/interface_test.go b/chainntnfs/interface_test.go index e4fe3fbd..5bba9681 100644 --- a/chainntnfs/interface_test.go +++ b/chainntnfs/interface_test.go @@ -420,8 +420,9 @@ func testSpendNotification(miner *rpctest.Harness, const numClients = 5 spendClients := make([]*chainntnfs.SpendEvent, numClients) for i := 0; i < numClients; i++ { - spentIntent, err := notifier.RegisterSpendNtfn(outpoint, - uint32(currentHeight)) + spentIntent, err := notifier.RegisterSpendNtfn( + outpoint, pkScript, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register for spend ntfn: %v", err) } @@ -471,8 +472,9 @@ func testSpendNotification(miner *rpctest.Harness, // Make sure registering a client after the tx is in the mempool still // doesn't trigger a notification. - spentIntent, err := notifier.RegisterSpendNtfn(outpoint, - uint32(currentHeight)) + spentIntent, err := notifier.RegisterSpendNtfn( + outpoint, pkScript, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register for spend ntfn: %v", err) } @@ -918,8 +920,9 @@ func testSpendBeforeNtfnRegistration(miner *rpctest.Harness, const numClients = 2 spendClients := make([]*chainntnfs.SpendEvent, numClients) for i := 0; i < numClients; i++ { - spentIntent, err := notifier.RegisterSpendNtfn(outpoint, - uint32(currentHeight)) + spentIntent, err := notifier.RegisterSpendNtfn( + outpoint, pkScript, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register for spend ntfn: %v", err) @@ -998,8 +1001,9 @@ func testCancelSpendNtfn(node *rpctest.Harness, const numClients = 2 spendClients := make([]*chainntnfs.SpendEvent, numClients) for i := 0; i < numClients; i++ { - spentIntent, err := notifier.RegisterSpendNtfn(outpoint, - uint32(currentHeight)) + spentIntent, err := notifier.RegisterSpendNtfn( + outpoint, pkScript, uint32(currentHeight), + ) if err != nil { t.Fatalf("unable to register for spend ntfn: %v", err) } From d129e7c8901e2f66aa4f742341f0b3bcfb84b02c Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:10:07 -0700 Subject: [PATCH 17/41] channeldb: update ChannelView to return new EdgePoint struct In this commit, we update the ChannelView method to be compatible with the new set of interfaces that require the script to be passed in in addition to the outpoint. In order to do this, we introduce a new EdgePoint struct which packages together a channel point along with the funding pkScript. Along the way, we've copied over a utility method from the lnwallet package to avoid having to deal with an import cycle. --- channeldb/channel_test.go | 8 ++-- channeldb/graph.go | 91 +++++++++++++++++++++++++++++++++++---- channeldb/graph_test.go | 44 ++++++++++++++++--- 3 files changed, 125 insertions(+), 18 deletions(-) diff --git a/channeldb/channel_test.go b/channeldb/channel_test.go index 3083e725..feb5b2d5 100644 --- a/channeldb/channel_test.go +++ b/channeldb/channel_test.go @@ -10,16 +10,16 @@ import ( "runtime" "testing" - "github.com/davecgh/go-spew/spew" - "github.com/lightningnetwork/lnd/keychain" - "github.com/lightningnetwork/lnd/lnwire" - "github.com/lightningnetwork/lnd/shachain" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" _ "github.com/btcsuite/btcwallet/walletdb/bdb" + "github.com/davecgh/go-spew/spew" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/shachain" ) var ( diff --git a/channeldb/graph.go b/channeldb/graph.go index 49b73aad..d716ead2 100644 --- a/channeldb/graph.go +++ b/channeldb/graph.go @@ -2,6 +2,7 @@ package channeldb import ( "bytes" + "crypto/sha256" "encoding/binary" "fmt" "image/color" @@ -12,6 +13,7 @@ import ( "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/coreos/bbolt" @@ -2350,12 +2352,62 @@ func (c *ChannelGraph) FetchChannelEdgesByID(chanID uint64) (*ChannelEdgeInfo, * return edgeInfo, policy1, policy2, nil } +// genMultiSigP2WSH generates the p2wsh'd multisig script for 2 of 2 pubkeys. +func genMultiSigP2WSH(aPub, bPub []byte) ([]byte, error) { + if len(aPub) != 33 || len(bPub) != 33 { + return nil, fmt.Errorf("Pubkey size error. Compressed " + + "pubkeys only") + } + + // Swap to sort pubkeys if needed. Keys are sorted in lexicographical + // order. The signatures within the scriptSig must also adhere to the + // order, ensuring that the signatures for each public key appears in + // the proper order on the stack. + if bytes.Compare(aPub, bPub) == 1 { + aPub, bPub = bPub, aPub + } + + // First, we'll generate the witness script for the multi-sig. + bldr := txscript.NewScriptBuilder() + bldr.AddOp(txscript.OP_2) + bldr.AddData(aPub) // Add both pubkeys (sorted). + bldr.AddData(bPub) + bldr.AddOp(txscript.OP_2) + bldr.AddOp(txscript.OP_CHECKMULTISIG) + witnessScript, err := bldr.Script() + if err != nil { + return nil, err + } + + // With the witness script generated, we'll now turn it into a p2sh + // script: + // * OP_0 + bldr = txscript.NewScriptBuilder() + bldr.AddOp(txscript.OP_0) + scriptHash := sha256.Sum256(witnessScript) + bldr.AddData(scriptHash[:]) + + return bldr.Script() +} + +// EdgePoint couples the outpoint of a channel with the funding script that it +// creates. The FilteredChainView will use this to watch for spends of this +// edge point on chain. We require both of these values as depending on the +// concrete implementation, either the pkScript, or the out point will be used. +type EdgePoint struct { + // FundingPkScript is the p2wsh multi-sig script of the target channel. + FundingPkScript []byte + + // OutPoint is the outpoint of the target channel. + OutPoint wire.OutPoint +} + // ChannelView returns the verifiable edge information for each active channel -// within the known channel graph. The set of UTXO's returned are the ones that -// need to be watched on chain to detect channel closes on the resident -// blockchain. -func (c *ChannelGraph) ChannelView() ([]wire.OutPoint, error) { - var chanPoints []wire.OutPoint +// within the known channel graph. The set of UTXO's (along with their scripts) +// returned are the ones that need to be watched on chain to detect channel +// closes on the resident blockchain. +func (c *ChannelGraph) ChannelView() ([]EdgePoint, error) { + var edgePoints []EdgePoint if err := c.db.View(func(tx *bolt.Tx) error { // We're going to iterate over the entire channel index, so // we'll need to fetch the edgeBucket to get to the index as @@ -2368,11 +2420,15 @@ func (c *ChannelGraph) ChannelView() ([]wire.OutPoint, error) { if chanIndex == nil { return ErrGraphNoEdgesFound } + edgeIndex := edges.Bucket(edgeIndexBucket) + if edgeIndex == nil { + return ErrGraphNoEdgesFound + } // Once we have the proper bucket, we'll range over each key // (which is the channel point for the channel) and decode it, // accumulating each entry. - return chanIndex.ForEach(func(chanPointBytes, _ []byte) error { + return chanIndex.ForEach(func(chanPointBytes, chanID []byte) error { chanPointReader := bytes.NewReader(chanPointBytes) var chanPoint wire.OutPoint @@ -2381,14 +2437,33 @@ func (c *ChannelGraph) ChannelView() ([]wire.OutPoint, error) { return err } - chanPoints = append(chanPoints, chanPoint) + edgeInfo, err := fetchChanEdgeInfo( + edgeIndex, chanID, + ) + if err != nil { + return err + } + + pkScript, err := genMultiSigP2WSH( + edgeInfo.BitcoinKey1Bytes[:], + edgeInfo.BitcoinKey2Bytes[:], + ) + if err != nil { + return err + } + + edgePoints = append(edgePoints, EdgePoint{ + FundingPkScript: pkScript, + OutPoint: chanPoint, + }) + return nil }) }); err != nil { return nil, err } - return chanPoints, nil + return edgePoints, nil } // NewChannelEdgePolicy returns a new blank ChannelEdgePolicy. diff --git a/channeldb/graph_test.go b/channeldb/graph_test.go index cc3055b3..7c828760 100644 --- a/channeldb/graph_test.go +++ b/channeldb/graph_test.go @@ -997,7 +997,7 @@ func assertNumNodes(t *testing.T, graph *ChannelGraph, n int) { } } -func assertChanViewEqual(t *testing.T, a []wire.OutPoint, b []*wire.OutPoint) { +func assertChanViewEqual(t *testing.T, a []EdgePoint, b []EdgePoint) { if len(a) != len(b) { _, _, line, _ := runtime.Caller(1) t.Fatalf("line %v: chan views don't match", line) @@ -1005,14 +1005,34 @@ func assertChanViewEqual(t *testing.T, a []wire.OutPoint, b []*wire.OutPoint) { chanViewSet := make(map[wire.OutPoint]struct{}) for _, op := range a { - chanViewSet[op] = struct{}{} + chanViewSet[op.OutPoint] = struct{}{} + } + + for _, op := range b { + if _, ok := chanViewSet[op.OutPoint]; !ok { + _, _, line, _ := runtime.Caller(1) + t.Fatalf("line %v: chanPoint(%v) not found in first "+ + "view", line, op) + } + } +} + +func assertChanViewEqualChanPoints(t *testing.T, a []EdgePoint, b []*wire.OutPoint) { + if len(a) != len(b) { + _, _, line, _ := runtime.Caller(1) + t.Fatalf("line %v: chan views don't match", line) + } + + chanViewSet := make(map[wire.OutPoint]struct{}) + for _, op := range a { + chanViewSet[op.OutPoint] = struct{}{} } for _, op := range b { if _, ok := chanViewSet[*op]; !ok { _, _, line, _ := runtime.Caller(1) - t.Fatalf("line %v: chanPoint(%v) not found in first view", - line, op) + t.Fatalf("line %v: chanPoint(%v) not found in first "+ + "view", line, op) } } } @@ -1056,6 +1076,7 @@ func TestGraphPruning(t *testing.T) { // With the vertexes created, we'll next create a series of channels // between them. channelPoints := make([]*wire.OutPoint, 0, numNodes-1) + edgePoints := make([]EdgePoint, 0, numNodes-1) for i := 0; i < numNodes-1; i++ { txHash := sha256.Sum256([]byte{byte(i)}) chanID := uint64(i + 1) @@ -1086,6 +1107,17 @@ func TestGraphPruning(t *testing.T) { t.Fatalf("unable to add node: %v", err) } + pkScript, err := genMultiSigP2WSH( + edgeInfo.BitcoinKey1Bytes[:], edgeInfo.BitcoinKey2Bytes[:], + ) + if err != nil { + t.Fatalf("unable to gen multi-sig p2wsh: %v", err) + } + edgePoints = append(edgePoints, EdgePoint{ + FundingPkScript: pkScript, + OutPoint: op, + }) + // Create and add an edge with random data that points from // node_i -> node_i+1 edge := randEdgePolicy(chanID, op, db) @@ -1113,7 +1145,7 @@ func TestGraphPruning(t *testing.T) { if err != nil { t.Fatalf("unable to get graph channel view: %v", err) } - assertChanViewEqual(t, channelView, channelPoints) + assertChanViewEqual(t, channelView, edgePoints) // Now with our test graph created, we can test the pruning // capabilities of the channel graph. @@ -1145,7 +1177,7 @@ func TestGraphPruning(t *testing.T) { if err != nil { t.Fatalf("unable to get graph channel view: %v", err) } - assertChanViewEqual(t, channelView, channelPoints[2:]) + assertChanViewEqualChanPoints(t, channelView, channelPoints[2:]) // Next we'll create a block that doesn't close any channels within the // graph to test the negative error case. From ec0fb9c537ad2a71c0ca0d20c390fedc06ebc539 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:12:28 -0700 Subject: [PATCH 18/41] routing/chainview: modify the UpdateFilter method to take a channeldb.EdgePoint In this commit, we update the existing UpdateFilter method to take the new channeldb.EdgePoint struct in place of the prior wire.OutPoint. We must do this as the caller now typically has this type due to the preparation to enable lnd to be able to be compatible with the new neutrino protocol. --- routing/chainview/interface.go | 3 ++- routing/chainview/interface_test.go | 18 +++++++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/routing/chainview/interface.go b/routing/chainview/interface.go index 036d90c0..cfa9fccf 100644 --- a/routing/chainview/interface.go +++ b/routing/chainview/interface.go @@ -3,6 +3,7 @@ package chainview import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/channeldb" ) // FilteredChainView represents a subscription to a certain subset of the @@ -42,7 +43,7 @@ type FilteredChainView interface { // relevant notifications are dispatched, meaning blocks with a height // lower than the best known height might be sent over the // FilteredBlocks() channel. - UpdateFilter(ops []wire.OutPoint, updateHeight uint32) error + UpdateFilter(ops []channeldb.EdgePoint, updateHeight uint32) error // FilterBlock takes a block hash, and returns a FilteredBlocks which // is the result of applying the current registered UTXO sub-set on the diff --git a/routing/chainview/interface_test.go b/routing/chainview/interface_test.go index c563434a..5ab054df 100644 --- a/routing/chainview/interface_test.go +++ b/routing/chainview/interface_test.go @@ -21,6 +21,7 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/lightninglabs/neutrino" + "github.com/lightningnetwork/lnd/channeldb" "github.com/ltcsuite/ltcd/btcjson" "github.com/btcsuite/btcwallet/walletdb" @@ -238,8 +239,11 @@ func testFilterBlockNotifications(node *rpctest.Harness, t.Fatalf("unable to get current height: %v", err) } - // Now we'll add both output to the current filter. - filter := []wire.OutPoint{*outPoint1, *outPoint2} + // Now we'll add both outpoints to the current filter. + filter := []channeldb.EdgePoint{ + {targetScript, *outPoint1}, + {targetScript, *outPoint2}, + } err = chainView.UpdateFilter(filter, uint32(currentHeight)) if err != nil { t.Fatalf("unable to update filter: %v", err) @@ -382,8 +386,9 @@ func testUpdateFilterBackTrack(node *rpctest.Harness, // After the block has been mined+notified we'll update the filter with // a _prior_ height so a "rewind" occurs. - filter := []wire.OutPoint{*outPoint} - + filter := []channeldb.EdgePoint{ + {testScript, *outPoint}, + } err = chainView.UpdateFilter(filter, uint32(currentHeight)) if err != nil { t.Fatalf("unable to update filter: %v", err) @@ -496,7 +501,10 @@ func testFilterSingleBlock(node *rpctest.Harness, chainView FilteredChainView, // Now we'll manually trigger filtering the block generated above. // First, we'll add the two outpoints to our filter. - filter := []wire.OutPoint{*outPoint1, *outPoint2} + filter := []channeldb.EdgePoint{ + {testScript, *outPoint1}, + {testScript, *outPoint2}, + } err = chainView.UpdateFilter(filter, uint32(currentHeight)) if err != nil { t.Fatalf("unable to update filter: %v", err) From 7e6997d6d35fe0fb9c897c8fd03479114c4e1f71 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:13:29 -0700 Subject: [PATCH 19/41] routing/chainview: update neutrino API due to recent changes In this commit, we ensure that the neutrino backend meets the target interface, and also we update the API usage for the internal neutrino rescan struct to use the new InputWithScript struct. --- routing/chainview/neutrino.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/routing/chainview/neutrino.go b/routing/chainview/neutrino.go index 53e71ba3..56d16708 100644 --- a/routing/chainview/neutrino.go +++ b/routing/chainview/neutrino.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcutil/gcs/builder" "github.com/btcsuite/btcwallet/waddrmgr" "github.com/lightninglabs/neutrino" + "github.com/lightningnetwork/lnd/channeldb" ) // CfFilteredChainView is an implementation of the FilteredChainView interface @@ -96,7 +97,7 @@ func (c *CfFilteredChainView) Start() error { // required that an user MUST set a addr/outpoint/txid when creating a // rescan. To get around this, we'll add a "zero" outpoint, that won't // actually be matched. - var zeroPoint wire.OutPoint + var zeroPoint neutrino.InputWithScript rescanOptions := []neutrino.RescanOption{ neutrino.StartBlock(startingPoint), neutrino.QuitChan(c.quit), @@ -106,7 +107,7 @@ func (c *CfFilteredChainView) Start() error { OnFilteredBlockDisconnected: c.onFilteredBlockDisconnected, }, ), - neutrino.WatchOutPoints(zeroPoint), + neutrino.WatchInputs(zeroPoint), } // Finally, we'll create our rescan struct, start it, and launch all @@ -314,7 +315,7 @@ func (c *CfFilteredChainView) FilterBlock(blockHash *chainhash.Hash) (*FilteredB // rewound to ensure all relevant notifications are dispatched. // // NOTE: This is part of the FilteredChainView interface. -func (c *CfFilteredChainView) UpdateFilter(ops []wire.OutPoint, +func (c *CfFilteredChainView) UpdateFilter(ops []channeldb.EdgePoint, updateHeight uint32) error { log.Debugf("Updating chain filter with new UTXO's: %v", ops) @@ -323,14 +324,22 @@ func (c *CfFilteredChainView) UpdateFilter(ops []wire.OutPoint, // UTXO's, ignoring duplicates in the process. c.filterMtx.Lock() for _, op := range ops { - c.chainFilter[op] = builder.OutPointToFilterEntry(op) + c.chainFilter[op.OutPoint] = op.FundingPkScript } c.filterMtx.Unlock() + inputs := make([]neutrino.InputWithScript, len(ops)) + for i, op := range ops { + inputs[i] = neutrino.InputWithScript{ + PkScript: op.FundingPkScript, + OutPoint: op.OutPoint, + } + } + // With our internal chain view update, we'll craft a new update to the // chainView which includes our new UTXO's, and current update height. rescanUpdate := []neutrino.UpdateOption{ - neutrino.AddOutPoints(ops...), + neutrino.AddInputs(inputs...), neutrino.Rewind(updateHeight), neutrino.DisableDisconnectedNtfns(true), } From 9b9029cf4bbb09a81c4f96587bc46f3c6392bc26 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:14:29 -0700 Subject: [PATCH 20/41] routing/chainview: ensure btcd impl meets new interface requirements --- routing/chainview/btcd.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/routing/chainview/btcd.go b/routing/chainview/btcd.go index fc232f61..34d106b3 100644 --- a/routing/chainview/btcd.go +++ b/routing/chainview/btcd.go @@ -12,6 +12,7 @@ import ( "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + "github.com/lightningnetwork/lnd/channeldb" ) // BtcdFilteredChainView is an implementation of the FilteredChainView @@ -447,11 +448,18 @@ type filterUpdate struct { // rewound to ensure all relevant notifications are dispatched. // // NOTE: This is part of the FilteredChainView interface. -func (b *BtcdFilteredChainView) UpdateFilter(ops []wire.OutPoint, updateHeight uint32) error { +func (b *BtcdFilteredChainView) UpdateFilter(ops []channeldb.EdgePoint, + updateHeight uint32) error { + + newUtxos := make([]wire.OutPoint, len(ops)) + for i, op := range ops { + newUtxos[i] = op.OutPoint + } + select { case b.filterUpdates <- filterUpdate{ - newUtxos: ops, + newUtxos: newUtxos, updateHeight: updateHeight, }: return nil From fb98f5940751aa3fff52045fbac21546dda91dfa Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:15:26 -0700 Subject: [PATCH 21/41] routing/chainview: ensure bitcoind impl meets new interface requirements --- routing/chainview/bitcoind.go | 33 +++++++++++++++++++++++---------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/routing/chainview/bitcoind.go b/routing/chainview/bitcoind.go index c4014dce..7996c472 100644 --- a/routing/chainview/bitcoind.go +++ b/routing/chainview/bitcoind.go @@ -13,9 +13,9 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwallet/chain" "github.com/btcsuite/btcwallet/wtxmgr" + "github.com/lightningnetwork/lnd/channeldb" ) // BitcoindFilteredChainView is an implementation of the FilteredChainView @@ -66,6 +66,7 @@ var _ FilteredChainView = (*BitcoindFilteredChainView)(nil) func NewBitcoindFilteredChainView(config rpcclient.ConnConfig, zmqConnect string, params chaincfg.Params) (*BitcoindFilteredChainView, error) { + chainView := &BitcoindFilteredChainView{ chainFilter: make(map[wire.OutPoint]struct{}), filterUpdates: make(chan filterUpdate), @@ -308,7 +309,6 @@ func (b *BitcoindFilteredChainView) chainFilterer() { // filter, so we'll apply the update, possibly rewinding our // state partially. case update := <-b.filterUpdates: - // First, we'll add all the new UTXO's to the set of // watched UTXO's, eliminating any duplicates in the // process. @@ -325,8 +325,9 @@ func (b *BitcoindFilteredChainView) chainFilterer() { // will cause all following notifications from and // calls to it return blocks filtered with the new // filter. - b.chainClient.LoadTxFilter(false, []btcutil.Address{}, - update.newUtxos) + b.chainClient.LoadTxFilter( + false, update.newUtxos, + ) // All blocks gotten after we loaded the filter will // have the filter applied, but we will need to rescan @@ -362,7 +363,8 @@ func (b *BitcoindFilteredChainView) chainFilterer() { // block at a time, skipping blocks that might // have gone missing. rescanned, err := b.chainClient.RescanBlocks( - []chainhash.Hash{*blockHash}) + []chainhash.Hash{*blockHash}, + ) if err != nil { log.Warnf("Unable to rescan block "+ "with hash %v at height %d: %v", @@ -379,7 +381,8 @@ func (b *BitcoindFilteredChainView) chainFilterer() { continue } decoded, err := decodeJSONBlock( - &rescanned[0], i) + &rescanned[0], i, + ) if err != nil { log.Errorf("Unable to decode block: %v", err) @@ -421,9 +424,12 @@ func (b *BitcoindFilteredChainView) chainFilterer() { // We've received a new event from the chain client. case event := <-b.chainClient.Notifications(): switch e := event.(type) { + case chain.FilteredBlockConnected: - b.onFilteredBlockConnected(e.Block.Height, - e.Block.Hash, e.RelevantTxs) + b.onFilteredBlockConnected( + e.Block.Height, e.Block.Hash, e.RelevantTxs, + ) + case chain.BlockDisconnected: b.onFilteredBlockDisconnected(e.Height, e.Hash) } @@ -442,11 +448,18 @@ func (b *BitcoindFilteredChainView) chainFilterer() { // rewound to ensure all relevant notifications are dispatched. // // NOTE: This is part of the FilteredChainView interface. -func (b *BitcoindFilteredChainView) UpdateFilter(ops []wire.OutPoint, updateHeight uint32) error { +func (b *BitcoindFilteredChainView) UpdateFilter(ops []channeldb.EdgePoint, + updateHeight uint32) error { + + newUtxos := make([]wire.OutPoint, len(ops)) + for i, op := range ops { + newUtxos[i] = op.OutPoint + } + select { case b.filterUpdates <- filterUpdate{ - newUtxos: ops, + newUtxos: newUtxos, updateHeight: updateHeight, }: return nil From 8c0a151cecb04d4fba3670a9acc17ec9f3794272 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:17:19 -0700 Subject: [PATCH 22/41] routing: update UTXO verification to use new interface, graph pruning too --- routing/notifications_test.go | 14 ++++---- routing/router.go | 60 ++++++++++++++++++++--------------- 2 files changed, 42 insertions(+), 32 deletions(-) diff --git a/routing/notifications_test.go b/routing/notifications_test.go index 40d6cab8..dbd9467a 100644 --- a/routing/notifications_test.go +++ b/routing/notifications_test.go @@ -10,15 +10,15 @@ import ( prand "math/rand" + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing/chainview" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" ) var ( @@ -176,7 +176,7 @@ func (m *mockChain) addUtxo(op wire.OutPoint, out *wire.TxOut) { m.utxos[op] = *out m.Unlock() } -func (m *mockChain) GetUtxo(op *wire.OutPoint, _ uint32) (*wire.TxOut, error) { +func (m *mockChain) GetUtxo(op *wire.OutPoint, _ []byte, _ uint32) (*wire.TxOut, error) { m.RLock() defer m.RUnlock() @@ -242,12 +242,12 @@ func (m *mockChainView) Reset() { m.staleBlocks = make(chan *chainview.FilteredBlock, 10) } -func (m *mockChainView) UpdateFilter(ops []wire.OutPoint, updateHeight uint32) error { +func (m *mockChainView) UpdateFilter(ops []channeldb.EdgePoint, updateHeight uint32) error { m.Lock() defer m.Unlock() for _, op := range ops { - m.filter[op] = struct{}{} + m.filter[op.OutPoint] = struct{}{} } return nil diff --git a/routing/router.go b/routing/router.go index dcef3a28..88858865 100644 --- a/routing/router.go +++ b/routing/router.go @@ -967,7 +967,7 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { // to obtain the full funding outpoint that's encoded within // the channel ID. channelID := lnwire.NewShortChanIDFromInt(msg.ChannelID) - fundingPoint, err := r.fetchChanPoint(&channelID) + fundingPoint, _, err := r.fetchChanPoint(&channelID) if err != nil { r.rejectMtx.Lock() r.rejectCache[msg.ChannelID] = struct{}{} @@ -977,11 +977,25 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { "chan_id=%v: %v", msg.ChannelID, err) } + // Recreate witness output to be sure that declared in channel + // edge bitcoin keys and channel value corresponds to the + // reality. + witnessScript, err := lnwallet.GenMultiSigScript( + msg.BitcoinKey1Bytes[:], msg.BitcoinKey2Bytes[:], + ) + if err != nil { + return err + } + fundingPkScript, err := lnwallet.WitnessScriptHash(witnessScript) + if err != nil { + return err + } + // Now that we have the funding outpoint of the channel, ensure // that it hasn't yet been spent. If so, then this channel has // been closed so we'll ignore it. chanUtxo, err := r.cfg.Chain.GetUtxo( - fundingPoint, channelID.BlockHeight, + fundingPoint, fundingPkScript, channelID.BlockHeight, ) if err != nil { r.rejectMtx.Lock() @@ -993,26 +1007,14 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { fundingPoint, err) } - // Recreate witness output to be sure that declared in channel - // edge bitcoin keys and channel value corresponds to the - // reality. - _, witnessOutput, err := lnwallet.GenFundingPkScript( - msg.BitcoinKey1Bytes[:], msg.BitcoinKey2Bytes[:], - chanUtxo.Value, - ) - if err != nil { - return errors.Errorf("unable to create funding pk "+ - "script: %v", err) - } - // By checking the equality of witness pkscripts we checks that // funding witness script is multisignature lock which contains // both local and remote public keys which was declared in // channel edge and also that the announced channel value is // right. - if !bytes.Equal(witnessOutput.PkScript, chanUtxo.PkScript) { + if !bytes.Equal(fundingPkScript, chanUtxo.PkScript) { return errors.Errorf("pkScript mismatch: expected %x, "+ - "got %x", witnessOutput.PkScript, chanUtxo.PkScript) + "got %x", fundingPkScript, chanUtxo.PkScript) } // TODO(roasbeef): this is a hack, needs to be removed @@ -1034,7 +1036,12 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { // update the current UTXO filter within our active // FilteredChainView so we are notified if/when this channel is // closed. - filterUpdate := []wire.OutPoint{*fundingPoint} + filterUpdate := []channeldb.EdgePoint{ + { + FundingPkScript: fundingPkScript, + OutPoint: *fundingPoint, + }, + } err = r.cfg.ChainView.UpdateFilter( filterUpdate, atomic.LoadUint32(&r.bestHeight), ) @@ -1105,7 +1112,9 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { // Before we can update the channel information, we'll // ensure that the target channel is still open by // querying the utxo-set for its existence. - chanPoint, err := r.fetchChanPoint(&channelID) + chanPoint, fundingPkScript, err := r.fetchChanPoint( + &channelID, + ) if err != nil { r.rejectMtx.Lock() r.rejectCache[msg.ChannelID] = struct{}{} @@ -1116,7 +1125,7 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { msg.ChannelID, err) } _, err = r.cfg.Chain.GetUtxo( - chanPoint, channelID.BlockHeight, + chanPoint, fundingPkScript, channelID.BlockHeight, ) if err != nil { r.rejectMtx.Lock() @@ -1157,21 +1166,22 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { } // fetchChanPoint retrieves the original outpoint which is encoded within the -// channelID. +// channelID. This method also return the public key script for the target +// transaction. // // TODO(roasbeef): replace with call to GetBlockTransaction? (would allow to // later use getblocktxn) -func (r *ChannelRouter) fetchChanPoint(chanID *lnwire.ShortChannelID) (*wire.OutPoint, error) { +func (r *ChannelRouter) fetchChanPoint(chanID *lnwire.ShortChannelID) (*wire.OutPoint, []byte, error) { // First fetch the block hash by the block number encoded, then use // that hash to fetch the block itself. blockNum := int64(chanID.BlockHeight) blockHash, err := r.cfg.Chain.GetBlockHash(blockNum) if err != nil { - return nil, err + return nil, nil, err } fundingBlock, err := r.cfg.Chain.GetBlock(blockHash) if err != nil { - return nil, err + return nil, nil, err } // As a sanity check, ensure that the advertised transaction index is @@ -1179,7 +1189,7 @@ func (r *ChannelRouter) fetchChanPoint(chanID *lnwire.ShortChannelID) (*wire.Out // block. numTxns := uint32(len(fundingBlock.Transactions)) if chanID.TxIndex > numTxns-1 { - return nil, fmt.Errorf("tx_index=#%v is out of range "+ + return nil, nil, fmt.Errorf("tx_index=#%v is out of range "+ "(max_index=%v), network_chan_id=%v\n", chanID.TxIndex, numTxns-1, spew.Sdump(chanID)) } @@ -1190,7 +1200,7 @@ func (r *ChannelRouter) fetchChanPoint(chanID *lnwire.ShortChannelID) (*wire.Out return &wire.OutPoint{ Hash: fundingTx.TxHash(), Index: uint32(chanID.TxPosition), - }, nil + }, fundingTx.TxOut[chanID.TxPosition].PkScript, nil } // routingMsg couples a routing related routing topology update to the From 21451bf251897eda3b68dd2731b316efd5313986 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:20:05 -0700 Subject: [PATCH 23/41] lnwallet: update dual funder verification code to use pkscript in GetUtxo --- lnwallet/wallet.go | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/lnwallet/wallet.go b/lnwallet/wallet.go index 0dcc4cc3..8a2d9b1f 100644 --- a/lnwallet/wallet.go +++ b/lnwallet/wallet.go @@ -8,20 +8,20 @@ import ( "sync" "sync/atomic" + "github.com/btcsuite/btcd/blockchain" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcutil/hdkeychain" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/keychain" "github.com/lightningnetwork/lnd/lnwire" - "github.com/btcsuite/btcd/blockchain" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcutil/hdkeychain" - "github.com/lightningnetwork/lnd/shachain" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil/txsort" + "github.com/lightningnetwork/lnd/shachain" ) const ( @@ -262,15 +262,15 @@ type LightningWallet struct { // is removed from limbo. Each reservation is tracked by a unique // monotonically integer. All requests concerning the channel MUST // carry a valid, active funding ID. - fundingLimbo map[uint64]*ChannelReservation - limboMtx sync.RWMutex + fundingLimbo map[uint64]*ChannelReservation + limboMtx sync.RWMutex // lockedOutPoints is a set of the currently locked outpoint. This // information is kept in order to provide an easy way to unlock all // the currently locked outpoints. lockedOutPoints map[wire.OutPoint]struct{} - quit chan struct{} + quit chan struct{} wg sync.WaitGroup @@ -971,8 +971,18 @@ func (l *LightningWallet) handleFundingCounterPartySigs(msg *addCounterPartySigs // Fetch the alleged previous output along with the // pkscript referenced by this input. - // TODO(roasbeef): when dual funder pass actual height-hint - output, err := l.Cfg.ChainIO.GetUtxo(&txin.PreviousOutPoint, 0) + // + // TODO(roasbeef): when dual funder pass actual + // height-hint + pkScript, err := WitnessScriptHash( + txin.Witness[len(txin.Witness)-1], + ) + if err != nil { + } + output, err := l.Cfg.ChainIO.GetUtxo( + &txin.PreviousOutPoint, + pkScript, 0, + ) if output == nil { msg.err <- fmt.Errorf("input to funding tx "+ "does not exist: %v", err) From f9f9eefab402f20c00355e19a7784cadd99a3bd8 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:20:52 -0700 Subject: [PATCH 24/41] lnwallet: update GetUtxo to accept the pkScript to comply with new gcs filter --- lnwallet/interface.go | 6 ++++-- lnwallet/script_utils_test.go | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lnwallet/interface.go b/lnwallet/interface.go index 416157fc..d9714a1c 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -237,10 +237,12 @@ type BlockChainIO interface { // GetUtxo attempts to return the passed outpoint if it's still a // member of the utxo set. The passed height hint should be the "birth - // height" of the passed outpoint. In the case that the output is in + // height" of the passed outpoint. The script passed should be the + // script that the oupoint creates. In the case that the output is in // the UTXO set, then the output corresponding to that output is // returned. Otherwise, a non-nil error will be returned. - GetUtxo(op *wire.OutPoint, heightHint uint32) (*wire.TxOut, error) + GetUtxo(op *wire.OutPoint, pkScript []byte, + heightHint uint32) (*wire.TxOut, error) // GetBlockHash returns the hash of the block in the best blockchain // at the given height. diff --git a/lnwallet/script_utils_test.go b/lnwallet/script_utils_test.go index 0bfac97a..7adacba4 100644 --- a/lnwallet/script_utils_test.go +++ b/lnwallet/script_utils_test.go @@ -8,12 +8,12 @@ import ( "testing" "time" - "github.com/lightningnetwork/lnd/keychain" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + "github.com/lightningnetwork/lnd/keychain" ) // TestCommitmentSpendValidation test the spendability of both outputs within From 84c458cc9ff107e9483972a18591018d01ad56a6 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:21:09 -0700 Subject: [PATCH 25/41] lnwallet/btcwallet: update neutrino GetUtxo impl to use new API --- lnwallet/btcwallet/blockchain.go | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lnwallet/btcwallet/blockchain.go b/lnwallet/btcwallet/blockchain.go index 22dee5dd..62a72650 100644 --- a/lnwallet/btcwallet/blockchain.go +++ b/lnwallet/btcwallet/blockchain.go @@ -9,10 +9,10 @@ import ( "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" - "github.com/lightninglabs/neutrino" - "github.com/lightningnetwork/lnd/lnwallet" "github.com/btcsuite/btcwallet/chain" "github.com/btcsuite/btcwallet/waddrmgr" + "github.com/lightninglabs/neutrino" + "github.com/lightningnetwork/lnd/lnwallet" ) var ( @@ -33,15 +33,21 @@ func (b *BtcWallet) GetBestBlock() (*chainhash.Hash, int32, error) { return b.chain.GetBestBlock() } -// GetUtxo returns the original output referenced by the passed outpoint. +// GetUtxo returns the original output referenced by the passed outpoint that +// creates the target pkScript. // // This method is a part of the lnwallet.BlockChainIO interface. -func (b *BtcWallet) GetUtxo(op *wire.OutPoint, heightHint uint32) (*wire.TxOut, error) { +func (b *BtcWallet) GetUtxo(op *wire.OutPoint, pkScript []byte, + heightHint uint32) (*wire.TxOut, error) { + switch backend := b.chain.(type) { case *chain.NeutrinoClient: spendReport, err := backend.CS.GetUtxo( - neutrino.WatchOutPoints(*op), + neutrino.WatchInputs(neutrino.InputWithScript{ + OutPoint: *op, + PkScript: pkScript, + }), neutrino.StartBlock(&waddrmgr.BlockStamp{ Height: int32(heightHint), }), From 343c1b80d24237645de46626d22d1b4feaa75afe Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:22:17 -0700 Subject: [PATCH 26/41] lnwallet: update NewBreachRetribution to include pkScripts for htlc outputs In this commit, we update the NewBreachRetribution method to include pkScripts for htlc outputs. We do this now, as the breach arbiter will need the raw pkScript when attempting to request spend notifications for each HTLC. --- lnwallet/channel.go | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lnwallet/channel.go b/lnwallet/channel.go index 7651a6aa..fc80fd42 100644 --- a/lnwallet/channel.go +++ b/lnwallet/channel.go @@ -2043,8 +2043,8 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, htlcRetributions := make([]HtlcRetribution, 0, len(revokedSnapshot.Htlcs)) for _, htlc := range revokedSnapshot.Htlcs { var ( - htlcScript []byte - err error + htlcWitnessScript []byte + err error ) // If the HTLC is dust, then we'll skip it as it doesn't have @@ -2072,7 +2072,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // the sender of the HTLC (relative to us). So we'll // re-generate the sender HTLC script. if htlc.Incoming { - htlcScript, err = senderHTLCScript( + htlcWitnessScript, err = senderHTLCScript( keyRing.RemoteHtlcKey, keyRing.LocalHtlcKey, keyRing.RevocationKey, htlc.RHash[:], ) @@ -2084,7 +2084,7 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, // Otherwise, is this was an outgoing HTLC that we // sent, then from the PoV of the remote commitment // state, they're the receiver of this HTLC. - htlcScript, err = receiverHTLCScript( + htlcWitnessScript, err = receiverHTLCScript( htlc.RefundTimeout, keyRing.LocalHtlcKey, keyRing.RemoteHtlcKey, keyRing.RevocationKey, htlc.RHash[:], @@ -2094,13 +2094,19 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64, } } + htlcPkScript, err := WitnessScriptHash(htlcWitnessScript) + if err != nil { + return nil, err + } + htlcRetributions = append(htlcRetributions, HtlcRetribution{ SignDesc: SignDescriptor{ KeyDesc: chanState.LocalChanCfg.RevocationBasePoint, DoubleTweak: commitmentSecret, - WitnessScript: htlcScript, + WitnessScript: htlcWitnessScript, Output: &wire.TxOut{ - Value: int64(htlc.Amt.ToSatoshis()), + PkScript: htlcPkScript, + Value: int64(htlc.Amt.ToSatoshis()), }, HashType: txscript.SigHashAll, }, @@ -5295,7 +5301,8 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon // With the sign desc created, we can now construct the full witness // for the timeout transaction, and populate it as well. timeoutWitness, err := senderHtlcSpendTimeout( - htlc.Signature, signer, &timeoutSignDesc, timeoutTx) + htlc.Signature, signer, &timeoutSignDesc, timeoutTx, + ) if err != nil { return nil, err } From 88bedec65b45d9fae2dc51770cfee95db3a09340 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:22:59 -0700 Subject: [PATCH 27/41] discovery: update unit tests to account for new ChainNotifier API --- discovery/gossiper_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/discovery/gossiper_test.go b/discovery/gossiper_test.go index b3880c08..44edf187 100644 --- a/discovery/gossiper_test.go +++ b/discovery/gossiper_test.go @@ -18,6 +18,9 @@ import ( "io/ioutil" "os" + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/chainntnfs" @@ -26,9 +29,6 @@ import ( "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/routing" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" ) var ( @@ -258,12 +258,12 @@ func newMockNotifier() *mockNotifier { } func (m *mockNotifier) RegisterConfirmationsNtfn(txid *chainhash.Hash, - numConfs, _ uint32) (*chainntnfs.ConfirmationEvent, error) { + _ []byte, numConfs, _ uint32) (*chainntnfs.ConfirmationEvent, error) { return nil, nil } -func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, +func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, _ []byte, _ uint32) (*chainntnfs.SpendEvent, error) { return nil, nil } From 067db8572c055dfde8fe76ff674ec62c8edc7762 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:23:47 -0700 Subject: [PATCH 28/41] test+lnd: update unit test mocks to adhere to new interface API's --- breacharbiter_test.go | 10 +++++----- chainparams.go | 6 +++--- fundingmanager_test.go | 6 +++++- mock.go | 12 ++++++------ test_utils.go | 8 ++++---- 5 files changed, 23 insertions(+), 19 deletions(-) diff --git a/breacharbiter_test.go b/breacharbiter_test.go index 85d10fe9..45dc93f5 100644 --- a/breacharbiter_test.go +++ b/breacharbiter_test.go @@ -18,7 +18,12 @@ import ( "testing" "time" + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btclog" + "github.com/btcsuite/btcutil" "github.com/go-errors/errors" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" @@ -27,11 +32,6 @@ import ( "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" ) var ( diff --git a/chainparams.go b/chainparams.go index 2751d81e..812bb6f3 100644 --- a/chainparams.go +++ b/chainparams.go @@ -1,13 +1,13 @@ package main import ( - "github.com/lightningnetwork/lnd/keychain" - litecoinCfg "github.com/ltcsuite/ltcd/chaincfg" - litecoinWire "github.com/ltcsuite/ltcd/wire" "github.com/btcsuite/btcd/chaincfg" bitcoinCfg "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" bitcoinWire "github.com/btcsuite/btcd/wire" + "github.com/lightningnetwork/lnd/keychain" + litecoinCfg "github.com/ltcsuite/ltcd/chaincfg" + litecoinWire "github.com/ltcsuite/ltcd/wire" ) // activeNetParams is a pointer to the parameters specific to the currently diff --git a/fundingmanager_test.go b/fundingmanager_test.go index fabcf780..a019f272 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -29,6 +29,10 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" + + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" ) const ( @@ -126,7 +130,7 @@ func (m *mockNotifier) Stop() error { return nil } -func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, +func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, _ []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) { return &chainntnfs.SpendEvent{ Spend: make(chan *chainntnfs.SpendDetail), diff --git a/mock.go b/mock.go index 28794bc4..cd0461e8 100644 --- a/mock.go +++ b/mock.go @@ -5,15 +5,15 @@ import ( "fmt" "sync" - "github.com/lightningnetwork/lnd/chainntnfs" - "github.com/lightningnetwork/lnd/keychain" - "github.com/lightningnetwork/lnd/lnwallet" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" + "github.com/lightningnetwork/lnd/chainntnfs" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnwallet" ) // The block height returned by the mock BlockChainIO's GetBestBlock. @@ -105,7 +105,7 @@ func (m *mockNotfier) Start() error { func (m *mockNotfier) Stop() error { return nil } -func (m *mockNotfier) RegisterSpendNtfn(outpoint *wire.OutPoint, +func (m *mockNotfier) RegisterSpendNtfn(outpoint *wire.OutPoint, _ []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) { return &chainntnfs.SpendEvent{ Spend: make(chan *chainntnfs.SpendDetail), @@ -131,7 +131,7 @@ func makeMockSpendNotifier() *mockSpendNotifier { } func (m *mockSpendNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, - heightHint uint32) (*chainntnfs.SpendEvent, error) { + _ []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) { m.mtx.Lock() defer m.mtx.Unlock() @@ -172,7 +172,7 @@ func (*mockChainIO) GetBestBlock() (*chainhash.Hash, int32, error) { return activeNetParams.GenesisHash, fundingBroadcastHeight, nil } -func (*mockChainIO) GetUtxo(op *wire.OutPoint, +func (*mockChainIO) GetUtxo(op *wire.OutPoint, _ []byte, heightHint uint32) (*wire.TxOut, error) { return nil, nil } diff --git a/test_utils.go b/test_utils.go index c44b4b43..0de7f0f8 100644 --- a/test_utils.go +++ b/test_utils.go @@ -10,6 +10,10 @@ import ( "net" "os" + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" + "github.com/btcsuite/btcutil" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/contractcourt" @@ -18,10 +22,6 @@ import ( "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/shachain" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" ) var ( From 53416260e3463cb869c97c602ba2659e67058e81 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:24:04 -0700 Subject: [PATCH 29/41] htlcswitch: update test to account for new ChainNotifier API --- htlcswitch/mock.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/htlcswitch/mock.go b/htlcswitch/mock.go index dbacaa69..9d3a2938 100644 --- a/htlcswitch/mock.go +++ b/htlcswitch/mock.go @@ -13,6 +13,10 @@ import ( "testing" "time" + "github.com/btcsuite/btcd/btcec" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/txscript" + "github.com/btcsuite/btcd/wire" "github.com/btcsuite/fastsha256" "github.com/go-errors/errors" "github.com/lightningnetwork/lightning-onion" @@ -22,10 +26,6 @@ import ( "github.com/lightningnetwork/lnd/lnpeer" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/txscript" - "github.com/btcsuite/btcd/wire" ) type mockPreimageCache struct { @@ -800,7 +800,7 @@ func (m *mockNotifier) Stop() error { return nil } -func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, +func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, _ []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) { return &chainntnfs.SpendEvent{ From 62ab7424affdd87fa7bc25048a380b65dc20d40a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:26:53 -0700 Subject: [PATCH 30/41] breacharbiter: update second level pkScript as well In this commit, we update the convertToSecondLevelRevoke function to also upgrade the second level pkScript as well. This is required as we'll need to use this for a confirmation notification once we sweep the output in the case that the remote party goes to the second layer. --- breacharbiter.go | 1 + 1 file changed, 1 insertion(+) diff --git a/breacharbiter.go b/breacharbiter.go index 950fc497..132477d4 100644 --- a/breacharbiter.go +++ b/breacharbiter.go @@ -298,6 +298,7 @@ func convertToSecondLevelRevoke(bo *breachedOutput, breachInfo *retributionInfo, newAmt := spendingTx.TxOut[0].Value bo.amt = btcutil.Amount(newAmt) bo.signDesc.Output.Value = newAmt + bo.signDesc.Output.PkScript = spendingTx.TxOut[0].PkScript // Finally, we'll need to adjust the witness program in the // SignDescriptor. From ab5b8b8fac8c6caa545d40c4b0c5ccaa51a4edbe Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:27:31 -0700 Subject: [PATCH 31/41] breacharbiter: use the pkScript of the breached output for spend ntnfs --- breacharbiter.go | 1 + 1 file changed, 1 insertion(+) diff --git a/breacharbiter.go b/breacharbiter.go index 132477d4..0862acd8 100644 --- a/breacharbiter.go +++ b/breacharbiter.go @@ -360,6 +360,7 @@ func (b *breachArbiter) waitForSpendEvent(breachInfo *retributionInfo, var err error spendNtfn, err = b.cfg.Notifier.RegisterSpendNtfn( &breachedOutput.outpoint, + breachedOutput.signDesc.Output.PkScript, breachInfo.breachHeight, ) if err != nil { From ac9f9bd061003e89dbc68c5347b02a6aaa4d2120 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:41:22 -0700 Subject: [PATCH 32/41] contractcourt: remove now obsolete code from the chain arbitrator We no longer need this code fragment as the chain watcher is now able to handle confirmed cooperative channel closures itself. --- contractcourt/chain_arbitrator.go | 56 ------------------------------- 1 file changed, 56 deletions(-) diff --git a/contractcourt/chain_arbitrator.go b/contractcourt/chain_arbitrator.go index 6656ef22..9ab11da8 100644 --- a/contractcourt/chain_arbitrator.go +++ b/contractcourt/chain_arbitrator.go @@ -384,22 +384,6 @@ func (c *ChainArbitrator) Start() error { // the chain any longer, only resolve the contracts on the confirmed // commitment. for _, closeChanInfo := range closingChannels { - // If this is a pending cooperative close channel then we'll - // simply launch a goroutine to wait until the closing - // transaction has been confirmed. - // TODO(halseth): can remove this since no coop close channels - // should be "pending close" after the recent changes. Keeping - // it for a bit in case someone with a coop close channel in - // the pending close state upgrades to the new commit. - if closeChanInfo.CloseType == channeldb.CooperativeClose { - go c.watchForChannelClose(closeChanInfo) - - // TODO(roasbeef): actually need arb to possibly - // recover from race condition broadcast? - // * if do, can't recover from multi-broadcast - continue - } - blockEpoch, err := c.cfg.Notifier.RegisterBlockEpochNtfn() if err != nil { return err @@ -493,46 +477,6 @@ func (c *ChainArbitrator) Stop() error { return nil } -// watchForChannelClose is used by the ChainArbitrator to watch for the -// ultimate on-chain conformation of an existing cooperative channel closure. -// This is needed if we started a co-op close, but it wasn't fully confirmed -// before we restarted. -// -// NOTE: This must be launched as a goroutine. -func (c *ChainArbitrator) watchForChannelClose(closeInfo *channeldb.ChannelCloseSummary) { - spendNtfn, err := c.cfg.Notifier.RegisterSpendNtfn( - &closeInfo.ChanPoint, closeInfo.CloseHeight, - ) - if err != nil { - log.Errorf("unable to register for spend: %v", err) - return - } - - log.Infof("Waiting for ChannelPoint(%v) to be coop closed on chain", - closeInfo.ChanPoint) - - var ( - commitSpend *chainntnfs.SpendDetail - ok bool - ) - select { - case commitSpend, ok = <-spendNtfn.Spend: - if !ok { - return - } - case <-c.quit: - return - } - - log.Infof("ChannelPoint(%v) is fully closed, at height: %v", - closeInfo.ChanPoint, commitSpend.SpendingHeight) - - if err := c.resolveContract(closeInfo.ChanPoint, nil); err != nil { - log.Errorf("unable to resolve contract: %v", err) - } - -} - // ContractSignals wraps the two signals that affect the state of a channel // being watched by an arbitrator. The two signals we care about are: the // channel has a new set of HTLC's, and the remote party has just broadcast From 08e11d197dd94ca55424c8eaabfcc99ebab30d93 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:42:17 -0700 Subject: [PATCH 33/41] contractcourt: update chain watcher to make funding pkScript for spend ntfn --- contractcourt/chain_watcher.go | 15 ++++++++++++++- contractcourt/chain_watcher_test.go | 6 +++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/contractcourt/chain_watcher.go b/contractcourt/chain_watcher.go index 8126d37d..04fc8647 100644 --- a/contractcourt/chain_watcher.go +++ b/contractcourt/chain_watcher.go @@ -173,8 +173,21 @@ func (c *chainWatcher) Start() error { heightHint = chanState.FundingBroadcastHeight } + localKey := chanState.LocalChanCfg.MultiSigKey.PubKey.SerializeCompressed() + remoteKey := chanState.RemoteChanCfg.MultiSigKey.PubKey.SerializeCompressed() + multiSigScript, err := lnwallet.GenMultiSigScript( + localKey, remoteKey, + ) + if err != nil { + return err + } + pkScript, err := lnwallet.WitnessScriptHash(multiSigScript) + if err != nil { + return err + } + spendNtfn, err := c.cfg.notifier.RegisterSpendNtfn( - fundingOut, heightHint, + fundingOut, pkScript, heightHint, ) if err != nil { return err diff --git a/contractcourt/chain_watcher_test.go b/contractcourt/chain_watcher_test.go index 5928be66..ab0b8f39 100644 --- a/contractcourt/chain_watcher_test.go +++ b/contractcourt/chain_watcher_test.go @@ -6,11 +6,11 @@ import ( "testing" "time" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" ) type mockNotifier struct { @@ -35,7 +35,7 @@ func (m *mockNotifier) Start() error { func (m *mockNotifier) Stop() error { return nil } -func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, +func (m *mockNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, _ []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) { return &chainntnfs.SpendEvent{ Spend: m.spendChan, From c6950fcc2f9e672643a709651dae5063289ced82 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:42:43 -0700 Subject: [PATCH 34/41] contractcourt: update channel arbitrator unit tests due to recent API changes --- contractcourt/channel_arbitrator.go | 2 +- contractcourt/channel_arbitrator_test.go | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/contractcourt/channel_arbitrator.go b/contractcourt/channel_arbitrator.go index 3d96d51b..8c3689b1 100644 --- a/contractcourt/channel_arbitrator.go +++ b/contractcourt/channel_arbitrator.go @@ -5,12 +5,12 @@ import ( "sync" "sync/atomic" + "github.com/btcsuite/btcd/wire" "github.com/davecgh/go-spew/spew" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" - "github.com/btcsuite/btcd/wire" ) const ( diff --git a/contractcourt/channel_arbitrator_test.go b/contractcourt/channel_arbitrator_test.go index db0f267b..f1748286 100644 --- a/contractcourt/channel_arbitrator_test.go +++ b/contractcourt/channel_arbitrator_test.go @@ -5,11 +5,11 @@ import ( "testing" "time" + "github.com/btcsuite/btcd/chaincfg/chainhash" + "github.com/btcsuite/btcd/wire" "github.com/lightningnetwork/lnd/chainntnfs" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" - "github.com/btcsuite/btcd/chaincfg/chainhash" - "github.com/btcsuite/btcd/wire" ) type mockChainIO struct{} @@ -18,7 +18,7 @@ func (*mockChainIO) GetBestBlock() (*chainhash.Hash, int32, error) { return nil, 0, nil } -func (*mockChainIO) GetUtxo(op *wire.OutPoint, +func (*mockChainIO) GetUtxo(op *wire.OutPoint, _ []byte, heightHint uint32) (*wire.TxOut, error) { return nil, nil } From d9969f5dc2c26c2256906edaa2fb118f1fd571f0 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:43:46 -0700 Subject: [PATCH 35/41] contractcourt: update contract resolvers to use pkScripts for spend ntfns --- contractcourt/contract_resolvers.go | 30 +++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/contractcourt/contract_resolvers.go b/contractcourt/contract_resolvers.go index 541b40fb..086b8d26 100644 --- a/contractcourt/contract_resolvers.go +++ b/contractcourt/contract_resolvers.go @@ -172,6 +172,7 @@ func (h *htlcTimeoutResolver) Resolve() (ContractResolver, error) { // has been spent by a confirmed transaction. spendNtfn, err := h.Notifier.RegisterSpendNtfn( &h.htlcResolution.ClaimOutpoint, + h.htlcResolution.SweepSignDesc.Output.PkScript, h.broadcastHeight, ) if err != nil { @@ -582,7 +583,9 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) { // To wrap this up, we'll wait until the second-level transaction has // been spent, then fully resolve the contract. spendNtfn, err := h.Notifier.RegisterSpendNtfn( - &h.htlcResolution.ClaimOutpoint, h.broadcastHeight, + &h.htlcResolution.ClaimOutpoint, + h.htlcResolution.SweepSignDesc.Output.PkScript, + h.broadcastHeight, ) if err != nil { return nil, err @@ -783,17 +786,34 @@ func (h *htlcOutgoingContestResolver) Resolve() (ContractResolver, error) { // output. If this isn't our commitment transaction, it'll be right on // the resolution. Otherwise, we fetch this pointer from the input of // the time out transaction. - var outPointToWatch wire.OutPoint + var ( + outPointToWatch wire.OutPoint + scriptToWatch []byte + err error + ) if h.htlcResolution.SignedTimeoutTx == nil { outPointToWatch = h.htlcResolution.ClaimOutpoint + scriptToWatch = h.htlcResolution.SweepSignDesc.Output.PkScript } else { + // If this is the remote party's commitment, then we'll need to + // grab watch the output that our timeout transaction points + // to. We can directly grab the outpoint, then also extract the + // witness script (the last element of the witness stack) to + // re-construct the pkScipt we need to watch. outPointToWatch = h.htlcResolution.SignedTimeoutTx.TxIn[0].PreviousOutPoint + witness := h.htlcResolution.SignedTimeoutTx.TxIn[0].Witness + scriptToWatch, err = lnwallet.WitnessScriptHash( + witness[len(witness)-1], + ) + if err != nil { + return nil, err + } } // First, we'll register for a spend notification for this output. If - // the remote party sweeps with the pre-image, we'll be notified. + // the remote party sweeps with the pre-image, we'll be notified. spendNtfn, err := h.Notifier.RegisterSpendNtfn( - &outPointToWatch, h.broadcastHeight, + &outPointToWatch, scriptToWatch, h.broadcastHeight, ) if err != nil { return nil, err @@ -802,6 +822,7 @@ func (h *htlcOutgoingContestResolver) Resolve() (ContractResolver, error) { // We'll quickly check to see if the output has already been spent. select { // If the output has already been spent, then we can stop early and + // sweep the pre-image from the output. case commitSpend, ok := <-spendNtfn.Spend: if !ok { return nil, fmt.Errorf("quitting") @@ -1295,6 +1316,7 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) { // until the commitment output has been spent. spendNtfn, err := c.Notifier.RegisterSpendNtfn( &c.commitResolution.SelfOutPoint, + c.commitResolution.SelfOutputSignDesc.Output.PkScript, c.broadcastHeight, ) if err != nil { From 2d71b5a0d58aae91a0e3473631ae9674ae35111a Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:47:12 -0700 Subject: [PATCH 36/41] build: update build to point to latest versions of btcd+btcwallet+neutrino --- Gopkg.lock | 10 +++++----- Gopkg.toml | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Gopkg.lock b/Gopkg.lock index 844640b4..8f212774 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -79,7 +79,7 @@ "txscript", "wire" ] - revision = "86fed781132ac890ee03e906e4ecd5d6fa180c64" + revision = "9a2f9524024889e129a5422aca2cff73cb3eabf6" [[projects]] name = "github.com/btcsuite/btclog" @@ -97,7 +97,7 @@ "hdkeychain", "txsort" ] - revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" + revision = "ab6388e0c60ae4834a1f57511e20c17b5f78be4b" [[projects]] name = "github.com/btcsuite/btcwallet" @@ -118,7 +118,7 @@ "walletdb/bdb", "wtxmgr" ] - revision = "64b5b448f5e6853a2d870f388504a7807bc951f1" + revision = "a4d9da433fcfaeec50d3e9657e6d511d14cddadf" [[projects]] branch = "master" @@ -231,7 +231,7 @@ "filterdb", "headerfs" ] - revision = "03f4c660ea0d1586331f32561185d45eab1ba4c9" + revision = "d5054cea8fe43c324a473e8aa7e0f6dd93622125" [[projects]] name = "github.com/lightningnetwork/lightning-onion" @@ -404,6 +404,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "7ac5bd06ab8aa8f6a01f191092ac37d1e33200f366fa3b52fb4152369d76c5be" + inputs-digest = "dcfe757e222cc26b43d926797fb0cb9f87a18da54dca6615e701eb715c35b375" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 32d09da7..6b05d65e 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -44,7 +44,7 @@ [[constraint]] name = "github.com/lightninglabs/neutrino" - revision = "03f4c660ea0d1586331f32561185d45eab1ba4c9" + revision = "d5054cea8fe43c324a473e8aa7e0f6dd93622125" [[constraint]] name = "github.com/lightningnetwork/lightning-onion" @@ -64,15 +64,15 @@ [[constraint]] name = "github.com/btcsuite/btcutil" - revision = "d4cc87b860166d00d6b5b9e0d3b3d71d6088d4d4" + revision = "ab6388e0c60ae4834a1f57511e20c17b5f78be4b" [[constraint]] name = "github.com/btcsuite/btcd" - revision = "86fed781132ac890ee03e906e4ecd5d6fa180c64" + revision = "9a2f9524024889e129a5422aca2cff73cb3eabf6" [[constraint]] name = "github.com/btcsuite/btcwallet" - revision = "64b5b448f5e6853a2d870f388504a7807bc951f1" + revision = "a4d9da433fcfaeec50d3e9657e6d511d14cddadf" [[constraint]] name = "github.com/tv42/zbase32" From 293a377edd4ea4823890197e8c81380075174e96 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Wed, 25 Jul 2018 19:33:46 -0700 Subject: [PATCH 37/41] lnwallet: modify IsSynced() for neutrino backend to ensure filter headers are synced --- lnwallet/btcwallet/btcwallet.go | 24 ++++++++++++++++++++---- lnwallet/interface.go | 2 +- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/lnwallet/btcwallet/btcwallet.go b/lnwallet/btcwallet/btcwallet.go index 3829d296..cc946e42 100644 --- a/lnwallet/btcwallet/btcwallet.go +++ b/lnwallet/btcwallet/btcwallet.go @@ -9,8 +9,6 @@ import ( "sync" "time" - "github.com/lightningnetwork/lnd/keychain" - "github.com/lightningnetwork/lnd/lnwallet" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/chaincfg/chainhash" @@ -21,6 +19,8 @@ import ( "github.com/btcsuite/btcwallet/waddrmgr" base "github.com/btcsuite/btcwallet/wallet" "github.com/btcsuite/btcwallet/walletdb" + "github.com/lightningnetwork/lnd/keychain" + "github.com/lightningnetwork/lnd/lnwallet" ) const ( @@ -745,8 +745,24 @@ func (b *BtcWallet) IsSynced() (bool, int64, error) { return false, 0, err } - // If the timestamp no the best header is more than 2 hours in the + // If the timestamp on the best header is more than 2 hours in the // past, then we're not yet synced. minus24Hours := time.Now().Add(-2 * time.Hour) - return !blockHeader.Timestamp.Before(minus24Hours), bestTimestamp, nil + if blockHeader.Timestamp.Before(minus24Hours) { + return false, bestTimestamp, nil + } + + // If this is neutrino, then we'll also want to wait until the set of + // filter headers also match + if neutrinoNode, ok := b.chain.(*chain.NeutrinoClient); ok { + filterDB := neutrinoNode.CS.RegFilterHeaders + _, filterHeaderTip, err := filterDB.ChainTip() + if err != nil { + return false, 0, err + } + + return filterHeaderTip == uint32(bestHeight), bestTimestamp, nil + } + + return true, bestTimestamp, nil } diff --git a/lnwallet/interface.go b/lnwallet/interface.go index d9714a1c..0aad560a 100644 --- a/lnwallet/interface.go +++ b/lnwallet/interface.go @@ -238,7 +238,7 @@ type BlockChainIO interface { // GetUtxo attempts to return the passed outpoint if it's still a // member of the utxo set. The passed height hint should be the "birth // height" of the passed outpoint. The script passed should be the - // script that the oupoint creates. In the case that the output is in + // script that the outpoint creates. In the case that the output is in // the UTXO set, then the output corresponding to that output is // returned. Otherwise, a non-nil error will be returned. GetUtxo(op *wire.OutPoint, pkScript []byte, From 3c60861005cc9ccee7d3ad9109c78221a1e58f7f Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 31 Jul 2018 18:46:13 -0700 Subject: [PATCH 38/41] routing/chainview: give neutrino backend more time to catch up for re-org test In this commit, we modify the test to explitlcy give the neutrino backend more time to catch up compared to the RPC backends. We do this as a recent change has been made in the neutrino backend to wait for the filter headers to finish syncing before proceeding with the rescan itself. As a result, we'll need to account for this in the test and sleep enough to give the backend a chance to catch up. --- routing/chainview/interface_test.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/routing/chainview/interface_test.go b/routing/chainview/interface_test.go index 5ab054df..bb8ef1af 100644 --- a/routing/chainview/interface_test.go +++ b/routing/chainview/interface_test.go @@ -570,6 +570,13 @@ func testFilterBlockDisconnected(node *rpctest.Harness, newBlocks := reorgView.FilteredBlocks() disconnectedBlocks := reorgView.DisconnectedBlocks() + // If this the neutrino backend, then we'll give it some time to catch + // up, as it's a bit slower to consume new blocks compared to the RPC + // backends. + if _, ok := reorgView.(*CfFilteredChainView); ok { + time.Sleep(time.Second * 3) + } + _, oldHeight, err := reorgNode.Node.GetBestBlock() if err != nil { t.Fatalf("unable to get current height: %v", err) @@ -599,7 +606,8 @@ func testFilterBlockDisconnected(node *rpctest.Harness, case block := <-newBlocks: if i < oldHeight { t.Fatalf("did not expect to get new block "+ - "in iteration %d", i) + "in iteration %d, old height: %v", i, + oldHeight) } expectedHeight := uint32(i - oldHeight + 1) if block.Height != expectedHeight { From 40806532c7541b04b7db668cb88fce2345975e2b Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 31 Jul 2018 19:33:57 -0700 Subject: [PATCH 39/41] funding: fix import paths for test --- fundingmanager_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/fundingmanager_test.go b/fundingmanager_test.go index a019f272..deb08f0f 100644 --- a/fundingmanager_test.go +++ b/fundingmanager_test.go @@ -29,10 +29,6 @@ import ( "github.com/lightningnetwork/lnd/lnrpc" "github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwire" - - "github.com/btcsuite/btcd/btcec" - "github.com/btcsuite/btcd/wire" - "github.com/btcsuite/btcutil" ) const ( From f7d9b8e6b0b5de84f90c72c28bd1abfd81307147 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 31 Jul 2018 20:29:39 -0700 Subject: [PATCH 40/41] lnwallet: increase timeout in waitForWalletSync for new neutrino re-org logic --- lnwallet/interface_test.go | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lnwallet/interface_test.go b/lnwallet/interface_test.go index 4d1a956a..2aa37c8b 100644 --- a/lnwallet/interface_test.go +++ b/lnwallet/interface_test.go @@ -1738,7 +1738,7 @@ func testReorgWalletBalance(r *rpctest.Harness, w *lnwallet.LightningWallet, t.Fatalf("unable to synchronize mining nodes: %v", err) } - // Step 3: Do a set of reorgs by disconecting the two miners, mining + // Step 3: Do a set of reorgs by disconnecting 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++ { @@ -1918,19 +1918,20 @@ func waitForMempoolTx(r *rpctest.Harness, txid *chainhash.Hash) error { } func waitForWalletSync(r *rpctest.Harness, w *lnwallet.LightningWallet) error { - var synced bool - var err error - var bestHash, knownHash *chainhash.Hash - var bestHeight, knownHeight int32 - timeout := time.After(10 * time.Second) + var ( + synced bool + err error + bestHash, knownHash *chainhash.Hash + bestHeight, knownHeight int32 + ) + timeout := time.After(30 * time.Second) for !synced { // Do a short wait select { case <-timeout: - return fmt.Errorf("timeout after 10s") - default: + return fmt.Errorf("timeout after 30s") + case <-time.Tick(50 * time.Millisecond): } - time.Sleep(100 * time.Millisecond) // Check whether the chain source of the wallet is caught up to // the harness it's supposed to be catching up to. From b1a775b29e35b1e7ca997bf3cb8245779f934d11 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 31 Jul 2018 21:28:27 -0700 Subject: [PATCH 41/41] chainntnfs/neutrinonotify: update async conf dispatch to use scripts --- chainntnfs/neutrinonotify/neutrino.go | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/chainntnfs/neutrinonotify/neutrino.go b/chainntnfs/neutrinonotify/neutrino.go index 17014fe9..f612ef1c 100644 --- a/chainntnfs/neutrinonotify/neutrino.go +++ b/chainntnfs/neutrinonotify/neutrino.go @@ -10,6 +10,7 @@ import ( "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/rpcclient" + "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcutil/gcs/builder" @@ -317,8 +318,19 @@ func (n *NeutrinoNotifier) notificationDispatcher() { defer n.wg.Done() confDetails, err := n.historicalConfDetails( - msg.TxID, currentHeight, - msg.heightHint, + msg.TxID, msg.pkScript, currentHeight, msg.heightHint, + ) + if err != nil { + chainntnfs.Log.Error(err) + } + + // We'll map the script into an address + // type so we can instruct neutrino to + // match if the transaction containing + // the script is found in a block. + params := n.p2pNode.ChainParams() + _, addrs, _, err := txscript.ExtractPkScriptAddrs( + msg.pkScript, ¶ms, ) if err != nil { chainntnfs.Log.Error(err)