diff --git a/Makefile b/Makefile index 054249d9..c4ee66aa 100644 --- a/Makefile +++ b/Makefile @@ -130,13 +130,13 @@ btcd: $(GLIDE_BIN) $(BTCD_DIR) build: @$(call print, "Building debug lnd and lncli.") - $(GOBUILD) -tags=$(TEST_TAGS) -o lnd-debug $(LDFLAGS) $(PKG) - $(GOBUILD) -tags=$(TEST_TAGS) -o lncli-debug $(LDFLAGS) $(PKG)/cmd/lncli + $(GOBUILD) -tags="$(TEST_TAGS) ${tags}" -o lnd-debug $(LDFLAGS) $(PKG) + $(GOBUILD) -tags="$(TEST_TAGS) ${tags}" -o lncli-debug $(LDFLAGS) $(PKG)/cmd/lncli install: @$(call print, "Installing lnd and lncli.") - go install -v $(LDFLAGS) $(PKG) - go install -v $(LDFLAGS) $(PKG)/cmd/lncli + go install -v -tags="${tags}" $(LDFLAGS) $(PKG) + go install -v -tags="${tags}" $(LDFLAGS) $(PKG)/cmd/lncli scratch: dep build diff --git a/config.go b/config.go index 32a5403d..4eadcf80 100644 --- a/config.go +++ b/config.go @@ -24,6 +24,7 @@ import ( "github.com/lightningnetwork/lnd/htlcswitch/hodl" "github.com/lightningnetwork/lnd/lncfg" "github.com/lightningnetwork/lnd/lnwire" + "github.com/lightningnetwork/lnd/routing" "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."` net tor.Net + + Routing *routing.Conf `group:"routing" namespace:"routing"` } // loadConfig initializes and parses the config using a config file and command @@ -692,6 +695,7 @@ func loadConfig() (*config, error) { } case "neutrino": // No need to get RPC parameters. + default: str := "%s: only btcd, bitcoind, and neutrino mode " + "supported for bitcoin at this time" diff --git a/routing/conf.go b/routing/conf.go new file mode 100644 index 00000000..ccce7c38 --- /dev/null +++ b/routing/conf.go @@ -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 +} diff --git a/routing/conf_experimental.go b/routing/conf_experimental.go new file mode 100644 index 00000000..5c11c9cd --- /dev/null +++ b/routing/conf_experimental.go @@ -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 +} diff --git a/routing/router.go b/routing/router.go index e275843a..075dcdea 100644 --- a/routing/router.go +++ b/routing/router.go @@ -177,6 +177,12 @@ type Config struct { // date knowledge of the available bandwidth of the link should be // returned. 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 @@ -966,7 +972,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, fundingTxOut, err := r.fetchChanPoint(&channelID) if err != nil { r.rejectMtx.Lock() r.rejectCache[msg.ChannelID] = struct{}{} @@ -990,20 +996,28 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { 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, fundingPkScript, channelID.BlockHeight, - ) - if err != nil { - r.rejectMtx.Lock() - r.rejectCache[msg.ChannelID] = struct{}{} - r.rejectMtx.Unlock() + var chanUtxo *wire.TxOut + if r.cfg.AssumeChannelValid { + // If AssumeChannelValid is present, we'll just use the + // txout returned from fetchChanPoint. + 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 { + r.rejectMtx.Lock() + r.rejectCache[msg.ChannelID] = struct{}{} + r.rejectMtx.Unlock() - return errors.Errorf("unable to fetch utxo for "+ - "chan_id=%v, chan_point=%v: %v", msg.ChannelID, - fundingPoint, err) + return errors.Errorf("unable to fetch utxo "+ + "for chan_id=%v, chan_point=%v: %v", + msg.ChannelID, fundingPoint, err) + } } // 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 // ensure that the target channel is still open by // querying the utxo-set for its existence. - chanPoint, fundingPkScript, err := r.fetchChanPoint( + chanPoint, fundingTxOut, err := r.fetchChanPoint( &channelID, ) if err != nil { @@ -1125,7 +1139,8 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { msg.ChannelID, err) } _, err = r.cfg.Chain.GetUtxo( - chanPoint, fundingPkScript, channelID.BlockHeight, + chanPoint, fundingTxOut.PkScript, + channelID.BlockHeight, ) if err != nil { r.rejectMtx.Lock() @@ -1171,7 +1186,9 @@ func (r *ChannelRouter) processUpdate(msg interface{}) error { // // TODO(roasbeef): replace with call to GetBlockTransaction? (would allow to // 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 // that hash to fetch the block itself. 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 - // transaction index to obtain the funding output and txid. + // transaction index to obtain the funding output and txout. fundingTx := fundingBlock.Transactions[chanID.TxIndex] - return &wire.OutPoint{ + outPoint := &wire.OutPoint{ Hash: fundingTx.TxHash(), 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 diff --git a/server.go b/server.go index 213f9312..598cd7c3 100644 --- a/server.go +++ b/server.go @@ -549,6 +549,7 @@ func newServer(listenAddrs []net.Addr, chanDB *channeldb.DB, cc *chainControl, // for the available bandwidth for the link. return link.Bandwidth() }, + AssumeChannelValid: cfg.Routing.UseAssumeChannelValid(), }) if err != nil { return nil, fmt.Errorf("can't create router: %v", err)