Merge pull request #1839 from cfromknecht/assume-channel-valid

routing: Assume channel valid
This commit is contained in:
Olaoluwa Osuntokun 2018-09-04 20:50:06 -07:00 committed by GitHub
commit 309e656a97
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 76 additions and 25 deletions

@ -130,13 +130,13 @@ btcd: $(GLIDE_BIN) $(BTCD_DIR)
build: build:
@$(call print, "Building debug lnd and lncli.") @$(call print, "Building debug lnd and lncli.")
$(GOBUILD) -tags=$(TEST_TAGS) -o lnd-debug $(LDFLAGS) $(PKG) $(GOBUILD) -tags="$(TEST_TAGS) ${tags}" -o lnd-debug $(LDFLAGS) $(PKG)
$(GOBUILD) -tags=$(TEST_TAGS) -o lncli-debug $(LDFLAGS) $(PKG)/cmd/lncli $(GOBUILD) -tags="$(TEST_TAGS) ${tags}" -o lncli-debug $(LDFLAGS) $(PKG)/cmd/lncli
install: install:
@$(call print, "Installing lnd and lncli.") @$(call print, "Installing lnd and lncli.")
go install -v $(LDFLAGS) $(PKG) go install -v -tags="${tags}" $(LDFLAGS) $(PKG)
go install -v $(LDFLAGS) $(PKG)/cmd/lncli go install -v -tags="${tags}" $(LDFLAGS) $(PKG)/cmd/lncli
scratch: dep build scratch: dep build

@ -24,6 +24,7 @@ import (
"github.com/lightningnetwork/lnd/htlcswitch/hodl" "github.com/lightningnetwork/lnd/htlcswitch/hodl"
"github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lncfg"
"github.com/lightningnetwork/lnd/lnwire" "github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
"github.com/lightningnetwork/lnd/tor" "github.com/lightningnetwork/lnd/tor"
) )
@ -234,6 +235,8 @@ type config struct {
NoChanUpdates bool `long:"nochanupdates" description:"If specified, lnd will not request real-time channel updates from connected peers. This option should be used by routing nodes to save bandwidth."` NoChanUpdates bool `long:"nochanupdates" description:"If specified, lnd will not request real-time channel updates from connected peers. This option should be used by routing nodes to save bandwidth."`
net tor.Net net tor.Net
Routing *routing.Conf `group:"routing" namespace:"routing"`
} }
// loadConfig initializes and parses the config using a config file and command // loadConfig initializes and parses the config using a config file and command
@ -692,6 +695,7 @@ func loadConfig() (*config, error) {
} }
case "neutrino": case "neutrino":
// No need to get RPC parameters. // No need to get RPC parameters.
default: default:
str := "%s: only btcd, bitcoind, and neutrino mode " + str := "%s: only btcd, bitcoind, and neutrino mode " +
"supported for bitcoin at this time" "supported for bitcoin at this time"

12
routing/conf.go Normal file

@ -0,0 +1,12 @@
// +build !experimental
package routing
// Conf provides the command line routing configuration. There are no fields in
// the production build so that this section is hidden by default.
type Conf struct{}
// UseAssumeChannelValid always returns false when not in experimental builds.
func (c *Conf) UseAssumeChannelValid() bool {
return false
}

@ -0,0 +1,14 @@
// +build experimental
package routing
// Conf exposes the experimental command line routing configurations.
type Conf struct {
AssumeChannelValid bool `long:"assumechanvalid" description:"Skip checking channel spentness during graph validation. (default: false)"`
}
// UseAssumeChannelValid returns true if the router should skip checking for
// spentness when processing channel updates and announcements.
func (c *Conf) UseAssumeChannelValid() bool {
return c.AssumeChannelValid
}

@ -177,6 +177,12 @@ type Config struct {
// date knowledge of the available bandwidth of the link should be // date knowledge of the available bandwidth of the link should be
// returned. // returned.
QueryBandwidth func(edge *channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi QueryBandwidth func(edge *channeldb.ChannelEdgeInfo) lnwire.MilliSatoshi
// AssumeChannelValid toggles whether or not the router will check for
// spentness of channel outpoints. For neutrino, this saves long rescans
// from blocking initial usage of the wallet. This should only be
// enabled on testnet.
AssumeChannelValid bool
} }
// routeTuple is an entry within the ChannelRouter's route cache. We cache // routeTuple is an entry within the ChannelRouter's route cache. We cache
@ -966,7 +972,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, fundingTxOut, 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{}{}
@ -990,20 +996,28 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
return err return err
} }
// Now that we have the funding outpoint of the channel, ensure var chanUtxo *wire.TxOut
// that it hasn't yet been spent. If so, then this channel has if r.cfg.AssumeChannelValid {
// been closed so we'll ignore it. // If AssumeChannelValid is present, we'll just use the
chanUtxo, err := r.cfg.Chain.GetUtxo( // txout returned from fetchChanPoint.
fundingPoint, fundingPkScript, channelID.BlockHeight, chanUtxo = fundingTxOut
} else {
// 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, fundingPkScript,
channelID.BlockHeight,
) )
if err != nil { if err != nil {
r.rejectMtx.Lock() r.rejectMtx.Lock()
r.rejectCache[msg.ChannelID] = struct{}{} r.rejectCache[msg.ChannelID] = struct{}{}
r.rejectMtx.Unlock() r.rejectMtx.Unlock()
return errors.Errorf("unable to fetch utxo for "+ return errors.Errorf("unable to fetch utxo "+
"chan_id=%v, chan_point=%v: %v", msg.ChannelID, "for chan_id=%v, chan_point=%v: %v",
fundingPoint, err) msg.ChannelID, fundingPoint, err)
}
} }
// By checking the equality of witness pkscripts we checks that // By checking the equality of witness pkscripts we checks that
@ -1108,11 +1122,11 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
} }
} }
if !exists { if !exists && !r.cfg.AssumeChannelValid {
// 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, fundingPkScript, err := r.fetchChanPoint( chanPoint, fundingTxOut, err := r.fetchChanPoint(
&channelID, &channelID,
) )
if err != nil { if err != nil {
@ -1125,7 +1139,8 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
msg.ChannelID, err) msg.ChannelID, err)
} }
_, err = r.cfg.Chain.GetUtxo( _, err = r.cfg.Chain.GetUtxo(
chanPoint, fundingPkScript, channelID.BlockHeight, chanPoint, fundingTxOut.PkScript,
channelID.BlockHeight,
) )
if err != nil { if err != nil {
r.rejectMtx.Lock() r.rejectMtx.Lock()
@ -1171,7 +1186,9 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error {
// //
// 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, []byte, error) { func (r *ChannelRouter) fetchChanPoint(
chanID *lnwire.ShortChannelID) (*wire.OutPoint, *wire.TxOut, 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)
@ -1195,12 +1212,15 @@ func (r *ChannelRouter) fetchChanPoint(chanID *lnwire.ShortChannelID) (*wire.Out
} }
// Finally once we have the block itself, we seek to the targeted // Finally once we have the block itself, we seek to the targeted
// transaction index to obtain the funding output and txid. // transaction index to obtain the funding output and txout.
fundingTx := fundingBlock.Transactions[chanID.TxIndex] fundingTx := fundingBlock.Transactions[chanID.TxIndex]
return &wire.OutPoint{ outPoint := &wire.OutPoint{
Hash: fundingTx.TxHash(), Hash: fundingTx.TxHash(),
Index: uint32(chanID.TxPosition), Index: uint32(chanID.TxPosition),
}, fundingTx.TxOut[chanID.TxPosition].PkScript, nil }
txOut := fundingTx.TxOut[chanID.TxPosition]
return outPoint, txOut, nil
} }
// routingMsg couples a routing related routing topology update to the // routingMsg couples a routing related routing topology update to the

@ -549,6 +549,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl,
// for the available bandwidth for the link. // for the available bandwidth for the link.
return link.Bandwidth() return link.Bandwidth()
}, },
AssumeChannelValid: cfg.Routing.UseAssumeChannelValid(),
}) })
if err != nil { if err != nil {
return nil, fmt.Errorf("can't create router: %v", err) return nil, fmt.Errorf("can't create router: %v", err)