From 8c0a151cecb04d4fba3670a9acc17ec9f3794272 Mon Sep 17 00:00:00 2001 From: Olaoluwa Osuntokun Date: Tue, 17 Jul 2018 19:17:19 -0700 Subject: [PATCH] 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