Merge pull request #762 from halseth/fee-estimator

Add types for representing fee rates
This commit is contained in:
Olaoluwa Osuntokun 2018-02-26 17:22:50 -08:00 committed by GitHub
commit f7ec490f44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 477 additions and 412 deletions

View File

@ -1050,13 +1050,13 @@ func (b *breachArbiter) createJusticeTx(
spendableOutputs = append(spendableOutputs, input)
}
txWeight := uint64(weightEstimate.Weight())
return b.sweepSpendableOutputsTxn(txWeight, spendableOutputs...)
txVSize := int64(weightEstimate.VSize())
return b.sweepSpendableOutputsTxn(txVSize, spendableOutputs...)
}
// sweepSpendableOutputsTxn creates a signed transaction from a sequence of
// spendable outputs by sweeping the funds into a single p2wkh output.
func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight uint64,
func (b *breachArbiter) sweepSpendableOutputsTxn(txVSize int64,
inputs ...SpendableOutput) (*wire.MsgTx, error) {
// First, we obtain a new public key script from the wallet which we'll
@ -1076,11 +1076,11 @@ func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight uint64,
// We'll actually attempt to target inclusion within the next two
// blocks as we'd like to sweep these funds back into our wallet ASAP.
feePerWeight, err := b.cfg.Estimator.EstimateFeePerWeight(2)
feePerVSize, err := b.cfg.Estimator.EstimateFeePerVSize(2)
if err != nil {
return nil, err
}
txFee := btcutil.Amount(txWeight * uint64(feePerWeight))
txFee := feePerVSize.FeeForVSize(txVSize)
// TODO(roasbeef): already start to siphon their funds into fees
sweepAmt := int64(totalAmt - txFee)

View File

@ -1331,18 +1331,18 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
}
estimator := &lnwallet.StaticFeeEstimator{FeeRate: 50}
feePerWeight, err := estimator.EstimateFeePerWeight(1)
feePerVSize, err := estimator.EstimateFeePerVSize(1)
if err != nil {
return nil, nil, nil, err
}
feePerKw := feePerWeight * 1000
feePerKw := feePerVSize.FeePerKWeight()
// TODO(roasbeef): need to factor in commit fee?
aliceCommit := channeldb.ChannelCommitment{
CommitHeight: 0,
LocalBalance: lnwire.NewMSatFromSatoshis(channelBal),
RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal),
FeePerKw: feePerKw,
FeePerKw: btcutil.Amount(feePerKw),
CommitFee: 8688,
CommitTx: aliceCommitTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
@ -1351,7 +1351,7 @@ func createInitChannels(revocationWindow int) (*lnwallet.LightningChannel, *lnwa
CommitHeight: 0,
LocalBalance: lnwire.NewMSatFromSatoshis(channelBal),
RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal),
FeePerKw: feePerKw,
FeePerKw: btcutil.Amount(feePerKw),
CommitFee: 8688,
CommitTx: bobCommitTx,
CommitSig: bytes.Repeat([]byte{1}, 71),

View File

@ -24,7 +24,6 @@ import (
"github.com/lightningnetwork/lnd/routing/chainview"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/rpcclient"
"github.com/roasbeef/btcutil"
"github.com/roasbeef/btcwallet/chain"
"github.com/roasbeef/btcwallet/walletdb"
)
@ -306,7 +305,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// if we're using bitcoind as a backend, then we can
// use live fee estimates, rather than a statically
// coded value.
fallBackFeeRate := btcutil.Amount(25)
fallBackFeeRate := lnwallet.SatPerVByte(25)
cc.feeEstimator, err = lnwallet.NewBitcoindFeeEstimator(
*rpcConfig, fallBackFeeRate,
)
@ -410,7 +409,7 @@ func newChainControlFromConfig(cfg *config, chanDB *channeldb.DB,
// if we're using btcd as a backend, then we can use
// live fee estimates, rather than a statically coded
// value.
fallBackFeeRate := btcutil.Amount(25)
fallBackFeeRate := lnwallet.SatPerVByte(25)
cc.feeEstimator, err = lnwallet.NewBtcdFeeEstimator(
*rpcConfig, fallBackFeeRate,
)

View File

@ -150,7 +150,7 @@ type channelCloser struct {
// passed configuration, and delivery+fee preference. The final argument should
// only be populated iff, we're the initiator of this closing request.
func newChannelCloser(cfg chanCloseCfg, deliveryScript []byte,
idealFeePerkw btcutil.Amount, negotiationHeight uint32,
idealFeePerKw lnwallet.SatPerKWeight, negotiationHeight uint32,
closeReq *htlcswitch.ChanClose,
closeCtx *contractcourt.CooperativeCloseCtx) *channelCloser {
@ -158,9 +158,7 @@ func newChannelCloser(cfg chanCloseCfg, deliveryScript []byte,
// fee will be starting at for this fee negotiation.
//
// TODO(roasbeef): should factor in minimal commit
idealFeeSat := btcutil.Amount(
cfg.channel.CalcFee(uint64(idealFeePerkw)),
)
idealFeeSat := cfg.channel.CalcFee(idealFeePerKw)
// If this fee is greater than the fee currently present within the
// commitment transaction, then we'll clamp it down to be within the

View File

@ -249,6 +249,10 @@ type ChannelCommitment struct {
// the commitment transaction for the entire duration of the channel's
// lifetime. This field may be updated during normal operation of the
// channel as on-chain conditions change.
//
// TODO(halseth): make this SatPerKWeight. Cannot be done atm because
// this will cause the import cycle lnwallet<->channeldb. Fee
// estimation stuff should be in its own package.
FeePerKw btcutil.Amount
// CommitTx is the latest version of the commitment state, broadcast

View File

@ -482,23 +482,24 @@ func (h *htlcSuccessResolver) Resolve() (ContractResolver, error) {
//
// TODO(roasbeef): signal up if fee would be too large
// to sweep singly, need to batch
satWeight, err := h.FeeEstimator.EstimateFeePerWeight(6)
feePerVSize, err := h.FeeEstimator.EstimateFeePerVSize(6)
if err != nil {
return nil, err
}
log.Debugf("%T(%x): using %v sat/weight to sweep htlc"+
log.Debugf("%T(%x): using %v sat/vbyte to sweep htlc"+
"incoming+remote htlc confirmed", h,
h.payHash[:], int64(satWeight))
h.payHash[:], int64(feePerVSize))
// Using a weight estimator, we'll compute the total
// fee required, and from that the value we'll end up
// with.
totalWeight := (&lnwallet.TxWeightEstimator{}).
totalVSize := (&lnwallet.TxWeightEstimator{}).
AddWitnessInput(lnwallet.OfferedHtlcSuccessWitnessSize).
AddP2WKHOutput().Weight()
totalFees := int64(totalWeight) * int64(satWeight)
sweepAmt := h.htlcResolution.SweepSignDesc.Output.Value - totalFees
AddP2WKHOutput().VSize()
totalFees := feePerVSize.FeeForVSize(int64(totalVSize))
sweepAmt := h.htlcResolution.SweepSignDesc.Output.Value -
int64(totalFees)
// With the fee computation finished, we'll now
// construct the sweep transaction.
@ -1252,19 +1253,19 @@ func (c *commitSweepResolver) Resolve() (ContractResolver, error) {
// First, we'll estimate the total weight so we can compute
// fees properly. We'll use a lax estimate, as this output is
// in no immediate danger.
satWeight, err := c.FeeEstimator.EstimateFeePerWeight(6)
feePerVSize, err := c.FeeEstimator.EstimateFeePerVSize(6)
if err != nil {
return nil, err
}
log.Debugf("%T(%v): using %v sat/weight for sweep tx", c,
c.chanPoint, int64(satWeight))
log.Debugf("%T(%v): using %v sat/vsize for sweep tx", c,
c.chanPoint, int64(feePerVSize))
totalWeight := (&lnwallet.TxWeightEstimator{}).
totalVSize := (&lnwallet.TxWeightEstimator{}).
AddP2PKHInput().
AddP2WKHOutput().Weight()
totalFees := int64(totalWeight) * int64(satWeight)
sweepAmt := signDesc.Output.Value - totalFees
AddP2WKHOutput().VSize()
totalFees := feePerVSize.FeeForVSize(int64(totalVSize))
sweepAmt := signDesc.Output.Value - int64(totalFees)
c.sweepTx = wire.NewMsgTx(2)
c.sweepTx.AddTxIn(&wire.TxIn{

View File

@ -865,7 +865,7 @@ func (f *fundingManager) handleFundingOpen(fmsg *fundingOpenMsg) {
// port with default advertised port
chainHash := chainhash.Hash(msg.ChainHash)
reservation, err := f.cfg.Wallet.InitChannelReservation(amt, 0,
msg.PushAmount, btcutil.Amount(msg.FeePerKiloWeight), 0,
msg.PushAmount, lnwallet.SatPerKWeight(msg.FeePerKiloWeight), 0,
fmsg.peerAddress.IdentityKey, fmsg.peerAddress.Address,
&chainHash, msg.ChannelFlags)
if err != nil {
@ -2349,7 +2349,7 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
// commitment transaction confirmed by the next few blocks (conf target
// of 3). We target the near blocks here to ensure that we'll be able
// to execute a timely unilateral channel closure if needed.
feePerWeight, err := f.cfg.FeeEstimator.EstimateFeePerWeight(3)
feePerVSize, err := f.cfg.FeeEstimator.EstimateFeePerVSize(3)
if err != nil {
msg.err <- err
return
@ -2357,7 +2357,7 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
// The protocol currently operates on the basis of fee-per-kw, so we'll
// multiply the computed sat/weight by 1000 to arrive at fee-per-kw.
commitFeePerKw := feePerWeight * 1000
commitFeePerKw := feePerVSize.FeePerKWeight()
// We set the channel flags to indicate whether we want this channel
// to be announced to the network.
@ -2371,8 +2371,9 @@ func (f *fundingManager) handleInitFundingMsg(msg *initFundingMsg) {
// wallet doesn't have enough funds to commit to this channel, then the
// request will fail, and be aborted.
reservation, err := f.cfg.Wallet.InitChannelReservation(capacity,
localAmt, msg.pushAmt, commitFeePerKw, msg.fundingFeePerWeight,
peerKey, msg.peerAddress.Address.(*net.TCPAddr), &msg.chainHash, channelFlags)
localAmt, msg.pushAmt, commitFeePerKw, msg.fundingFeePerVSize,
peerKey, msg.peerAddress.Address.(*net.TCPAddr),
&msg.chainHash, channelFlags)
if err != nil {
msg.err <- err
return

View File

@ -18,7 +18,6 @@ import (
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcutil"
)
const (
@ -387,16 +386,16 @@ func (l *channelLink) EligibleToForward() bool {
// chain in a timely manner. The returned value is expressed in fee-per-kw, as
// this is the native rate used when computing the fee for commitment
// transactions, and the second-level HTLC transactions.
func (l *channelLink) sampleNetworkFee() (btcutil.Amount, error) {
// We'll first query for the sat/weight recommended to be confirmed
// within 3blocks.
feePerWeight, err := l.cfg.FeeEstimator.EstimateFeePerWeight(3)
func (l *channelLink) sampleNetworkFee() (lnwallet.SatPerKWeight, error) {
// We'll first query for the sat/vbyte recommended to be confirmed
// within 3 blocks.
feePerVSize, err := l.cfg.FeeEstimator.EstimateFeePerVSize(3)
if err != nil {
return 0, err
}
// Once we have this fee rate, we'll convert to sat-per-kw.
feePerKw := feePerWeight * 1000
feePerKw := feePerVSize.FeePerKWeight()
log.Debugf("ChannelLink(%v): sampled fee rate for 3 block conf: %v "+
"sat/kw", l, int64(feePerKw))
@ -407,7 +406,7 @@ func (l *channelLink) sampleNetworkFee() (btcutil.Amount, error) {
// shouldAdjustCommitFee returns true if we should update our commitment fee to
// match that of the network fee. We'll only update our commitment fee if the
// network fee is +/- 10% to our network fee.
func shouldAdjustCommitFee(netFee, chanFee btcutil.Amount) bool {
func shouldAdjustCommitFee(netFee, chanFee lnwallet.SatPerKWeight) bool {
switch {
// If the network fee is greater than the commitment fee, then we'll
// switch to it if it's at least 10% greater than the commit fee.
@ -1148,7 +1147,7 @@ func (l *channelLink) handleUpstreamMsg(msg lnwire.Message) {
case *lnwire.UpdateFee:
// We received fee update from peer. If we are the initiator we
// will fail the channel, if not we will apply the update.
fee := btcutil.Amount(msg.FeePerKw)
fee := lnwallet.SatPerKWeight(msg.FeePerKw)
if err := l.channel.ReceiveUpdateFee(fee); err != nil {
l.fail("error receiving fee update: %v", err)
return
@ -1348,7 +1347,7 @@ func (l *channelLink) HandleChannelUpdate(message lnwire.Message) {
// updateChannelFee updates the commitment fee-per-kw on this channel by
// committing to an update_fee message.
func (l *channelLink) updateChannelFee(feePerKw btcutil.Amount) error {
func (l *channelLink) updateChannelFee(feePerKw lnwallet.SatPerKWeight) error {
log.Infof("ChannelPoint(%v): updating commit fee to %v sat/kw", l,
feePerKw)

View File

@ -1683,13 +1683,13 @@ func TestChannelLinkBandwidthConsistency(t *testing.T) {
estimator := &lnwallet.StaticFeeEstimator{
FeeRate: 24,
}
feePerWeight, err := estimator.EstimateFeePerWeight(1)
feeRate, err := estimator.EstimateFeePerVSize(1)
if err != nil {
t.Fatalf("unable to query fee estimator: %v", err)
}
feePerKw := feePerWeight * 1000
feePerKw := feeRate.FeePerKWeight()
htlcFee := lnwire.NewMSatFromSatoshis(
btcutil.Amount((int64(feePerKw) * lnwallet.HtlcWeight) / 1000),
feePerKw.FeeForWeight(lnwallet.HtlcWeight),
)
// The starting bandwidth of the channel should be exactly the amount
@ -2010,11 +2010,11 @@ func TestChannelLinkBandwidthConsistencyOverflow(t *testing.T) {
estimator := &lnwallet.StaticFeeEstimator{
FeeRate: 24,
}
feePerWeight, err := estimator.EstimateFeePerWeight(1)
feeRate, err := estimator.EstimateFeePerVSize(1)
if err != nil {
t.Fatalf("unable to query fee estimator: %v", err)
}
feePerKw := feePerWeight * 1000
feePerKw := feeRate.FeePerKWeight()
// The starting bandwidth of the channel should be exactly the amount
// that we created the channel between her and Bob.
@ -2077,7 +2077,7 @@ func TestChannelLinkBandwidthConsistencyOverflow(t *testing.T) {
time.Sleep(time.Second * 1)
commitWeight := lnwallet.CommitWeight + lnwallet.HtlcWeight*numHTLCs
htlcFee := lnwire.NewMSatFromSatoshis(
btcutil.Amount((int64(feePerKw) * commitWeight) / 1000),
feePerKw.FeeForWeight(commitWeight),
)
expectedBandwidth = aliceStartingBandwidth - totalHtlcAmt - htlcFee
expectedBandwidth += lnwire.NewMSatFromSatoshis(defaultCommitFee)
@ -2222,13 +2222,13 @@ func TestChannelLinkBandwidthChanReserve(t *testing.T) {
estimator := &lnwallet.StaticFeeEstimator{
FeeRate: 24,
}
feePerWeight, err := estimator.EstimateFeePerWeight(1)
feeRate, err := estimator.EstimateFeePerVSize(1)
if err != nil {
t.Fatalf("unable to query fee estimator: %v", err)
}
feePerKw := feePerWeight * 1000
feePerKw := feeRate.FeePerKWeight()
htlcFee := lnwire.NewMSatFromSatoshis(
btcutil.Amount((int64(feePerKw) * lnwallet.HtlcWeight) / 1000),
feePerKw.FeeForWeight(lnwallet.HtlcWeight),
)
// The starting bandwidth of the channel should be exactly the amount
@ -2595,8 +2595,8 @@ func TestChannelRetransmission(t *testing.T) {
// deviates from our current fee by more 10% or more.
func TestShouldAdjustCommitFee(t *testing.T) {
tests := []struct {
netFee btcutil.Amount
chanFee btcutil.Amount
netFee lnwallet.SatPerKWeight
chanFee lnwallet.SatPerKWeight
shouldAdjust bool
}{
@ -2754,9 +2754,15 @@ func TestChannelLinkUpdateCommitFee(t *testing.T) {
startingFeeRate := channels.aliceToBob.CommitFeeRate()
// Convert starting fee rate to sat/vbyte. This is usually a
// lossy conversion, but since the startingFeeRate is
// 6000 sat/kw in this case, we won't lose precision.
startingFeeRateSatPerVByte := lnwallet.SatPerVByte(
startingFeeRate * 4 / 1000)
// Next, we'll send the first fee rate response to Alice.
select {
case n.feeEstimator.weightFeeIn <- startingFeeRate / 1000:
case n.feeEstimator.byteFeeIn <- startingFeeRateSatPerVByte:
case <-time.After(time.Second * 5):
t.Fatalf("alice didn't query for the new " +
"network fee")
@ -2803,7 +2809,7 @@ func TestChannelLinkUpdateCommitFee(t *testing.T) {
// fee update.
newFeeRate := startingFeeRate * 3
select {
case n.feeEstimator.weightFeeIn <- newFeeRate:
case n.feeEstimator.byteFeeIn <- startingFeeRateSatPerVByte * 3:
case <-time.After(time.Second * 5):
t.Fatalf("alice didn't query for the new " +
"network fee")
@ -2813,19 +2819,15 @@ func TestChannelLinkUpdateCommitFee(t *testing.T) {
// At this point, Alice should've triggered a new fee update that
// increased the fee rate to match the new rate.
//
// We'll scale the new fee rate by 100 as we deal with units of fee
// per-kw.
expectedFeeRate := newFeeRate * 1000
aliceFeeRate = channels.aliceToBob.CommitFeeRate()
bobFeeRate = channels.bobToAlice.CommitFeeRate()
if aliceFeeRate != expectedFeeRate {
if aliceFeeRate != newFeeRate {
t.Fatalf("alice's fee rate didn't change: expected %v, got %v",
expectedFeeRate, aliceFeeRate)
newFeeRate, aliceFeeRate)
}
if bobFeeRate != expectedFeeRate {
if bobFeeRate != newFeeRate {
t.Fatalf("bob's fee rate didn't change: expected %v, got %v",
expectedFeeRate, aliceFeeRate)
newFeeRate, aliceFeeRate)
}
if aliceFeeRate != bobFeeRate {
t.Fatalf("fee rates don't match: expected %v got %v",

View File

@ -24,7 +24,6 @@ import (
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/txscript"
"github.com/roasbeef/btcd/wire"
"github.com/roasbeef/btcutil"
)
type mockPreimageCache struct {
@ -57,13 +56,12 @@ func (m *mockPreimageCache) SubscribeUpdates() *contractcourt.WitnessSubscriptio
}
type mockFeeEstimator struct {
byteFeeIn chan btcutil.Amount
weightFeeIn chan btcutil.Amount
byteFeeIn chan lnwallet.SatPerVByte
quit chan struct{}
}
func (m *mockFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error) {
func (m *mockFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (lnwallet.SatPerVByte, error) {
select {
case feeRate := <-m.byteFeeIn:
return feeRate, nil
@ -72,15 +70,6 @@ func (m *mockFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amount,
}
}
func (m *mockFeeEstimator) EstimateFeePerWeight(numBlocks uint32) (btcutil.Amount, error) {
select {
case feeRate := <-m.weightFeeIn:
return feeRate, nil
case <-m.quit:
return 0, fmt.Errorf("exiting")
}
}
func (m *mockFeeEstimator) Start() error {
return nil
}

View File

@ -81,7 +81,7 @@ type ChanClose struct {
// This value is only utilized if the closure type is CloseRegular.
// This will be the starting offered fee when the fee negotiation
// process for the cooperative closure transaction kicks off.
TargetFeePerKw btcutil.Amount
TargetFeePerKw lnwallet.SatPerKWeight
// Updates is used by request creator to receive the notifications about
// execution of the close channel request.
@ -741,9 +741,9 @@ func (s *Switch) handlePacketForward(packet *htlcPacket) error {
// directing the specified closure type. If the closure type if CloseRegular,
// then the last parameter should be the ideal fee-per-kw that will be used as
// a starting point for close negotiation.
func (s *Switch) CloseLink(chanPoint *wire.OutPoint,
closeType ChannelCloseType,
targetFeePerKw btcutil.Amount) (chan *lnrpc.CloseStatusUpdate, chan error) {
func (s *Switch) CloseLink(chanPoint *wire.OutPoint, closeType ChannelCloseType,
targetFeePerKw lnwallet.SatPerKWeight) (chan *lnrpc.CloseStatusUpdate,
chan error) {
// TODO(roasbeef) abstract out the close updates.
updateChan := make(chan *lnrpc.CloseStatusUpdate, 2)

View File

@ -188,12 +188,12 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
estimator := &lnwallet.StaticFeeEstimator{
FeeRate: 24,
}
feePerWeight, err := estimator.EstimateFeePerWeight(1)
feePerVSize, err := estimator.EstimateFeePerVSize(1)
if err != nil {
return nil, nil, nil, nil, err
}
feePerKw := btcutil.Amount(feePerWeight * 1000)
commitFee := (feePerKw * btcutil.Amount(724)) / 1000
feePerKw := feePerVSize.FeePerKWeight()
commitFee := feePerKw.FeeForWeight(724)
const broadcastHeight = 1
bobAddr := &net.TCPAddr{
@ -211,7 +211,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
LocalBalance: lnwire.NewMSatFromSatoshis(aliceAmount - commitFee),
RemoteBalance: lnwire.NewMSatFromSatoshis(bobAmount),
CommitFee: commitFee,
FeePerKw: feePerKw,
FeePerKw: btcutil.Amount(feePerKw),
CommitTx: aliceCommitTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
}
@ -220,7 +220,7 @@ func createTestChannel(alicePrivKey, bobPrivKey []byte,
LocalBalance: lnwire.NewMSatFromSatoshis(bobAmount),
RemoteBalance: lnwire.NewMSatFromSatoshis(aliceAmount - commitFee),
CommitFee: commitFee,
FeePerKw: feePerKw,
FeePerKw: btcutil.Amount(feePerKw),
CommitTx: bobCommitTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
}
@ -744,9 +744,8 @@ func newThreeHopNetwork(t testing.TB, aliceChannel, firstBobChannel,
decoder := &mockIteratorDecoder{}
feeEstimator := &mockFeeEstimator{
byteFeeIn: make(chan btcutil.Amount),
weightFeeIn: make(chan btcutil.Amount),
quit: make(chan struct{}),
byteFeeIn: make(chan lnwallet.SatPerVByte),
quit: make(chan struct{}),
}
pCache := &mockPreimageCache{

View File

@ -366,7 +366,7 @@ func calcStaticFee(numHTLCs int) btcutil.Amount {
const (
commitWeight = btcutil.Amount(724)
htlcWeight = 172
feePerKw = btcutil.Amount(50/4) * 1000
feePerKw = btcutil.Amount(50 * 1000 / 4)
)
return feePerKw * (commitWeight +
btcutil.Amount(htlcWeight*numHTLCs)) / 1000
@ -4635,8 +4635,9 @@ func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
// amount of payments, between Alice and Bob, at the end of the test
// Alice should send all money from her side to Bob.
ctxt, _ := context.WithTimeout(ctxb, timeout)
channelCapacity := btcutil.Amount(paymentAmt * 2000)
chanPoint := openChannelAndAssert(ctxt, t, net, net.Alice, net.Bob,
paymentAmt*2000, 0)
channelCapacity, 0)
info, err := getChanInfo(net.Alice)
if err != nil {
@ -4645,8 +4646,10 @@ func testAsyncPayments(net *lntest.NetworkHarness, t *harnessTest) {
// Calculate the number of invoices. We will deplete the channel
// all the way down to the channel reserve.
chanReserve := info.LocalBalance / 100
numInvoices := int((info.LocalBalance - chanReserve) / paymentAmt)
chanReserve := channelCapacity / 100
availableBalance := btcutil.Amount(info.LocalBalance) - chanReserve
numInvoices := int(availableBalance / paymentAmt)
bobAmt := int64(numInvoices * paymentAmt)
aliceAmt := info.LocalBalance - bobAmt

View File

@ -316,11 +316,11 @@ func (b *BtcWallet) FetchRootKey() (*btcec.PrivateKey, error) {
//
// This is a part of the WalletController interface.
func (b *BtcWallet) SendOutputs(outputs []*wire.TxOut,
feeSatPerByte btcutil.Amount) (*chainhash.Hash, error) {
feeRate lnwallet.SatPerVByte) (*chainhash.Hash, error) {
// The fee rate is passed in using units of sat/byte, so we'll scale
// The fee rate is passed in using units of sat/vbyte, so we'll scale
// this up to sat/KB as the SendOutputs method requires this unit.
feeSatPerKB := feeSatPerByte * 1024
feeSatPerKB := btcutil.Amount(feeRate * 1000)
return b.wallet.SendOutputs(outputs, defaultAccount, 1, feeSatPerKB)
}

View File

@ -349,7 +349,7 @@ type commitment struct {
// feePerKw is the fee per kw used to calculate this commitment
// transaction's fee.
feePerKw btcutil.Amount
feePerKw SatPerKWeight
// dustLimit is the limit on the commitment transaction such that no
// output values should be below this amount.
@ -532,7 +532,7 @@ func (c *commitment) toDiskCommit(ourCommit bool) *channeldb.ChannelCommitment {
LocalBalance: c.ourBalance,
RemoteBalance: c.theirBalance,
CommitFee: c.fee,
FeePerKw: c.feePerKw,
FeePerKw: btcutil.Amount(c.feePerKw),
CommitTx: c.txn,
CommitSig: c.sig,
Htlcs: make([]channeldb.HTLC, 0, numHtlcs),
@ -596,7 +596,7 @@ func (c *commitment) toDiskCommit(ourCommit bool) *channeldb.ChannelCommitment {
// commitment struct and updateLog. This function is used when we need to
// restore commitment state written do disk back into memory once we need to
// restart a channel session.
func (lc *LightningChannel) diskHtlcToPayDesc(feeRate btcutil.Amount,
func (lc *LightningChannel) diskHtlcToPayDesc(feeRate SatPerKWeight,
commitHeight uint64, isPendingCommit bool, htlc *channeldb.HTLC,
localCommitKeys, remoteCommitKeys *CommitmentKeyRing) (PaymentDescriptor, error) {
@ -673,7 +673,7 @@ func (lc *LightningChannel) diskHtlcToPayDesc(feeRate btcutil.Amount,
// these payment descriptors can be re-inserted into the in-memory updateLog
// for each side.
func (lc *LightningChannel) extractPayDescs(commitHeight uint64,
isPendingCommit bool, feeRate btcutil.Amount,
isPendingCommit bool, feeRate SatPerKWeight,
htlcs []channeldb.HTLC, localCommitKeys *CommitmentKeyRing,
remoteCommitKeys *CommitmentKeyRing) ([]PaymentDescriptor, []PaymentDescriptor, error) {
@ -735,7 +735,7 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal, isPendingCommit bool,
// update log.
incomingHtlcs, outgoingHtlcs, err := lc.extractPayDescs(
diskCommit.CommitHeight, isPendingCommit,
diskCommit.FeePerKw, diskCommit.Htlcs,
SatPerKWeight(diskCommit.FeePerKw), diskCommit.Htlcs,
localCommitKeys, remoteCommitKeys,
)
if err != nil {
@ -756,7 +756,7 @@ func (lc *LightningChannel) diskCommitToMemCommit(isLocal, isPendingCommit bool,
txn: diskCommit.CommitTx,
sig: diskCommit.CommitSig,
fee: diskCommit.CommitFee,
feePerKw: diskCommit.FeePerKw,
feePerKw: SatPerKWeight(diskCommit.FeePerKw),
incomingHTLCs: incomingHtlcs,
outgoingHTLCs: outgoingHtlcs,
}
@ -1181,12 +1181,12 @@ type LightningChannel struct {
// channel initiator) or received (if non-initiator) in an update fee
// message, which haven't yet been included in a commitment. It will
// be nil if no fee update is un-committed.
pendingFeeUpdate *btcutil.Amount
pendingFeeUpdate *SatPerKWeight
// pendingAckFeeUpdate is set to the last committed fee update which is
// not yet ACKed. This value will be nil if a fee update hasn't been
// initiated.
pendingAckFeeUpdate *btcutil.Amount
pendingAckFeeUpdate *SatPerKWeight
// LocalFundingKey is the public key under control by the wallet that
// was used for the 2-of-2 funding output which created this channel.
@ -1355,7 +1355,7 @@ func (lc *LightningChannel) ResetState() {
// if nothing happened.
func (lc *LightningChannel) logUpdateToPayDesc(logUpdate *channeldb.LogUpdate,
remoteUpdateLog *updateLog, commitHeight uint64,
feeRate btcutil.Amount, remoteCommitKeys *CommitmentKeyRing,
feeRate SatPerKWeight, remoteCommitKeys *CommitmentKeyRing,
remoteDustLimit btcutil.Amount) (*PaymentDescriptor, error) {
// Depending on the type of update message we'll map that to a distinct
@ -1626,14 +1626,14 @@ func (lc *LightningChannel) restoreStateLogs(
// entry within the updateLog, so we'll just apply it and move
// on.
if feeUpdate, ok := logUpdate.UpdateMsg.(*lnwire.UpdateFee); ok {
newFeeRate := btcutil.Amount(feeUpdate.FeePerKw)
newFeeRate := SatPerKWeight(feeUpdate.FeePerKw)
lc.pendingAckFeeUpdate = &newFeeRate
continue
}
payDesc, err := lc.logUpdateToPayDesc(
&logUpdate, lc.remoteUpdateLog, pendingHeight,
pendingCommit.FeePerKw, pendingRemoteKeys,
SatPerKWeight(pendingCommit.FeePerKw), pendingRemoteKeys,
lc.channelState.RemoteChanCfg.DustLimit,
)
if err != nil {
@ -1930,14 +1930,14 @@ func NewBreachRetribution(chanState *channeldb.OpenChannel, stateNum uint64,
// htlcTimeoutFee returns the fee in satoshis required for an HTLC timeout
// transaction based on the current fee rate.
func htlcTimeoutFee(feePerKw btcutil.Amount) btcutil.Amount {
return (feePerKw * HtlcTimeoutWeight) / 1000
func htlcTimeoutFee(feePerKw SatPerKWeight) btcutil.Amount {
return feePerKw.FeeForWeight(HtlcTimeoutWeight)
}
// htlcSuccessFee returns the fee in satoshis required for an HTLC success
// transaction based on the current fee rate.
func htlcSuccessFee(feePerKw btcutil.Amount) btcutil.Amount {
return (feePerKw * HtlcSuccessWeight) / 1000
func htlcSuccessFee(feePerKw SatPerKWeight) btcutil.Amount {
return feePerKw.FeeForWeight(HtlcSuccessWeight)
}
// htlcIsDust determines if an HTLC output is dust or not depending on two
@ -1946,8 +1946,8 @@ func htlcSuccessFee(feePerKw btcutil.Amount) btcutil.Amount {
// require as we currently used second-level HTLC transactions as off-chain
// covenants. Depending on the two bits, we'll either be using a timeout or
// success transaction which have different weights.
func htlcIsDust(incoming, ourCommit bool,
feePerKw, htlcAmt, dustLimit btcutil.Amount) bool {
func htlcIsDust(incoming, ourCommit bool, feePerKw SatPerKWeight,
htlcAmt, dustLimit btcutil.Amount) bool {
// First we'll determine the fee required for this HTLC based on if this is
// an incoming HTLC or not, and also on whose commitment transaction it
@ -2135,7 +2135,7 @@ func (lc *LightningChannel) createCommitmentTx(c *commitment,
// With the weight known, we can now calculate the commitment fee,
// ensuring that we account for any dust outputs trimmed above.
commitFee := btcutil.Amount((int64(c.feePerKw) * totalCommitWeight) / 1000)
commitFee := c.feePerKw.FeeForWeight(totalCommitWeight)
// Currently, within the protocol, the initiator always pays the fees.
// So we'll subtract the fee amount from the balance of the current
@ -3049,7 +3049,7 @@ func (lc *LightningChannel) ChanSyncMsg() (*lnwire.ChannelReestablish, error) {
// of the HTLCs will be set to the next commitment height.
func (lc *LightningChannel) computeView(view *htlcView, remoteChain bool,
updateState bool) (lnwire.MilliSatoshi, lnwire.MilliSatoshi, int64,
*htlcView, btcutil.Amount) {
*htlcView, SatPerKWeight) {
commitChain := lc.localCommitChain
dustLimit := lc.localChanCfg.DustLimit
@ -3176,7 +3176,7 @@ func (lc *LightningChannel) validateCommitmentSanity(theirLogCounter,
// Calculate the commitment fee, and subtract it from the
// initiator's balance.
commitFee := btcutil.Amount((int64(feePerKw) * commitWeight) / 1000)
commitFee := feePerKw.FeeForWeight(commitWeight)
if lc.channelState.IsInitiator {
ourBalance -= lnwire.NewMSatFromSatoshis(commitFee)
} else {
@ -4229,7 +4229,7 @@ func NewUnilateralCloseSummary(chanState *channeldb.OpenChannel, signer Signer,
// Next, we'll obtain HTLC resolutions for all the outgoing HTLC's we
// had on their commitment transaction.
htlcResolutions, err := extractHtlcResolutions(
remoteCommit.FeePerKw, false, signer, remoteCommit.Htlcs,
SatPerKWeight(remoteCommit.FeePerKw), false, signer, remoteCommit.Htlcs,
keyRing, &chanState.LocalChanCfg, &chanState.RemoteChanCfg,
*commitSpend.SpenderTxHash, pCache,
)
@ -4403,7 +4403,7 @@ type HtlcResolutions struct {
// the remote party's commitment transaction.
func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelConfig,
commitHash chainhash.Hash, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
feePewKw, dustLimit btcutil.Amount, csvDelay uint32, localCommit bool,
feePerKw SatPerKWeight, dustLimit btcutil.Amount, csvDelay uint32, localCommit bool,
) (*OutgoingHtlcResolution, error) {
op := wire.OutPoint{
@ -4453,7 +4453,7 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
// In order to properly reconstruct the HTLC transaction, we'll need to
// re-calculate the fee required at this state, so we can add the
// correct output value amount to the transaction.
htlcFee := htlcTimeoutFee(feePewKw)
htlcFee := htlcTimeoutFee(feePerKw)
secondLevelOutputAmt := htlc.Amt.ToSatoshis() - htlcFee
// With the fee calculated, re-construct the second level timeout
@ -4541,9 +4541,8 @@ func newOutgoingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
// TODO(roasbeef) consolidate code with above func
func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelConfig,
commitHash chainhash.Hash, htlc *channeldb.HTLC, keyRing *CommitmentKeyRing,
feePewKw, dustLimit btcutil.Amount, csvDelay uint32, localCommit bool,
preimage [32]byte,
) (*IncomingHtlcResolution, error) {
feePerKw SatPerKWeight, dustLimit btcutil.Amount, csvDelay uint32,
localCommit bool, preimage [32]byte) (*IncomingHtlcResolution, error) {
op := wire.OutPoint{
Hash: commitHash,
@ -4590,7 +4589,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
// First, we'll reconstruct the original HTLC success transaction,
// taking into account the fee rate used.
htlcFee := htlcSuccessFee(feePewKw)
htlcFee := htlcSuccessFee(feePerKw)
secondLevelOutputAmt := htlc.Amt.ToSatoshis() - htlcFee
successTx, err := createHtlcSuccessTx(
op, secondLevelOutputAmt, csvDelay,
@ -4672,7 +4671,7 @@ func newIncomingHtlcResolution(signer Signer, localChanCfg *channeldb.ChannelCon
// extractHtlcResolutions creates a series of outgoing HTLC resolutions, and
// the local key used when generating the HTLC scrips. This function is to be
// used in two cases: force close, or a unilateral close.
func extractHtlcResolutions(feePerKw btcutil.Amount, ourCommit bool,
func extractHtlcResolutions(feePerKw SatPerKWeight, ourCommit bool,
signer Signer, htlcs []channeldb.HTLC, keyRing *CommitmentKeyRing,
localChanCfg, remoteChanCfg *channeldb.ChannelConfig,
commitHash chainhash.Hash, pCache PreimageCache) (*HtlcResolutions, error) {
@ -4872,8 +4871,9 @@ func (lc *LightningChannel) ForceClose() (*ForceCloseSummary, error) {
// outgoing HTLC's that we'll need to claim as well.
txHash := commitTx.TxHash()
htlcResolutions, err := extractHtlcResolutions(
localCommitment.FeePerKw, true, lc.signer, localCommitment.Htlcs,
keyRing, lc.localChanCfg, lc.remoteChanCfg, txHash, lc.pCache)
SatPerKWeight(localCommitment.FeePerKw), true, lc.signer,
localCommitment.Htlcs, keyRing, lc.localChanCfg,
lc.remoteChanCfg, txHash, lc.pCache)
if err != nil {
return nil, err
}
@ -5075,7 +5075,7 @@ func (lc *LightningChannel) availableBalance() (lnwire.MilliSatoshi, int64) {
// If we are the channel initiator, we must remember to subtract the
// commitment fee from our available balance.
commitFee := btcutil.Amount((int64(feePerKw) * commitWeight) / 1000)
commitFee := feePerKw.FeeForWeight(commitWeight)
if lc.channelState.IsInitiator {
ourBalance -= lnwire.NewMSatFromSatoshis(commitFee)
}
@ -5095,7 +5095,7 @@ func (lc *LightningChannel) StateSnapshot() *channeldb.ChannelSnapshot {
// validateFeeRate ensures that if the passed fee is applied to the channel,
// and a new commitment is created (which evaluates this fee), then the
// initiator of the channel does not dip below their reserve.
func (lc *LightningChannel) validateFeeRate(feePerKw btcutil.Amount) error {
func (lc *LightningChannel) validateFeeRate(feePerKw SatPerKWeight) error {
// We'll ensure that we can accommodate this new fee change, yet still
// be above our reserve balance. Otherwise, we'll reject the fee
// update.
@ -5105,7 +5105,7 @@ func (lc *LightningChannel) validateFeeRate(feePerKw btcutil.Amount) error {
// a commitment now, we'll compute our remaining balance if we apply
// this new fee update.
newFee := lnwire.NewMSatFromSatoshis(
btcutil.Amount((int64(feePerKw) * txWeight) / 1000),
feePerKw.FeeForWeight(txWeight),
)
balanceAfterFee := availableBalance - newFee
@ -5128,7 +5128,7 @@ func (lc *LightningChannel) validateFeeRate(feePerKw btcutil.Amount) error {
// UpdateFee initiates a fee update for this channel. Must only be called by
// the channel initiator, and must be called before sending update_fee to
// the remote.
func (lc *LightningChannel) UpdateFee(feePerKw btcutil.Amount) error {
func (lc *LightningChannel) UpdateFee(feePerKw SatPerKWeight) error {
lc.Lock()
defer lc.Unlock()
@ -5150,7 +5150,7 @@ func (lc *LightningChannel) UpdateFee(feePerKw btcutil.Amount) error {
// ReceiveUpdateFee handles an updated fee sent from remote. This method will
// return an error if called as channel initiator.
func (lc *LightningChannel) ReceiveUpdateFee(feePerKw btcutil.Amount) error {
func (lc *LightningChannel) ReceiveUpdateFee(feePerKw SatPerKWeight) error {
lc.Lock()
defer lc.Unlock()
@ -5301,8 +5301,8 @@ func CreateCooperativeCloseTx(fundingTxIn wire.TxIn,
// CalcFee returns the commitment fee to use for the given
// fee rate (fee-per-kw).
func (lc *LightningChannel) CalcFee(feeRate uint64) uint64 {
return (feeRate * uint64(CommitWeight)) / 1000
func (lc *LightningChannel) CalcFee(feeRate SatPerKWeight) btcutil.Amount {
return feeRate.FeeForWeight(CommitWeight)
}
// RemoteNextRevocation returns the channelState's RemoteNextRevocation.
@ -5325,11 +5325,11 @@ func (lc *LightningChannel) IsInitiator() bool {
// CommitFeeRate returns the current fee rate of the commitment transaction in
// units of sat-per-kw.
func (lc *LightningChannel) CommitFeeRate() btcutil.Amount {
func (lc *LightningChannel) CommitFeeRate() SatPerKWeight {
lc.RLock()
defer lc.RUnlock()
return lc.channelState.LocalCommitment.FeePerKw
return SatPerKWeight(lc.channelState.LocalCommitment.FeePerKw)
}
// IsPending returns true if the channel's funding transaction has been fully

View File

@ -231,11 +231,11 @@ func createTestChannels(revocationWindow int) (*LightningChannel,
}
estimator := &StaticFeeEstimator{24}
feePerWeight, err := estimator.EstimateFeePerWeight(1)
feePerVSize, err := estimator.EstimateFeePerVSize(1)
if err != nil {
return nil, nil, nil, err
}
feePerKw := feePerWeight * 1000
feePerKw := feePerVSize.FeePerKWeight()
commitFee := calcStaticFee(0)
aliceCommit := channeldb.ChannelCommitment{
@ -243,7 +243,7 @@ func createTestChannels(revocationWindow int) (*LightningChannel,
LocalBalance: lnwire.NewMSatFromSatoshis(channelBal - commitFee),
RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal),
CommitFee: commitFee,
FeePerKw: feePerKw,
FeePerKw: btcutil.Amount(feePerKw),
CommitTx: aliceCommitTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
}
@ -252,7 +252,7 @@ func createTestChannels(revocationWindow int) (*LightningChannel,
LocalBalance: lnwire.NewMSatFromSatoshis(channelBal),
RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal - commitFee),
CommitFee: commitFee,
FeePerKw: feePerKw,
FeePerKw: btcutil.Amount(feePerKw),
CommitTx: bobCommitTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
}
@ -747,12 +747,12 @@ func TestCooperativeChannelClosure(t *testing.T) {
aliceDeliveryScript := bobsPrivKey[:]
bobDeliveryScript := testHdSeed[:]
aliceFeeRate := uint64(aliceChannel.channelState.LocalCommitment.FeePerKw)
bobFeeRate := uint64(bobChannel.channelState.LocalCommitment.FeePerKw)
aliceFeeRate := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw)
bobFeeRate := SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw)
// We'll store with both Alice and Bob creating a new close proposal
// with the same fee.
aliceFee := btcutil.Amount(aliceChannel.CalcFee(aliceFeeRate))
aliceFee := aliceChannel.CalcFee(aliceFeeRate)
aliceSig, _, _, err := aliceChannel.CreateCloseProposal(
aliceFee, aliceDeliveryScript, bobDeliveryScript,
)
@ -761,7 +761,7 @@ func TestCooperativeChannelClosure(t *testing.T) {
}
aliceCloseSig := append(aliceSig, byte(txscript.SigHashAll))
bobFee := btcutil.Amount(bobChannel.CalcFee(bobFeeRate))
bobFee := bobChannel.CalcFee(bobFeeRate)
bobSig, _, _, err := bobChannel.CreateCloseProposal(
bobFee, bobDeliveryScript, aliceDeliveryScript,
)
@ -889,8 +889,8 @@ func TestForceClose(t *testing.T) {
// Factoring in the fee rate, Alice's amount should properly reflect
// that we've added two additional HTLC to the commitment transaction.
totalCommitWeight := CommitWeight + (HtlcWeight * 2)
feePerKw := aliceChannel.channelState.LocalCommitment.FeePerKw
commitFee := btcutil.Amount((int64(feePerKw) * totalCommitWeight) / 1000)
feePerKw := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw)
commitFee := feePerKw.FeeForWeight(totalCommitWeight)
expectedAmount := (aliceChannel.Capacity / 2) - htlcAmount.ToSatoshis() - commitFee
if aliceCommitResolution.SelfOutputSignDesc.Output.Value != int64(expectedAmount) {
t.Fatalf("alice incorrect output value in SelfOutputSignDesc, "+
@ -1297,8 +1297,8 @@ func TestHTLCDustLimit(t *testing.T) {
// The amount of the HTLC should be above Alice's dust limit and below
// Bob's dust limit.
htlcSat := (btcutil.Amount(500) +
htlcTimeoutFee(aliceChannel.channelState.LocalCommitment.FeePerKw))
htlcSat := (btcutil.Amount(500) + htlcTimeoutFee(
SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw)))
htlcAmount := lnwire.NewMSatFromSatoshis(htlcSat)
htlc, preimage := createHTLC(0, htlcAmount)
@ -1392,7 +1392,7 @@ func TestChannelBalanceDustLimit(t *testing.T) {
aliceBalance := aliceChannel.channelState.LocalCommitment.LocalBalance.ToSatoshis()
htlcSat := aliceBalance - defaultFee
htlcSat += htlcSuccessFee(
aliceChannel.channelState.LocalCommitment.FeePerKw,
SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw),
)
htlcAmount := lnwire.NewMSatFromSatoshis(htlcSat)
@ -1853,8 +1853,8 @@ func TestCooperativeCloseDustAdherence(t *testing.T) {
}
defer cleanUp()
aliceFeeRate := uint64(aliceChannel.channelState.LocalCommitment.FeePerKw)
bobFeeRate := uint64(bobChannel.channelState.LocalCommitment.FeePerKw)
aliceFeeRate := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw)
bobFeeRate := SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw)
setDustLimit := func(dustVal btcutil.Amount) {
aliceChannel.channelState.LocalChanCfg.DustLimit = dustVal
@ -2069,7 +2069,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
}
// Simulate Alice sending update fee message to bob.
fee := btcutil.Amount(111)
fee := SatPerKWeight(111)
aliceChannel.UpdateFee(fee)
bobChannel.ReceiveUpdateFee(fee)
@ -2089,7 +2089,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
t.Fatalf("bob unable to process alice's new commitment: %v", err)
}
if bobChannel.channelState.LocalCommitment.FeePerKw == fee {
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee {
t.Fatalf("bob's feePerKw was unexpectedly locked in")
}
@ -2100,7 +2100,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
t.Fatalf("unable to generate bob revocation: %v", err)
}
if bobChannel.channelState.LocalCommitment.FeePerKw != fee {
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee {
t.Fatalf("bob's feePerKw was not locked in")
}
@ -2125,7 +2125,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
t.Fatalf("alice unable to process bob's new commitment: %v", err)
}
if aliceChannel.channelState.LocalCommitment.FeePerKw == fee {
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) == fee {
t.Fatalf("alice's feePerKw was unexpectedly locked in")
}
@ -2136,7 +2136,7 @@ func TestUpdateFeeSenderCommits(t *testing.T) {
t.Fatalf("unable to revoke alice channel: %v", err)
}
if aliceChannel.channelState.LocalCommitment.FeePerKw != fee {
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != fee {
t.Fatalf("alice's feePerKw was not locked in")
}
@ -2181,7 +2181,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
}
// Simulate Alice sending update fee message to bob
fee := btcutil.Amount(111)
fee := SatPerKWeight(111)
aliceChannel.UpdateFee(fee)
bobChannel.ReceiveUpdateFee(fee)
@ -2228,7 +2228,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
t.Fatalf("alice unable to process bob's new commitment: %v", err)
}
if bobChannel.channelState.LocalCommitment.FeePerKw == fee {
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee {
t.Fatalf("bob's feePerKw was unexpectedly locked in")
}
@ -2240,7 +2240,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
t.Fatalf("unable to revoke alice channel: %v", err)
}
if bobChannel.channelState.LocalCommitment.FeePerKw != fee {
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee {
t.Fatalf("bob's feePerKw was not locked in")
}
@ -2264,7 +2264,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
t.Fatalf("alice unable to process bob's new commitment: %v", err)
}
if aliceChannel.channelState.LocalCommitment.FeePerKw == fee {
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) == fee {
t.Fatalf("alice's feePerKw was unexpectedly locked in")
}
@ -2275,7 +2275,7 @@ func TestUpdateFeeReceiverCommits(t *testing.T) {
t.Fatalf("unable to generate bob revocation: %v", err)
}
if aliceChannel.channelState.LocalCommitment.FeePerKw != fee {
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != fee {
t.Fatalf("Alice's feePerKw was not locked in")
}
@ -2302,7 +2302,7 @@ func TestUpdateFeeReceiverSendsUpdate(t *testing.T) {
// Since Alice is the channel initiator, she should fail when receiving
// fee update
fee := btcutil.Amount(111)
fee := SatPerKWeight(111)
err = aliceChannel.ReceiveUpdateFee(fee)
if err == nil {
t.Fatalf("expected alice to fail receiving fee update")
@ -2330,9 +2330,9 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
defer cleanUp()
// Simulate Alice sending update fee message to bob.
fee1 := btcutil.Amount(111)
fee2 := btcutil.Amount(222)
fee := btcutil.Amount(333)
fee1 := SatPerKWeight(111)
fee2 := SatPerKWeight(222)
fee := SatPerKWeight(333)
aliceChannel.UpdateFee(fee1)
aliceChannel.UpdateFee(fee2)
aliceChannel.UpdateFee(fee)
@ -2357,15 +2357,15 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
t.Fatalf("bob unable to process alice's new commitment: %v", err)
}
if bobChannel.channelState.LocalCommitment.FeePerKw == fee {
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) == fee {
t.Fatalf("bob's feePerKw was unexpectedly locked in")
}
// Alice sending more fee updates now should not mess up the old fee
// they both committed to.
fee3 := btcutil.Amount(444)
fee4 := btcutil.Amount(555)
fee5 := btcutil.Amount(666)
fee3 := SatPerKWeight(444)
fee4 := SatPerKWeight(555)
fee5 := SatPerKWeight(666)
aliceChannel.UpdateFee(fee3)
aliceChannel.UpdateFee(fee4)
aliceChannel.UpdateFee(fee5)
@ -2380,7 +2380,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
t.Fatalf("unable to generate bob revocation: %v", err)
}
if bobChannel.channelState.LocalCommitment.FeePerKw != fee {
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != fee {
t.Fatalf("bob's feePerKw was not locked in")
}
@ -2404,7 +2404,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
t.Fatalf("alice unable to process bob's new commitment: %v", err)
}
if aliceChannel.channelState.LocalCommitment.FeePerKw == fee {
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) == fee {
t.Fatalf("alice's feePerKw was unexpectedly locked in")
}
@ -2415,7 +2415,7 @@ func TestUpdateFeeMultipleUpdates(t *testing.T) {
t.Fatalf("unable to revoke alice channel: %v", err)
}
if aliceChannel.channelState.LocalCommitment.FeePerKw != fee {
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != fee {
t.Fatalf("alice's feePerKw was not locked in")
}
@ -3498,7 +3498,7 @@ func TestFeeUpdateRejectInsaneFee(t *testing.T) {
// Next, we'll try to add a fee rate to Alice which is 1,000,000x her
// starting fee rate.
startingFeeRate := aliceChannel.channelState.LocalCommitment.FeePerKw
startingFeeRate := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw)
newFeeRate := startingFeeRate * 1000000
// Both Alice and Bob should reject this new fee rate as it it far too
@ -3524,7 +3524,7 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
// First, we'll fetch the current fee rate present within the
// commitment transactions.
startingFeeRate := aliceChannel.channelState.LocalCommitment.FeePerKw
startingFeeRate := SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw)
// Next, we'll start a commitment update, with Alice sending a new
// update to double the fee rate of the commitment.
@ -3664,10 +3664,10 @@ func TestChannelRetransmissionFeeUpdate(t *testing.T) {
}
// Both parties should now have the latest fee rate locked-in.
if aliceChannel.channelState.LocalCommitment.FeePerKw != newFeeRate {
if SatPerKWeight(aliceChannel.channelState.LocalCommitment.FeePerKw) != newFeeRate {
t.Fatalf("alice's feePerKw was not locked in")
}
if bobChannel.channelState.LocalCommitment.FeePerKw != newFeeRate {
if SatPerKWeight(bobChannel.channelState.LocalCommitment.FeePerKw) != newFeeRate {
t.Fatalf("bob's feePerKw was not locked in")
}

View File

@ -8,19 +8,38 @@ import (
"github.com/roasbeef/btcutil"
)
// SatPerVByte represents a fee rate in satoshis per vbyte.
type SatPerVByte btcutil.Amount
// FeeForVSize calculates the fee resulting from this fee rate and
// the given vsize in vbytes.
func (s SatPerVByte) FeeForVSize(vbytes int64) btcutil.Amount {
return btcutil.Amount(s) * btcutil.Amount(vbytes)
}
// FeePerKWeight converts the fee rate into SatPerKWeight.
func (s SatPerVByte) FeePerKWeight() SatPerKWeight {
return SatPerKWeight(s * 1000 / blockchain.WitnessScaleFactor)
}
// SatPerKWeight represents a fee rate in satoshis per kilo weight unit.
type SatPerKWeight btcutil.Amount
// FeeForWeight calculates the fee resulting from this fee rate and the
// given weight in weight units (wu).
func (s SatPerKWeight) FeeForWeight(wu int64) btcutil.Amount {
// The resulting fee is rounded down, as specified in BOLT#03.
return btcutil.Amount(s) * btcutil.Amount(wu) / 1000
}
// FeeEstimator provides the ability to estimate on-chain transaction fees for
// various combinations of transaction sizes and desired confirmation time
// (measured by number of blocks).
type FeeEstimator interface {
// EstimateFeePerByte takes in a target for the number of blocks until
// EstimateFeePerVSize takes in a target for the number of blocks until
// an initial confirmation and returns the estimated fee expressed in
// satoshis/byte.
EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error)
// EstimateFeePerWeight takes in a target for the number of blocks
// until an initial confirmation and returns the estimated fee
// expressed in satoshis/weight.
EstimateFeePerWeight(numBlocks uint32) (btcutil.Amount, error)
// satoshis/vbyte.
EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error)
// Start signals the FeeEstimator to start any processes or goroutines
// it needs to perform its duty.
@ -35,26 +54,18 @@ type FeeEstimator interface {
// requests. It is designed to be replaced by a proper fee calculation
// implementation.
type StaticFeeEstimator struct {
// FeeRate is the static fee rate in satoshis-per-byte that will be
// returned by this fee estimator. Queries for the fee rate in weight
// units will be scaled accordingly.
FeeRate btcutil.Amount
// FeeRate is the static fee rate in satoshis-per-vbyte that will be
// returned by this fee estimator.
FeeRate SatPerVByte
}
// EstimateFeePerByte will return a static value for fee calculations.
// EstimateFeePerVSize will return a static value for fee calculations.
//
// NOTE: This method is part of the FeeEstimator interface.
func (e StaticFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error) {
func (e StaticFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error) {
return e.FeeRate, nil
}
// EstimateFeePerWeight will return a static value for fee calculations.
//
// NOTE: This method is part of the FeeEstimator interface.
func (e StaticFeeEstimator) EstimateFeePerWeight(numBlocks uint32) (btcutil.Amount, error) {
return e.FeeRate / blockchain.WitnessScaleFactor, nil
}
// Start signals the FeeEstimator to start any processes or goroutines
// it needs to perform its duty.
//
@ -79,10 +90,10 @@ var _ FeeEstimator = (*StaticFeeEstimator)(nil)
// by the RPC interface of an active btcd node. This implementation will proxy
// any fee estimation requests to btcd's RPC interface.
type BtcdFeeEstimator struct {
// fallBackFeeRate is the fall back fee rate in satoshis per byte that
// fallBackFeeRate is the fall back fee rate in satoshis per vbyte that
// is returned if the fee estimator does not yet have enough data to
// actually produce fee estimates.
fallBackFeeRate btcutil.Amount
fallBackFeeRate SatPerVByte
btcdConn *rpcclient.Client
}
@ -93,7 +104,7 @@ type BtcdFeeEstimator struct {
// the occasion that the estimator has insufficient data, or returns zero for a
// fee estimate.
func NewBtcdFeeEstimator(rpcConfig rpcclient.ConnConfig,
fallBackFeeRate btcutil.Amount) (*BtcdFeeEstimator, error) {
fallBackFeeRate SatPerVByte) (*BtcdFeeEstimator, error) {
rpcConfig.DisableConnectOnNew = true
rpcConfig.DisableAutoReconnect = false
@ -130,11 +141,13 @@ func (b *BtcdFeeEstimator) Stop() error {
return nil
}
// EstimateFeePerByte takes in a target for the number of blocks until an
// EstimateFeePerVSize takes in a target for the number of blocks until an
// initial confirmation and returns the estimated fee expressed in
// satoshis/byte.
func (b *BtcdFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error) {
feeEstimate, err := b.fetchEstimatePerByte(numBlocks)
// satoshis/vbyte.
//
// NOTE: This method is part of the FeeEstimator interface.
func (b *BtcdFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error) {
feeEstimate, err := b.fetchEstimatePerVSize(numBlocks)
switch {
// If the estimator doesn't have enough data, or returns an error, then
// to return a proper value, then we'll return the default fall back
@ -150,31 +163,10 @@ func (b *BtcdFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amount,
return feeEstimate, nil
}
// EstimateFeePerWeight takes in a target for the number of blocks until an
// initial confirmation and returns the estimated fee expressed in
// satoshis/weight.
func (b *BtcdFeeEstimator) EstimateFeePerWeight(numBlocks uint32) (btcutil.Amount, error) {
feePerByte, err := b.EstimateFeePerByte(numBlocks)
if err != nil {
return 0, err
}
// We'll scale down the fee per byte to fee per weight, as for each raw
// byte, there's 1/4 unit of weight mapped to it.
satWeight := feePerByte / blockchain.WitnessScaleFactor
// If this ends up scaling down to a zero sat/weight amount, then we'll
// use the default fallback fee rate.
if satWeight == 0 {
return b.fallBackFeeRate / blockchain.WitnessScaleFactor, nil
}
return satWeight, nil
}
// fetchEstimate returns a fee estimate for a transaction be be confirmed in
// confTarget blocks. The estimate is returned in sat/byte.
func (b *BtcdFeeEstimator) fetchEstimatePerByte(confTarget uint32) (btcutil.Amount, error) {
// confTarget blocks. The estimate is returned in sat/vbyte.
func (b *BtcdFeeEstimator) fetchEstimatePerVSize(
confTarget uint32) (SatPerVByte, error) {
// First, we'll fetch the estimate for our confirmation target.
btcPerKB, err := b.btcdConn.EstimateFee(int64(confTarget))
if err != nil {
@ -189,14 +181,14 @@ func (b *BtcdFeeEstimator) fetchEstimatePerByte(confTarget uint32) (btcutil.Amou
}
// The value returned is expressed in fees per KB, while we want
// fee-per-byte, so we'll divide by 1024 to map to satoshis-per-byte
// fee-per-byte, so we'll divide by 1000 to map to satoshis-per-byte
// before returning the estimate.
satPerByte := satPerKB / 1024
satPerByte := satPerKB / 1000
walletLog.Debugf("Returning %v sat/byte for conf target of %v",
walletLog.Debugf("Returning %v sat/vbyte for conf target of %v",
int64(satPerByte), confTarget)
return satPerByte, nil
return SatPerVByte(satPerByte), nil
}
// A compile-time assertion to ensure that BtcdFeeEstimator implements the
@ -207,10 +199,10 @@ var _ FeeEstimator = (*BtcdFeeEstimator)(nil)
// backed by the RPC interface of an active bitcoind node. This implementation
// will proxy any fee estimation requests to bitcoind's RPC interface.
type BitcoindFeeEstimator struct {
// fallBackFeeRate is the fall back fee rate in satoshis per byte that
// fallBackFeeRate is the fall back fee rate in satoshis per vbyte that
// is returned if the fee estimator does not yet have enough data to
// actually produce fee estimates.
fallBackFeeRate btcutil.Amount
fallBackFeeRate SatPerVByte
bitcoindConn *rpcclient.Client
}
@ -221,7 +213,7 @@ type BitcoindFeeEstimator struct {
// is used in the occasion that the estimator has insufficient data, or returns
// zero for a fee estimate.
func NewBitcoindFeeEstimator(rpcConfig rpcclient.ConnConfig,
fallBackFeeRate btcutil.Amount) (*BitcoindFeeEstimator, error) {
fallBackFeeRate SatPerVByte) (*BitcoindFeeEstimator, error) {
rpcConfig.DisableConnectOnNew = true
rpcConfig.DisableAutoReconnect = false
@ -254,11 +246,13 @@ func (b *BitcoindFeeEstimator) Stop() error {
return nil
}
// EstimateFeePerByte takes in a target for the number of blocks until an
// EstimateFeePerVSize takes in a target for the number of blocks until an
// initial confirmation and returns the estimated fee expressed in
// satoshis/byte.
func (b *BitcoindFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amount, error) {
feeEstimate, err := b.fetchEstimatePerByte(numBlocks)
// satoshis/vbyte.
//
// NOTE: This method is part of the FeeEstimator interface.
func (b *BitcoindFeeEstimator) EstimateFeePerVSize(numBlocks uint32) (SatPerVByte, error) {
feeEstimate, err := b.fetchEstimatePerVSize(numBlocks)
switch {
// If the estimator doesn't have enough data, or returns an error, then
// to return a proper value, then we'll return the default fall back
@ -274,33 +268,10 @@ func (b *BitcoindFeeEstimator) EstimateFeePerByte(numBlocks uint32) (btcutil.Amo
return feeEstimate, nil
}
// EstimateFeePerWeight takes in a target for the number of blocks until an
// initial confirmation and returns the estimated fee expressed in
// satoshis/weight.
func (b *BitcoindFeeEstimator) EstimateFeePerWeight(numBlocks uint32) (btcutil.Amount, error) {
feePerByte, err := b.EstimateFeePerByte(numBlocks)
if err != nil {
return 0, err
}
// We'll scale down the fee per byte to fee per weight, as for each raw
// byte, there's 1/4 unit of weight mapped to it.
satWeight := feePerByte / blockchain.WitnessScaleFactor
// If this ends up scaling down to a zero sat/weight amount, then we'll
// use the default fallback fee rate.
// TODO(aakselrod): maybe use the per-byte rate if it's non-zero?
// Otherwise, we can return a higher sat/byte than sat/weight.
if satWeight == 0 {
return b.fallBackFeeRate / blockchain.WitnessScaleFactor, nil
}
return satWeight, nil
}
// fetchEstimate returns a fee estimate for a transaction be be confirmed in
// confTarget blocks. The estimate is returned in sat/byte.
func (b *BitcoindFeeEstimator) fetchEstimatePerByte(confTarget uint32) (btcutil.Amount, error) {
// fetchEstimatePerVSize returns a fee estimate for a transaction be be confirmed in
// confTarget blocks. The estimate is returned in sat/vbyte.
func (b *BitcoindFeeEstimator) fetchEstimatePerVSize(
confTarget uint32) (SatPerVByte, error) {
// First, we'll send an "estimatesmartfee" command as a raw request,
// since it isn't supported by btcd but is available in bitcoind.
target, err := json.Marshal(uint64(confTarget))
@ -335,10 +306,10 @@ func (b *BitcoindFeeEstimator) fetchEstimatePerByte(confTarget uint32) (btcutil.
// before returning the estimate.
satPerByte := satPerKB / 1000
walletLog.Debugf("Returning %v sat/byte for conf target of %v",
walletLog.Debugf("Returning %v sat/vbyte for conf target of %v",
int64(satPerByte), confTarget)
return satPerByte, nil
return SatPerVByte(satPerByte), nil
}
// A compile-time assertion to ensure that BitcoindFeeEstimator implements the

View File

@ -0,0 +1,94 @@
package lnwallet_test
import (
"testing"
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/roasbeef/btcutil"
)
// TestFeeRateTypes checks that converting fee rates between the
// different types that represent fee rates and calculating fees
// work as expected.
func TestFeeRateTypes(t *testing.T) {
t.Parallel()
// Let our fee rate be 100 sat/vbyte.
feePerVSize := lnwallet.SatPerVByte(100)
// It is also equivalent to 25000 sat/kw.
feePerKw := feePerVSize.FeePerKWeight()
if feePerKw != 25000 {
t.Fatalf("expected %d sat/kw, got %d sat/kw", 25000,
feePerKw)
}
const txVSize = 300
// We'll now run through a set of values for the fee per vsize type,
// making sure the conversion to sat/kw and fee calculation is done
// correctly.
for f := lnwallet.SatPerVByte(0); f <= 40; f++ {
fPerKw := f.FeePerKWeight()
// The kw is always 250*vsize.
if fPerKw != lnwallet.SatPerKWeight(f*250) {
t.Fatalf("expected %d sat/kw, got %d sat/kw, when "+
"converting %d sat/vbyte", f*250, fPerKw, f)
}
// The tx fee should simply be f*txvsize.
fee := f.FeeForVSize(txVSize)
if fee != btcutil.Amount(f*txVSize) {
t.Fatalf("expected tx fee to be %d sat, was %d sat",
f*txVSize, fee)
}
// The weight is 4*vsize. Fee calculation from the fee/kw
// should result in the same fee.
fee2 := fPerKw.FeeForWeight(txVSize * 4)
if fee != fee2 {
t.Fatalf("fee calculated from vsize (%d) not equal "+
"fee calculated from weight (%d)", fee, fee2)
}
}
// Do the same for fee per kw.
for f := lnwallet.SatPerKWeight(0); f < 1500; f++ {
weight := int64(txVSize * 4)
// The expected fee is weight*f / 1000, since the fee is
// denominated per 1000 wu.
expFee := btcutil.Amount(weight) * btcutil.Amount(f) / 1000
fee := f.FeeForWeight(weight)
if fee != expFee {
t.Fatalf("expected fee to be %d sat, was %d",
fee, expFee)
}
}
}
// TestStaticFeeEstimator checks that the StaticFeeEstimator
// returns the expected fee rate.
func TestStaticFeeEstimator(t *testing.T) {
t.Parallel()
const feePerVSize = 100
feeEstimator := &lnwallet.StaticFeeEstimator{
FeeRate: feePerVSize,
}
if err := feeEstimator.Start(); err != nil {
t.Fatalf("unable to start fee estimator: %v", err)
}
defer feeEstimator.Stop()
feeRate, err := feeEstimator.EstimateFeePerVSize(6)
if err != nil {
t.Fatalf("unable to get fee rate: %v", err)
}
if feeRate != feePerVSize {
t.Fatalf("expected fee rate %v, got %v", feePerVSize, feeRate)
}
}

View File

@ -161,9 +161,9 @@ type WalletController interface {
// paying out to the specified outputs. In the case the wallet has
// insufficient funds, or the outputs are non-standard, an error should
// be returned. This method also takes the target fee expressed in
// sat/byte that should be used when crafting the transaction.
// sat/vbyte that should be used when crafting the transaction.
SendOutputs(outputs []*wire.TxOut,
feeSatPerByte btcutil.Amount) (*chainhash.Hash, error)
feeRate SatPerVByte) (*chainhash.Hash, error)
// ListUnspentWitness returns all unspent outputs which are version 0
// witness programs. The 'confirms' parameter indicates the minimum

View File

@ -283,13 +283,13 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness,
// Alice initiates a channel funded with 5 BTC for each side, so 10 BTC
// total. She also generates 2 BTC in change.
feePerWeight, err := alice.Cfg.FeeEstimator.EstimateFeePerWeight(1)
feeRate, err := alice.Cfg.FeeEstimator.EstimateFeePerVSize(1)
if err != nil {
t.Fatalf("unable to query fee estimator: %v", err)
}
feePerKw := feePerWeight * 1000
feePerKw := feeRate.FeePerKWeight()
aliceChanReservation, err := alice.InitChannelReservation(
fundingAmount*2, fundingAmount, 0, feePerKw, feePerKw,
fundingAmount*2, fundingAmount, 0, feePerKw, feeRate,
bobPub, bobAddr, chainHash, lnwire.FFAnnounceChannel)
if err != nil {
t.Fatalf("unable to initialize funding reservation: %v", err)
@ -313,7 +313,7 @@ func testDualFundingReservationWorkflow(miner *rpctest.Harness,
// receives' Alice's contribution, and consumes that so we can continue
// the funding process.
bobChanReservation, err := bob.InitChannelReservation(fundingAmount*2,
fundingAmount, 0, feePerKw, feePerKw, alicePub, aliceAddr,
fundingAmount, 0, feePerKw, feeRate, alicePub, aliceAddr,
chainHash, lnwire.FFAnnounceChannel)
if err != nil {
t.Fatalf("bob unable to init channel reservation: %v", err)
@ -449,13 +449,13 @@ func testFundingTransactionLockedOutputs(miner *rpctest.Harness,
// Create a single channel asking for 16 BTC total.
fundingAmount := btcutil.Amount(8 * 1e8)
feePerWeight, err := alice.Cfg.FeeEstimator.EstimateFeePerWeight(1)
feeRate, err := alice.Cfg.FeeEstimator.EstimateFeePerVSize(1)
if err != nil {
t.Fatalf("unable to query fee estimator: %v", err)
}
feePerKw := feePerWeight * 1000
feePerKw := feeRate.FeePerKWeight()
_, err = alice.InitChannelReservation(fundingAmount,
fundingAmount, 0, feePerKw, feePerKw, bobPub, bobAddr, chainHash,
fundingAmount, 0, feePerKw, feeRate, bobPub, bobAddr, chainHash,
lnwire.FFAnnounceChannel,
)
if err != nil {
@ -467,7 +467,8 @@ func testFundingTransactionLockedOutputs(miner *rpctest.Harness,
// that aren't locked, so this should fail.
amt := btcutil.Amount(900 * 1e8)
failedReservation, err := alice.InitChannelReservation(amt, amt, 0,
feePerKw, feePerKw, bobPub, bobAddr, chainHash, lnwire.FFAnnounceChannel)
feePerKw, feeRate, bobPub, bobAddr, chainHash,
lnwire.FFAnnounceChannel)
if err == nil {
t.Fatalf("not error returned, should fail on coin selection")
}
@ -482,16 +483,16 @@ func testFundingTransactionLockedOutputs(miner *rpctest.Harness,
func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness,
alice, _ *lnwallet.LightningWallet, t *testing.T) {
feePerWeight, err := alice.Cfg.FeeEstimator.EstimateFeePerWeight(1)
feeRate, err := alice.Cfg.FeeEstimator.EstimateFeePerVSize(1)
if err != nil {
t.Fatalf("unable to query fee estimator: %v", err)
}
feePerKw := feePerWeight * 1000
feePerKw := feeRate.FeePerKWeight()
// Create a reservation for 44 BTC.
fundingAmount := btcutil.Amount(44 * 1e8)
chanReservation, err := alice.InitChannelReservation(fundingAmount,
fundingAmount, 0, feePerKw, feePerKw, bobPub, bobAddr, chainHash,
fundingAmount, 0, feePerKw, feeRate, bobPub, bobAddr, chainHash,
lnwire.FFAnnounceChannel)
if err != nil {
t.Fatalf("unable to initialize funding reservation: %v", err)
@ -499,7 +500,7 @@ func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness,
// Attempt to create another channel with 44 BTC, this should fail.
_, err = alice.InitChannelReservation(fundingAmount,
fundingAmount, 0, feePerKw, feePerKw, bobPub, bobAddr, chainHash,
fundingAmount, 0, feePerKw, feeRate, bobPub, bobAddr, chainHash,
lnwire.FFAnnounceChannel,
)
if _, ok := err.(*lnwallet.ErrInsufficientFunds); !ok {
@ -530,8 +531,9 @@ func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness,
// attempting coin selection.
// Request to fund a new channel should now succeed.
_, err = alice.InitChannelReservation(fundingAmount, fundingAmount, 0,
feePerKw, feePerKw, bobPub, bobAddr, chainHash, lnwire.FFAnnounceChannel)
_, err = alice.InitChannelReservation(fundingAmount, fundingAmount,
0, feePerKw, feeRate, bobPub, bobAddr, chainHash,
lnwire.FFAnnounceChannel)
if err != nil {
t.Fatalf("unable to initialize funding reservation: %v", err)
}
@ -540,14 +542,15 @@ func testFundingCancellationNotEnoughFunds(miner *rpctest.Harness,
func testCancelNonExistentReservation(miner *rpctest.Harness,
alice, _ *lnwallet.LightningWallet, t *testing.T) {
feeRate, err := alice.Cfg.FeeEstimator.EstimateFeePerWeight(1)
feeRate, err := alice.Cfg.FeeEstimator.EstimateFeePerVSize(1)
if err != nil {
t.Fatalf("unable to query fee estimator: %v", err)
}
// Create our own reservation, give it some ID.
res, err := lnwallet.NewChannelReservation(
10000, 10000, feeRate, alice, 22, 10, &testHdSeed, lnwire.FFAnnounceChannel,
10000, 10000, feeRate.FeePerKWeight(), alice,
22, 10, &testHdSeed, lnwire.FFAnnounceChannel,
)
if err != nil {
t.Fatalf("unable to create res: %v", err)
@ -567,9 +570,10 @@ func testReservationInitiatorBalanceBelowDustCancel(miner *rpctest.Harness,
// rate. This should push our balance into the negative and result in a
// failure to create the reservation.
fundingAmount := btcutil.Amount(4 * 1e8)
feePerKw := btcutil.Amount(btcutil.SatoshiPerBitcoin * 10)
feePerVSize := lnwallet.SatPerVByte(btcutil.SatoshiPerBitcoin * 4 / 100)
feePerKw := feePerVSize.FeePerKWeight()
_, err := alice.InitChannelReservation(
fundingAmount, fundingAmount, 0, feePerKw, feePerKw, bobPub,
fundingAmount, fundingAmount, 0, feePerKw, feePerVSize, bobPub,
bobAddr, chainHash, lnwire.FFAnnounceChannel,
)
switch {
@ -636,13 +640,13 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness,
// towards Bob's side.
fundingAmt := btcutil.Amount(4 * 1e8)
pushAmt := lnwire.NewMSatFromSatoshis(btcutil.SatoshiPerBitcoin)
feePerWeight, err := alice.Cfg.FeeEstimator.EstimateFeePerWeight(1)
feeRate, err := alice.Cfg.FeeEstimator.EstimateFeePerVSize(1)
if err != nil {
t.Fatalf("unable to query fee estimator: %v", err)
}
feePerKw := feePerWeight * 1000
feePerKw := feeRate.FeePerKWeight()
aliceChanReservation, err := alice.InitChannelReservation(fundingAmt,
fundingAmt, pushAmt, feePerKw, feePerKw, bobPub, bobAddr, chainHash,
fundingAmt, pushAmt, feePerKw, feeRate, bobPub, bobAddr, chainHash,
lnwire.FFAnnounceChannel)
if err != nil {
t.Fatalf("unable to init channel reservation: %v", err)
@ -666,7 +670,7 @@ func testSingleFunderReservationWorkflow(miner *rpctest.Harness,
// Next, Bob receives the initial request, generates a corresponding
// reservation initiation, then consume Alice's contribution.
bobChanReservation, err := bob.InitChannelReservation(fundingAmt, 0,
pushAmt, feePerKw, feePerKw, alicePub, aliceAddr, chainHash,
pushAmt, feePerKw, feeRate, alicePub, aliceAddr, chainHash,
lnwire.FFAnnounceChannel)
if err != nil {
t.Fatalf("unable to create bob reservation: %v", err)

View File

@ -135,9 +135,9 @@ type ChannelReservation struct {
// used only internally by lnwallet. In order to concurrent safety, the
// creation of all channel reservations should be carried out via the
// lnwallet.InitChannelReservation interface.
func NewChannelReservation(capacity, fundingAmt, commitFeePerKw btcutil.Amount,
wallet *LightningWallet, id uint64, pushMSat lnwire.MilliSatoshi,
chainHash *chainhash.Hash,
func NewChannelReservation(capacity, fundingAmt btcutil.Amount,
commitFeePerKw SatPerKWeight, wallet *LightningWallet,
id uint64, pushMSat lnwire.MilliSatoshi, chainHash *chainhash.Hash,
flags lnwire.FundingFlag) (*ChannelReservation, error) {
var (
@ -146,10 +146,7 @@ func NewChannelReservation(capacity, fundingAmt, commitFeePerKw btcutil.Amount,
initiator bool
)
commitFee := btcutil.Amount(
(int64(commitFeePerKw) * CommitWeight) / 1000,
)
commitFee := commitFeePerKw.FeeForWeight(CommitWeight)
fundingMSat := lnwire.NewMSatFromSatoshis(fundingAmt)
capacityMSat := lnwire.NewMSatFromSatoshis(capacity)
feeMSat := lnwire.NewMSatFromSatoshis(commitFee)
@ -229,13 +226,13 @@ func NewChannelReservation(capacity, fundingAmt, commitFeePerKw btcutil.Amount,
LocalCommitment: channeldb.ChannelCommitment{
LocalBalance: ourBalance,
RemoteBalance: theirBalance,
FeePerKw: commitFeePerKw,
FeePerKw: btcutil.Amount(commitFeePerKw),
CommitFee: commitFee,
},
RemoteCommitment: channeldb.ChannelCommitment{
LocalBalance: ourBalance,
RemoteBalance: theirBalance,
FeePerKw: commitFeePerKw,
FeePerKw: btcutil.Amount(commitFeePerKw),
CommitFee: commitFee,
},
Db: wallet.Cfg.Database,

View File

@ -513,3 +513,9 @@ func (twe *TxWeightEstimator) Weight() int {
}
return weight
}
// VSize gets the estimated virtual size of the transactions, in vbytes.
func (twe *TxWeightEstimator) VSize() int {
// A tx's vsize is 1/4 of the weight, rounded up.
return (twe.Weight() + witnessScaleFactor - 1) / witnessScaleFactor
}

View File

@ -787,7 +787,7 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
height: test.commitment.CommitHeight,
ourBalance: test.commitment.LocalBalance,
theirBalance: test.commitment.RemoteBalance,
feePerKw: test.commitment.FeePerKw,
feePerKw: SatPerKWeight(test.commitment.FeePerKw),
dustLimit: tc.dustLimit,
isOurs: true,
}
@ -829,8 +829,8 @@ func TestCommitmentAndHTLCTransactions(t *testing.T) {
// Generate second-level HTLC transactions for HTLCs in
// commitment tx.
htlcResolutions, err := extractHtlcResolutions(
test.commitment.FeePerKw, true, signer, htlcs, keys,
channel.localChanCfg, channel.remoteChanCfg,
SatPerKWeight(test.commitment.FeePerKw), true, signer,
htlcs, keys, channel.localChanCfg, channel.remoteChanCfg,
commitTx.TxHash(), pCache,
)
if err != nil {

View File

@ -97,11 +97,11 @@ type initFundingReserveMsg struct {
// of initial commitment transactions. In order to ensure timely
// confirmation, it is recommended that this fee should be generous,
// paying some multiple of the accepted base fee rate of the network.
commitFeePerKw btcutil.Amount
commitFeePerKw SatPerKWeight
// fundingFeePerWeight is the fee rate in satoshis per eight unit to
// use for the initial funding transaction.
fundingFeePerWeight btcutil.Amount
// fundingFeePerVSize is the fee rate in sat/vbyte to use for the
// initial funding transaction.
fundingFeePerVSize SatPerVByte
// pushMSat is the number of milli-satoshis that should be pushed over
// the responder as part of the initial channel creation.
@ -450,7 +450,7 @@ out:
// commitment transaction is valid.
func (l *LightningWallet) InitChannelReservation(
capacity, ourFundAmt btcutil.Amount, pushMSat lnwire.MilliSatoshi,
commitFeePerKw, fundingFeePerWeight btcutil.Amount,
commitFeePerKw SatPerKWeight, fundingFeePerVSize SatPerVByte,
theirID *btcec.PublicKey, theirAddr net.Addr,
chainHash *chainhash.Hash, flags lnwire.FundingFlag) (*ChannelReservation, error) {
@ -458,17 +458,17 @@ func (l *LightningWallet) InitChannelReservation(
respChan := make(chan *ChannelReservation, 1)
l.msgChan <- &initFundingReserveMsg{
chainHash: chainHash,
nodeID: theirID,
nodeAddr: theirAddr,
fundingAmount: ourFundAmt,
capacity: capacity,
commitFeePerKw: commitFeePerKw,
fundingFeePerWeight: fundingFeePerWeight,
pushMSat: pushMSat,
flags: flags,
err: errChan,
resp: respChan,
chainHash: chainHash,
nodeID: theirID,
nodeAddr: theirAddr,
fundingAmount: ourFundAmt,
capacity: capacity,
commitFeePerKw: commitFeePerKw,
fundingFeePerVSize: fundingFeePerVSize,
pushMSat: pushMSat,
flags: flags,
err: errChan,
resp: respChan,
}
return <-respChan, <-errChan
@ -516,10 +516,10 @@ func (l *LightningWallet) handleFundingReserveRequest(req *initFundingReserveMsg
// don't need to perform any coin selection. Otherwise, attempt to
// obtain enough coins to meet the required funding amount.
if req.fundingAmount != 0 {
// Coin selection is done on the basis of sat-per-weight, we'll
// use the passed sat/byte passed in to perform coin selection.
// Coin selection is done on the basis of sat-per-vbyte, we'll
// use the passed sat/vbyte passed in to perform coin selection.
err := l.selectCoinsAndChange(
req.fundingFeePerWeight, req.fundingAmount,
req.fundingFeePerVSize, req.fundingAmount,
reservation.ourContribution,
)
if err != nil {
@ -1284,7 +1284,7 @@ func (l *LightningWallet) handleSingleFunderSigs(req *addSingleFunderSigsMsg) {
// within the passed contribution's inputs. If necessary, a change address will
// also be generated.
// TODO(roasbeef): remove hardcoded fees and req'd confs for outputs.
func (l *LightningWallet) selectCoinsAndChange(feeRatePerWeight btcutil.Amount,
func (l *LightningWallet) selectCoinsAndChange(feeRate SatPerVByte,
amt btcutil.Amount, contribution *ChannelContribution) error {
// We hold the coin select mutex while querying for outputs, and
@ -1294,7 +1294,7 @@ func (l *LightningWallet) selectCoinsAndChange(feeRatePerWeight btcutil.Amount,
defer l.coinSelectMtx.Unlock()
walletLog.Infof("Performing funding tx coin selection using %v "+
"sat/weight as fee rate", int64(feeRatePerWeight))
"sat/vbyte as fee rate", int64(feeRate))
// Find all unlocked unspent witness outputs with greater than 1
// confirmation.
@ -1307,7 +1307,7 @@ func (l *LightningWallet) selectCoinsAndChange(feeRatePerWeight btcutil.Amount,
// Perform coin selection over our available, unlocked unspent outputs
// in order to find enough coins to meet the funding amount
// requirements.
selectedCoins, changeAmt, err := coinSelect(feeRatePerWeight, amt, coins)
selectedCoins, changeAmt, err := coinSelect(feeRate, amt, coins)
if err != nil {
return err
}
@ -1415,9 +1415,9 @@ func selectInputs(amt btcutil.Amount, coins []*Utxo) (btcutil.Amount, []*Utxo, e
// coinSelect attempts to select a sufficient amount of coins, including a
// change output to fund amt satoshis, adhering to the specified fee rate. The
// specified fee rate should be expressed in sat/byte for coin selection to
// specified fee rate should be expressed in sat/vbyte for coin selection to
// function properly.
func coinSelect(feeRatePerWeight, amt btcutil.Amount,
func coinSelect(feeRate SatPerVByte, amt btcutil.Amount,
coins []*Utxo) ([]*Utxo, btcutil.Amount, error) {
amtNeeded := amt
@ -1461,9 +1461,7 @@ func coinSelect(feeRatePerWeight, amt btcutil.Amount,
// amount isn't enough to pay fees, then increase the requested
// coin amount by the estimate required fee, performing another
// round of coin selection.
requiredFee := btcutil.Amount(
uint64(weightEstimate.Weight()) * uint64(feeRatePerWeight),
)
requiredFee := feeRate.FeeForVSize(int64(weightEstimate.VSize()))
if overShootAmt < requiredFee {
amtNeeded = amt + requiredFee
continue

View File

@ -68,6 +68,9 @@ type OpenChannel struct {
// FeePerKiloWeight is the initial fee rate that the initiator suggests
// for both commitment transaction. This value is expressed in sat per
// kilo-weight.
//
// TODO(halseth): make SatPerKWeight when fee estimation is in own
// package. Currently this will cause an import cycle.
FeePerKiloWeight uint32
// CsvDelay is the number of blocks to use for the relative time lock

View File

@ -1,6 +1,8 @@
package lnwire
import "io"
import (
"io"
)
// UpdateFee is the message the channel initiator sends to the other peer if
// the channel commitment fee needs to be updated.
@ -10,6 +12,9 @@ type UpdateFee struct {
// FeePerKw is the fee-per-kw on commit transactions that the sender of
// this message wants to use for this channel.
//
// TODO(halseth): make SatPerKWeight when fee estimation is moved to
// own package. Currently this will cause an import cycle.
FeePerKw uint32
}

View File

@ -229,7 +229,7 @@ func (m *mockWalletController) FetchRootKey() (*btcec.PrivateKey, error) {
return m.rootKey, nil
}
func (*mockWalletController) SendOutputs(outputs []*wire.TxOut,
_ btcutil.Amount) (*chainhash.Hash, error) {
_ lnwallet.SatPerVByte) (*chainhash.Hash, error) {
return nil, nil
}

View File

@ -1415,7 +1415,7 @@ func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) (*channelCloser, e
// In order to begin fee negotiations, we'll first compute our
// target ideal fee-per-kw. We'll set this to a lax value, as
// we weren't the ones that initiated the channel closure.
satPerWight, err := p.server.cc.feeEstimator.EstimateFeePerWeight(6)
feePerVSize, err := p.server.cc.feeEstimator.EstimateFeePerVSize(6)
if err != nil {
return nil, fmt.Errorf("unable to query fee "+
"estimator: %v", err)
@ -1424,7 +1424,7 @@ func (p *peer) fetchActiveChanCloser(chanID lnwire.ChannelID) (*channelCloser, e
// We'll then convert the sat per weight to sat per k/w as this
// is the native unit used within the protocol when dealing
// with fees.
targetFeePerKw := satPerWight * 1000
targetFeePerKw := feePerVSize.FeePerKWeight()
_, startingHeight, err := p.server.cc.chainIO.GetBestBlock()
if err != nil {

View File

@ -139,7 +139,7 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) {
CloseType: htlcswitch.CloseRegular,
ChanPoint: initiatorChan.ChannelPoint(),
Updates: updateChan,
TargetFeePerKw: 12000,
TargetFeePerKw: 12500,
Err: errChan,
}
initiator.localCloseChanReqs <- closeCommand
@ -170,11 +170,12 @@ func TestPeerChannelClosureAcceptFeeInitiator(t *testing.T) {
}
estimator := lnwallet.StaticFeeEstimator{FeeRate: 50}
feeRate, err := estimator.EstimateFeePerWeight(1)
feeRate, err := estimator.EstimateFeePerVSize(1)
if err != nil {
t.Fatalf("unable to query fee estimator: %v", err)
}
fee := btcutil.Amount(responderChan.CalcFee(uint64(feeRate * 1000)))
feePerKw := feeRate.FeePerKWeight()
fee := responderChan.CalcFee(feePerKw)
closeSig, _, _, err := responderChan.CreateCloseProposal(fee,
dummyDeliveryScript, initiatorDeliveryScript)
if err != nil {
@ -428,7 +429,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
CloseType: htlcswitch.CloseRegular,
ChanPoint: initiatorChan.ChannelPoint(),
Updates: updateChan,
TargetFeePerKw: 12000,
TargetFeePerKw: 12500,
Err: errChan,
}
@ -460,12 +461,12 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
}
estimator := lnwallet.StaticFeeEstimator{FeeRate: 50}
initiatorIdealFeeRate, err := estimator.EstimateFeePerWeight(1)
initiatorIdealFeeRate, err := estimator.EstimateFeePerVSize(1)
if err != nil {
t.Fatalf("unable to query fee estimator: %v", err)
}
initiatorIdealFee := responderChan.CalcFee(
uint64(initiatorIdealFeeRate * 1000),
initiatorIdealFeeRate.FeePerKWeight(),
)
increasedFee := btcutil.Amount(float64(initiatorIdealFee) * 2.5)
closeSig, _, _, err := responderChan.CreateCloseProposal(
@ -499,7 +500,7 @@ func TestPeerChannelClosureFeeNegotiationsInitiator(t *testing.T) {
if !ok {
t.Fatalf("expected ClosingSigned message, got %T", msg)
}
if uint64(closingSignedMsg.FeeSatoshis) != initiatorIdealFee {
if closingSignedMsg.FeeSatoshis != initiatorIdealFee {
t.Fatalf("expected ClosingSigned fee to be %v, instead got %v",
initiatorIdealFee, closingSignedMsg.FeeSatoshis)
}

View File

@ -85,7 +85,7 @@ func (c *chanController) OpenChannel(target *btcec.PublicKey,
// With the connection established, we'll now establish our connection
// to the target peer, waiting for the first update before we exit.
feePerWeight, err := c.server.cc.feeEstimator.EstimateFeePerWeight(3)
feePerVSize, err := c.server.cc.feeEstimator.EstimateFeePerVSize(3)
if err != nil {
return err
}
@ -94,7 +94,7 @@ func (c *chanController) OpenChannel(target *btcec.PublicKey,
minHtlc := lnwire.NewMSatFromSatoshis(1)
updateStream, errChan := c.server.OpenChannel(target, amt, 0,
minHtlc, feePerWeight, false)
minHtlc, feePerVSize, false)
select {
case err := <-errChan:

View File

@ -344,28 +344,28 @@ func addrPairsToOutputs(addrPairs map[string]int64) ([]*wire.TxOut, error) {
// more addresses specified in the passed payment map. The payment map maps an
// address to a specified output value to be sent to that address.
func (r *rpcServer) sendCoinsOnChain(paymentMap map[string]int64,
feePerByte btcutil.Amount) (*chainhash.Hash, error) {
feeRate lnwallet.SatPerVByte) (*chainhash.Hash, error) {
outputs, err := addrPairsToOutputs(paymentMap)
if err != nil {
return nil, err
}
return r.server.cc.wallet.SendOutputs(outputs, feePerByte)
return r.server.cc.wallet.SendOutputs(outputs, feeRate)
}
// determineFeePerByte will determine the fee in sat/byte that should be paid
// determineFeePerVSize will determine the fee in sat/vbyte that should be paid
// given an estimator, a confirmation target, and a manual value for sat/byte.
// A value is chosen based on the two free parameters as one, or both of them
// can be zero.
func determineFeePerByte(feeEstimator lnwallet.FeeEstimator, targetConf int32,
satPerByte int64) (btcutil.Amount, error) {
func determineFeePerVSize(feeEstimator lnwallet.FeeEstimator, targetConf int32,
feePerByte int64) (lnwallet.SatPerVByte, error) {
switch {
// If the target number of confirmations is set, then we'll use that to
// consult our fee estimator for an adequate fee.
case targetConf != 0:
satPerByte, err := feeEstimator.EstimateFeePerByte(
feePerVSize, err := feeEstimator.EstimateFeePerVSize(
uint32(targetConf),
)
if err != nil {
@ -373,22 +373,22 @@ func determineFeePerByte(feeEstimator lnwallet.FeeEstimator, targetConf int32,
"estimator: %v", err)
}
return btcutil.Amount(satPerByte), nil
return feePerVSize, nil
// If a manual sat/byte fee rate is set, then we'll use that directly.
case satPerByte != 0:
return btcutil.Amount(satPerByte), nil
case feePerByte != 0:
return lnwallet.SatPerVByte(feePerByte), nil
// Otherwise, we'll attempt a relaxed confirmation target for the
// transaction
default:
satPerByte, err := feeEstimator.EstimateFeePerByte(6)
feePerVSize, err := feeEstimator.EstimateFeePerVSize(6)
if err != nil {
return 0, fmt.Errorf("unable to query fee "+
"estimator: %v", err)
}
return satPerByte, nil
return feePerVSize, nil
}
}
@ -399,18 +399,18 @@ func (r *rpcServer) SendCoins(ctx context.Context,
// Based on the passed fee related parameters, we'll determine an
// appropriate fee rate for this transaction.
feePerByte, err := determineFeePerByte(
feeRate, err := determineFeePerVSize(
r.server.cc.feeEstimator, in.TargetConf, in.SatPerByte,
)
if err != nil {
return nil, err
}
rpcsLog.Infof("[sendcoins] addr=%v, amt=%v, sat/byte=%v",
in.Addr, btcutil.Amount(in.Amount), int64(feePerByte))
rpcsLog.Infof("[sendcoins] addr=%v, amt=%v, sat/vbyte=%v",
in.Addr, btcutil.Amount(in.Amount), int64(feeRate))
paymentMap := map[string]int64{in.Addr: in.Amount}
txid, err := r.sendCoinsOnChain(paymentMap, feePerByte)
txid, err := r.sendCoinsOnChain(paymentMap, feeRate)
if err != nil {
return nil, err
}
@ -427,17 +427,17 @@ func (r *rpcServer) SendMany(ctx context.Context,
// Based on the passed fee related parameters, we'll determine an
// approriate fee rate for this transaction.
feePerByte, err := determineFeePerByte(
feeRate, err := determineFeePerVSize(
r.server.cc.feeEstimator, in.TargetConf, in.SatPerByte,
)
if err != nil {
return nil, err
}
rpcsLog.Infof("[sendmany] outputs=%v, sat/byte=%v",
spew.Sdump(in.AddrToAmount), int64(feePerByte))
rpcsLog.Infof("[sendmany] outputs=%v, sat/vbyte=%v",
spew.Sdump(in.AddrToAmount), int64(feeRate))
txid, err := r.sendCoinsOnChain(in.AddrToAmount, feePerByte)
txid, err := r.sendCoinsOnChain(in.AddrToAmount, feeRate)
if err != nil {
return nil, err
}
@ -742,15 +742,15 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
// Based on the passed fee related parameters, we'll determine an
// appropriate fee rate for the funding transaction.
feePerByte, err := determineFeePerByte(
feeRate, err := determineFeePerVSize(
r.server.cc.feeEstimator, in.TargetConf, in.SatPerByte,
)
if err != nil {
return err
}
rpcsLog.Debugf("[openchannel]: using fee of %v sat/byte for funding "+
"tx", int64(feePerByte))
rpcsLog.Debugf("[openchannel]: using fee of %v sat/vbyte for funding "+
"tx", int64(feeRate))
// Instruct the server to trigger the necessary events to attempt to
// open a new channel. A stream is returned in place, this stream will
@ -758,7 +758,7 @@ func (r *rpcServer) OpenChannel(in *lnrpc.OpenChannelRequest,
updateChan, errChan := r.server.OpenChannel(
nodePubKey, localFundingAmt,
lnwire.NewMSatFromSatoshis(remoteInitialBalance),
minHtlc, feePerByte, in.Private,
minHtlc, feeRate, in.Private,
)
var outpoint wire.OutPoint
@ -864,20 +864,20 @@ func (r *rpcServer) OpenChannelSync(ctx context.Context,
// Based on the passed fee related parameters, we'll determine an
// appropriate fee rate for the funding transaction.
feePerByte, err := determineFeePerByte(
feeRate, err := determineFeePerVSize(
r.server.cc.feeEstimator, in.TargetConf, in.SatPerByte,
)
if err != nil {
return nil, err
}
rpcsLog.Tracef("[openchannel] target sat/byte for funding tx: %v",
int64(feePerByte))
rpcsLog.Tracef("[openchannel] target sat/vbyte for funding tx: %v",
int64(feeRate))
updateChan, errChan := r.server.OpenChannel(
nodepubKey, localFundingAmt,
lnwire.NewMSatFromSatoshis(remoteInitialBalance),
minHtlc, feePerByte, in.Private,
minHtlc, feeRate, in.Private,
)
select {
@ -1045,24 +1045,20 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
// Based on the passed fee related parameters, we'll determine
// an appropriate fee rate for the cooperative closure
// transaction.
feePerByte, err := determineFeePerByte(
feeRate, err := determineFeePerVSize(
r.server.cc.feeEstimator, in.TargetConf, in.SatPerByte,
)
if err != nil {
return err
}
rpcsLog.Debugf("Target sat/byte for closing transaction: %v",
int64(feePerByte))
rpcsLog.Debugf("Target sat/vbyte for closing transaction: %v",
int64(feeRate))
// When crating commitment transaction, or closure
// transactions, we typically deal in fees per-kw, so we'll
// convert now before passing the close request to the switch.
feePerWeight := (feePerByte / blockchain.WitnessScaleFactor)
if feePerWeight == 0 {
if feeRate == 0 {
// If the fee rate returned isn't usable, then we'll
// fall back to an lax fee estimate.
feePerWeight, err = r.server.cc.feeEstimator.EstimateFeePerWeight(6)
feeRate, err = r.server.cc.feeEstimator.EstimateFeePerVSize(6)
if err != nil {
return err
}
@ -1072,7 +1068,7 @@ func (r *rpcServer) CloseChannel(in *lnrpc.CloseChannelRequest,
// cooperative channel closure. So we'll forward the request to
// the htlc switch which will handle the negotiation and
// broadcast details.
feePerKw := feePerWeight * 1000
feePerKw := feeRate.FeePerKWeight()
updateChan, errChan = r.server.htlcSwitch.CloseLink(chanPoint,
htlcswitch.CloseRegular, feePerKw)
}

View File

@ -25,7 +25,6 @@ import (
"github.com/lightningnetwork/lnd/lnwallet"
"github.com/lightningnetwork/lnd/lnwire"
"github.com/lightningnetwork/lnd/routing"
"github.com/roasbeef/btcd/blockchain"
"github.com/roasbeef/btcd/btcec"
"github.com/roasbeef/btcd/chaincfg/chainhash"
"github.com/roasbeef/btcd/connmgr"
@ -1651,7 +1650,7 @@ type openChanReq struct {
pushAmt lnwire.MilliSatoshi
fundingFeePerWeight btcutil.Amount
fundingFeePerVSize lnwallet.SatPerVByte
private bool
@ -1779,7 +1778,7 @@ func (s *server) DisconnectPeer(pubKey *btcec.PublicKey) error {
func (s *server) OpenChannel(nodeKey *btcec.PublicKey,
localAmt btcutil.Amount, pushAmt lnwire.MilliSatoshi,
minHtlc lnwire.MilliSatoshi,
fundingFeePerByte btcutil.Amount,
fundingFeePerVSize lnwallet.SatPerVByte,
private bool) (chan *lnrpc.OpenStatusUpdate, chan error) {
updateChan := make(chan *lnrpc.OpenStatusUpdate, 1)
@ -1811,15 +1810,11 @@ func (s *server) OpenChannel(nodeKey *btcec.PublicKey,
return updateChan, errChan
}
// We'll scale the sat/byte set as the fee rate to sat/weight as this
// is what's used internally when deciding upon coin selection.
fundingFeePerWeight := fundingFeePerByte / blockchain.WitnessScaleFactor
// If the fee rate wasn't high enough to cleanly convert to weight,
// then we'll use a default confirmation target.
if fundingFeePerWeight == 0 {
// If the fee rate wasn't specified, then we'll use a default
// confirmation target.
if fundingFeePerVSize == 0 {
estimator := s.cc.feeEstimator
fundingFeePerWeight, err = estimator.EstimateFeePerWeight(6)
fundingFeePerVSize, err = estimator.EstimateFeePerVSize(6)
if err != nil {
errChan <- err
return updateChan, errChan
@ -1831,15 +1826,15 @@ func (s *server) OpenChannel(nodeKey *btcec.PublicKey,
// instead of blocking on this request which is exported as a
// synchronous request to the outside world.
req := &openChanReq{
targetPubkey: nodeKey,
chainHash: *activeNetParams.GenesisHash,
localFundingAmt: localAmt,
fundingFeePerWeight: fundingFeePerWeight,
pushAmt: pushAmt,
private: private,
minHtlc: minHtlc,
updates: updateChan,
err: errChan,
targetPubkey: nodeKey,
chainHash: *activeNetParams.GenesisHash,
localFundingAmt: localAmt,
fundingFeePerVSize: fundingFeePerVSize,
pushAmt: pushAmt,
private: private,
minHtlc: minHtlc,
updates: updateChan,
err: errChan,
}
// TODO(roasbeef): pass in chan that's closed if/when funding succeeds

View File

@ -138,19 +138,19 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
}
estimator := &lnwallet.StaticFeeEstimator{FeeRate: 50}
feePerWeight, err := estimator.EstimateFeePerWeight(1)
feePerVSize, err := estimator.EstimateFeePerVSize(1)
if err != nil {
return nil, nil, nil, nil, err
}
feePerKw := feePerWeight * 1000
feePerKw := feePerVSize.FeePerKWeight()
// TODO(roasbeef): need to factor in commit fee?
aliceCommit := channeldb.ChannelCommitment{
CommitHeight: 0,
LocalBalance: lnwire.NewMSatFromSatoshis(channelBal),
RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal),
FeePerKw: feePerKw,
CommitFee: 8688,
FeePerKw: btcutil.Amount(feePerKw),
CommitFee: feePerKw.FeeForWeight(lnwallet.CommitWeight),
CommitTx: aliceCommitTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
}
@ -158,8 +158,8 @@ func createTestPeer(notifier chainntnfs.ChainNotifier,
CommitHeight: 0,
LocalBalance: lnwire.NewMSatFromSatoshis(channelBal),
RemoteBalance: lnwire.NewMSatFromSatoshis(channelBal),
FeePerKw: feePerKw,
CommitFee: 8688,
FeePerKw: btcutil.Amount(feePerKw),
CommitFee: feePerKw.FeeForWeight(lnwallet.CommitWeight),
CommitTx: bobCommitTx,
CommitSig: bytes.Repeat([]byte{1}, 71),
}

View File

@ -994,15 +994,15 @@ func (u *utxoNursery) createSweepTx(kgtnOutputs []kidOutput,
utxnLog.Infof("Creating sweep transaction for %v CSV inputs, %v CLTV "+
"inputs", len(csvOutputs), len(cltvOutputs))
txWeight := uint64(weightEstimate.Weight())
return u.populateSweepTx(txWeight, classHeight, csvOutputs, cltvOutputs)
txVSize := int64(weightEstimate.VSize())
return u.populateSweepTx(txVSize, classHeight, csvOutputs, cltvOutputs)
}
// populateSweepTx populate the final sweeping transaction with all witnesses
// in place for all inputs using the provided txn fee. The created transaction
// has a single output sending all the funds back to the source wallet, after
// accounting for the fee estimate.
func (u *utxoNursery) populateSweepTx(txWeight uint64, classHeight uint32,
func (u *utxoNursery) populateSweepTx(txVSize int64, classHeight uint32,
csvInputs []CsvSpendableOutput,
cltvInputs []SpendableOutput) (*wire.MsgTx, error) {
@ -1022,11 +1022,11 @@ func (u *utxoNursery) populateSweepTx(txWeight uint64, classHeight uint32,
}
// Using the txn weight estimate, compute the required txn fee.
feePerWeight, err := u.cfg.Estimator.EstimateFeePerWeight(6)
feePerVSize, err := u.cfg.Estimator.EstimateFeePerVSize(6)
if err != nil {
return nil, err
}
txFee := btcutil.Amount(txWeight) * feePerWeight
txFee := feePerVSize.FeeForVSize(txVSize)
// Sweep as much possible, after subtracting txn fees.
sweepAmt := int64(totalSum - txFee)