diff --git a/docs/INSTALL.md b/docs/INSTALL.md index b2d695ab..9f612765 100644 --- a/docs/INSTALL.md +++ b/docs/INSTALL.md @@ -99,7 +99,7 @@ make check ### Installing btcd -When using the `btcd` backend, `lnd` currently requires the +If one wishes to use the `btcd` backend, `lnd` currently requires the [roasbeef](https://github.com/roasbeef/btcd) fork of `btcd` due to neutrino additions that are not yet available in the master branch. To install, run the following commands: @@ -114,11 +114,13 @@ make btcd Running the following command will create `rpc.cert` and default `btcd.conf`. ``` -btcd --testnet --txindex --rpcuser=REPLACEME --rpcpass=REPLACEME +btcd --testnet --rpcuser=REPLACEME --rpcpass=REPLACEME ``` If you want to use `lnd` on testnet, `btcd` needs to first fully sync the testnet blockchain. Depending on your hardware, this may take up to a few -hours. +hours. Note that adding `--txindex` is optional, as it will take longer to sync +the node, but then `lnd` will generally operate faster as it can hit the index +directly, rather than scanning blocks or BIP 158 filters for relevant items. (NOTE: It may take several minutes to find segwit-enabled peers.) @@ -202,14 +204,15 @@ The configuration for bitcoind and litecoind are nearly identical, the following steps can be mirrored with loss of generality to enable a litecoind backend. Setup will be described in regards to `bitcoind`, but note that `lnd` uses a distinct `litecoin.node=litecoind` argument and analogous subconfigurations -prefixed by `litecoind`. +prefixed by `litecoind`. Note that adding `--txindex` is optional, as it will +take longer to sync the node, but then `lnd` will generally operate faster as +it can hit the index directly, rather than scanning blocks or BIP 158 filters +for relevant items. To configure your bitcoind backend for use with lnd, first complete and verify the following: -- The `bitcoind` instance must be configured with `--txindex` just like `btcd` - above -- Additionally, since `lnd` uses +- Since `lnd` uses [ZeroMQ](https://github.com/bitcoin/bitcoin/blob/master/doc/zmq.md) to interface with `bitcoind`, *your `bitcoind` installation must be compiled with ZMQ*. Note that if you installed `bitcoind` from source and ZMQ was not present, @@ -228,7 +231,6 @@ the following: Here's a sample `bitcoin.conf` for use with lnd: ``` testnet=1 -txindex=1 server=1 daemon=1 zmqpubrawblock=tcp://127.0.0.1:28332 diff --git a/htlcswitch/link.go b/htlcswitch/link.go index 03ee9d5f..9a933f04 100644 --- a/htlcswitch/link.go +++ b/htlcswitch/link.go @@ -94,7 +94,6 @@ type ForwardingPolicy struct { func ExpectedFee(f ForwardingPolicy, htlcAmt lnwire.MilliSatoshi) lnwire.MilliSatoshi { - // TODO(roasbeef): write some basic table driven tests return f.BaseFee + (htlcAmt*f.FeeRate)/1000000 } diff --git a/htlcswitch/link_test.go b/htlcswitch/link_test.go index d7609012..c6b2819f 100644 --- a/htlcswitch/link_test.go +++ b/htlcswitch/link_test.go @@ -4473,3 +4473,57 @@ func TestChannelLinkFail(t *testing.T) { cleanUp() } } + +// TestExpectedFee tests calculation of ExpectedFee returns expected fee, given +// a baseFee, a feeRate, and an htlc amount. +func TestExpectedFee(t *testing.T) { + testCases := []struct { + baseFee lnwire.MilliSatoshi + feeRate lnwire.MilliSatoshi + htlcAmt lnwire.MilliSatoshi + expected lnwire.MilliSatoshi + }{ + { + lnwire.MilliSatoshi(0), + lnwire.MilliSatoshi(0), + lnwire.MilliSatoshi(0), + lnwire.MilliSatoshi(0), + }, + { + lnwire.MilliSatoshi(0), + lnwire.MilliSatoshi(1), + lnwire.MilliSatoshi(999999), + lnwire.MilliSatoshi(0), + }, + { + lnwire.MilliSatoshi(0), + lnwire.MilliSatoshi(1), + lnwire.MilliSatoshi(1000000), + lnwire.MilliSatoshi(1), + }, + { + lnwire.MilliSatoshi(0), + lnwire.MilliSatoshi(1), + lnwire.MilliSatoshi(1000001), + lnwire.MilliSatoshi(1), + }, + { + lnwire.MilliSatoshi(1), + lnwire.MilliSatoshi(1), + lnwire.MilliSatoshi(1000000), + lnwire.MilliSatoshi(2), + }, + } + + for _, test := range testCases { + f := ForwardingPolicy{ + BaseFee: test.baseFee, + FeeRate: test.feeRate, + } + fee := ExpectedFee(f, test.htlcAmt) + if fee != test.expected { + t.Errorf("expected fee to be (%v), instead got (%v)", test.expected, + fee) + } + } +} diff --git a/rpcserver.go b/rpcserver.go index 3cac313c..5be59000 100644 --- a/rpcserver.go +++ b/rpcserver.go @@ -9,6 +9,7 @@ import ( "fmt" "io" "math" + "sort" "strings" "time" @@ -1137,8 +1138,9 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest, // the htlc switch which will handle the negotiation and // broadcast details. feePerKw := feeRate.FeePerKWeight() - updateChan, errChan = r.server.htlcSwitch.CloseLink(chanPoint, - htlcswitch.CloseRegular, feePerKw) + updateChan, errChan = r.server.htlcSwitch.CloseLink( + chanPoint, htlcswitch.CloseRegular, feePerKw, + ) } out: for { @@ -1583,14 +1585,14 @@ func (r *rpcServer) PendingChannels(ctx context.Context, return resp, nil } -// ClosedChannels returns a list of all the channels have been closed. +// ClosedChannels returns a list of all the channels have been closed. // This does not include channels that are still in the process of closing. func (r *rpcServer) ClosedChannels(ctx context.Context, - in *lnrpc.ClosedChannelsRequest) (*lnrpc.ClosedChannelsResponse, + in *lnrpc.ClosedChannelsRequest) (*lnrpc.ClosedChannelsResponse, error) { // Show all channels when no filter flags are set. - filterResults := in.Cooperative || in.LocalForce || + filterResults := in.Cooperative || in.LocalForce || in.RemoteForce || in.Breach || in.FundingCanceled resp := &lnrpc.ClosedChannelsResponse{} @@ -1600,6 +1602,13 @@ func (r *rpcServer) ClosedChannels(ctx context.Context, return nil, err } + // In order to make the response easier to parse for clients, we'll + // sort the set of closed channels by their closing height before + // serializing the proto response. + sort.Slice(dbChannels, func(i, j int) bool { + return dbChannels[i].CloseHeight < dbChannels[j].CloseHeight + }) + for _, dbChannel := range dbChannels { if dbChannel.IsPending { continue diff --git a/utxonursery.go b/utxonursery.go index 2e2d1f2e..59f2d25b 100644 --- a/utxonursery.go +++ b/utxonursery.go @@ -736,7 +736,7 @@ func (u *utxoNursery) regraduateClass(classHeight uint32) error { utxnLog.Infof("Re-registering confirmation for kindergarten "+ "sweep transaction at height=%d ", classHeight) - err = u.registerSweepConf(finalTx, kgtnOutputs, classHeight) + err = u.sweepMatureOutputs(classHeight, finalTx, kgtnOutputs) if err != nil { utxnLog.Errorf("Failed to re-register for kindergarten "+ "sweep transaction at height=%d: %v", @@ -1124,7 +1124,8 @@ func (u *utxoNursery) sweepMatureOutputs(classHeight uint32, finalTx *wire.MsgTx // With the sweep transaction fully signed, broadcast the transaction // to the network. Additionally, we can stop tracking these outputs as // they've just been swept. - if err := u.cfg.PublishTransaction(finalTx); err != nil { + err := u.cfg.PublishTransaction(finalTx) + if err != nil && err != lnwallet.ErrDoubleSpend { utxnLog.Errorf("unable to broadcast sweep tx: %v, %v", err, spew.Sdump(finalTx)) return err @@ -1230,7 +1231,8 @@ func (u *utxoNursery) sweepCribOutput(classHeight uint32, baby *babyOutput) erro // We'll now broadcast the HTLC transaction, then wait for it to be // confirmed before transitioning it to kindergarten. - if err := u.cfg.PublishTransaction(baby.timeoutTx); err != nil { + err := u.cfg.PublishTransaction(baby.timeoutTx) + if err != nil && err != lnwallet.ErrDoubleSpend { utxnLog.Errorf("Unable to broadcast baby tx: "+ "%v, %v", err, spew.Sdump(baby.timeoutTx)) return err