routing: update UTXO verification to use new interface, graph pruning too

This commit is contained in:
Olaoluwa Osuntokun 2018-07-17 19:17:19 -07:00
parent fb98f59407
commit 8c0a151cec
No known key found for this signature in database
GPG Key ID: 964EA263DD637C21
2 changed files with 42 additions and 32 deletions

@ -10,15 +10,15 @@ import (
prand "math/rand" 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/go-errors/errors"
"github.com/lightningnetwork/lnd/channeldb" "github.com/lightningnetwork/lnd/channeldb"
"github.com/lightningnetwork/lnd/lnwallet" "github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing/chainview" "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 ( var (
@ -176,7 +176,7 @@ func (m *mockChain) addUtxo(op wire.OutPoint, out *wire.TxOut) {
m.utxos[op] = *out m.utxos[op] = *out
m.Unlock() 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() m.RLock()
defer m.RUnlock() defer m.RUnlock()
@ -242,12 +242,12 @@ func (m *mockChainView) Reset() {
m.staleBlocks = make(chan *chainview.FilteredBlock, 10) 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() m.Lock()
defer m.Unlock() defer m.Unlock()
for _, op := range ops { for _, op := range ops {
m.filter[op] = struct{}{} m.filter[op.OutPoint] = struct{}{}
} }
return nil return nil

@ -967,7 +967,7 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
// to obtain the full funding outpoint that's encoded within // to obtain the full funding outpoint that's encoded within
// the channel ID. // the channel ID.
channelID := lnwire.NewShortChanIDFromInt(msg.ChannelID) channelID := lnwire.NewShortChanIDFromInt(msg.ChannelID)
fundingPoint, err := r.fetchChanPoint(&channelID) fundingPoint, _, err := r.fetchChanPoint(&channelID)
if err != nil { if err != nil {
r.rejectMtx.Lock() r.rejectMtx.Lock()
r.rejectCache[msg.ChannelID] = struct{}{} r.rejectCache[msg.ChannelID] = struct{}{}
@ -977,11 +977,25 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
"chan_id=%v: %v", msg.ChannelID, err) "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 // Now that we have the funding outpoint of the channel, ensure
// that it hasn't yet been spent. If so, then this channel has // that it hasn't yet been spent. If so, then this channel has
// been closed so we'll ignore it. // been closed so we'll ignore it.
chanUtxo, err := r.cfg.Chain.GetUtxo( chanUtxo, err := r.cfg.Chain.GetUtxo(
fundingPoint, channelID.BlockHeight, fundingPoint, fundingPkScript, channelID.BlockHeight,
) )
if err != nil { if err != nil {
r.rejectMtx.Lock() r.rejectMtx.Lock()
@ -993,26 +1007,14 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
fundingPoint, err) 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 // By checking the equality of witness pkscripts we checks that
// funding witness script is multisignature lock which contains // funding witness script is multisignature lock which contains
// both local and remote public keys which was declared in // both local and remote public keys which was declared in
// channel edge and also that the announced channel value is // channel edge and also that the announced channel value is
// right. // right.
if !bytes.Equal(witnessOutput.PkScript, chanUtxo.PkScript) { if !bytes.Equal(fundingPkScript, chanUtxo.PkScript) {
return errors.Errorf("pkScript mismatch: expected %x, "+ 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 // 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 // update the current UTXO filter within our active
// FilteredChainView so we are notified if/when this channel is // FilteredChainView so we are notified if/when this channel is
// closed. // closed.
filterUpdate := []wire.OutPoint{*fundingPoint} filterUpdate := []channeldb.EdgePoint{
{
FundingPkScript: fundingPkScript,
OutPoint: *fundingPoint,
},
}
err = r.cfg.ChainView.UpdateFilter( err = r.cfg.ChainView.UpdateFilter(
filterUpdate, atomic.LoadUint32(&r.bestHeight), 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 // Before we can update the channel information, we'll
// ensure that the target channel is still open by // ensure that the target channel is still open by
// querying the utxo-set for its existence. // querying the utxo-set for its existence.
chanPoint, err := r.fetchChanPoint(&channelID) chanPoint, fundingPkScript, err := r.fetchChanPoint(
&channelID,
)
if err != nil { if err != nil {
r.rejectMtx.Lock() r.rejectMtx.Lock()
r.rejectCache[msg.ChannelID] = struct{}{} r.rejectCache[msg.ChannelID] = struct{}{}
@ -1116,7 +1125,7 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
msg.ChannelID, err) msg.ChannelID, err)
} }
_, err = r.cfg.Chain.GetUtxo( _, err = r.cfg.Chain.GetUtxo(
chanPoint, channelID.BlockHeight, chanPoint, fundingPkScript, channelID.BlockHeight,
) )
if err != nil { if err != nil {
r.rejectMtx.Lock() r.rejectMtx.Lock()
@ -1157,21 +1166,22 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
} }
// fetchChanPoint retrieves the original outpoint which is encoded within the // 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 // TODO(roasbeef): replace with call to GetBlockTransaction? (would allow to
// later use getblocktxn) // 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 // First fetch the block hash by the block number encoded, then use
// that hash to fetch the block itself. // that hash to fetch the block itself.
blockNum := int64(chanID.BlockHeight) blockNum := int64(chanID.BlockHeight)
blockHash, err := r.cfg.Chain.GetBlockHash(blockNum) blockHash, err := r.cfg.Chain.GetBlockHash(blockNum)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
fundingBlock, err := r.cfg.Chain.GetBlock(blockHash) fundingBlock, err := r.cfg.Chain.GetBlock(blockHash)
if err != nil { if err != nil {
return nil, err return nil, nil, err
} }
// As a sanity check, ensure that the advertised transaction index is // 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. // block.
numTxns := uint32(len(fundingBlock.Transactions)) numTxns := uint32(len(fundingBlock.Transactions))
if chanID.TxIndex > numTxns-1 { 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, "(max_index=%v), network_chan_id=%v\n", chanID.TxIndex,
numTxns-1, spew.Sdump(chanID)) numTxns-1, spew.Sdump(chanID))
} }
@ -1190,7 +1200,7 @@ func (r *ChannelRouter) fetchChanPoint(chanID *lnwire.ShortChannelID) (*wire.Out
return &wire.OutPoint{ return &wire.OutPoint{
Hash: fundingTx.TxHash(), Hash: fundingTx.TxHash(),
Index: uint32(chanID.TxPosition), Index: uint32(chanID.TxPosition),
}, nil }, fundingTx.TxOut[chanID.TxPosition].PkScript, nil
} }
// routingMsg couples a routing related routing topology update to the // routingMsg couples a routing related routing topology update to the