order+rpcserver: use account version in fee estimation

This commit is contained in:
Oliver Gugger 2022-06-30 22:17:32 +02:00
parent a6c18a9bb1
commit 1e7c00a586
No known key found for this signature in database
GPG key ID: 8E4256593F177720
5 changed files with 54 additions and 18 deletions

View file

@ -230,7 +230,7 @@ func (v *batchVerifier) Verify(batch *Batch, bestHeight uint32) error {
// Now that we know how many channels were created from the
// given account, let's also account for the chain fees.
tally.ChainFees(batch.BatchTxFeeRate)
tally.ChainFees(batch.BatchTxFeeRate, acct.Version)
// Even if the account output is dust, we should arrive at the
// same number with our tally as the server.

View file

@ -275,8 +275,12 @@ type Order interface {
Digest() ([hashSize]byte, error)
// ReservedValue returns the maximum value that could be deducted from
// the account if the order is is matched, and therefore has to be
// reserved to ensure the trader can afford it.
// the account if the order is matched, and therefore has to be
// reserved to ensure the trader can afford it. This always uses the
// worst-case fee estimation using the version 0 p2wsh script witness
// size calculation for the account spend.
//
// TODO(guggero): Update to be more precise for p2tr accounts.
ReservedValue(feeSchedule terms.FeeSchedule) btcutil.Amount
}
@ -453,10 +457,13 @@ func (a *Ask) Digest() ([hashSize]byte, error) {
// account if the given order is matched under the worst case fee conditions.
// This usually means the order is partially matched with the minimum match
// size, all in different batches, leading to maximum chain and execution fees
// being paid.
// being paid. This always uses the worst-case fee estimation using the version
// 0 p2wsh script witness size calculation for the account spend.
//
// The passed function should be set to either calculate the maker or taker
// balance delta for a single match of the given amount.
//
// TODO(guggero): Update to be more precise for p2tr accounts.
func reservedValue(o Order,
perMatchDelta func(btcutil.Amount) btcutil.Amount) btcutil.Amount {
@ -490,9 +497,13 @@ func reservedValue(o Order,
// Subtract the worst case chain fee from the balance.
maxFeeRate := o.Details().MaxBatchFeeRate
balanceDelta -= maxNumMatches * EstimateTraderFee(1, maxFeeRate)
balanceDelta -= maxNumMatches * EstimateTraderFee(
1, maxFeeRate, account.VersionInitialNoVersion,
)
if rem > 0 {
balanceDelta -= EstimateTraderFee(1, maxFeeRate)
balanceDelta -= EstimateTraderFee(
1, maxFeeRate, account.VersionInitialNoVersion,
)
}
// If the balance delta is negative, meaning this order will decrease
@ -507,7 +518,7 @@ func reservedValue(o Order,
}
// ReservedValue returns the maximum value that could be deducted from a single
// account if the ask is is matched under the worst case fee conditions.
// account if the ask is matched under the worst case fee conditions.
func (a *Ask) ReservedValue(feeSchedule terms.FeeSchedule) btcutil.Amount {
// For an ask the clearing price will be no lower than the ask's fixed
// rate, resulting in the smallest gain for the asker.
@ -681,7 +692,7 @@ func (b *Bid) Digest() ([hashSize]byte, error) {
}
// ReservedValue returns the maximum value that could be deducted from a single
// account if the bid is is matched under the worst case fee conditions.
// account if the bid is matched under the worst case fee conditions.
func (b *Bid) ReservedValue(feeSchedule terms.FeeSchedule) btcutil.Amount {
// For a bid, the final clearing price is never higher that the bid's
// fixed rate, resulting in the highest possible premium paid by the

View file

@ -4,6 +4,7 @@ import (
"testing"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/pool/account"
"github.com/lightninglabs/pool/terms"
)
@ -15,8 +16,9 @@ func TestOrderReservedValue(t *testing.T) {
simpleFeeSchedule := terms.NewLinearFeeSchedule(1, 100)
testCases := []struct {
name string
order Order
name string
order Order
accountVersion account.Version
}{
{
name: "bid 1 unit",
@ -236,7 +238,7 @@ func TestOrderReservedValue(t *testing.T) {
LumpSumPremium(amt, o.LeaseDuration)
exeFee := executionFee(amt, simpleFeeSchedule)
chainFee := EstimateTraderFee(
1, o.MaxBatchFeeRate,
1, o.MaxBatchFeeRate, tc.accountVersion,
)
// For bids the lump sum, chain fee and the
@ -274,7 +276,7 @@ func TestOrderReservedValue(t *testing.T) {
LumpSumPremium(amt, 144)
exeFee := executionFee(amt, simpleFeeSchedule)
chainFee := EstimateTraderFee(
1, o.MaxBatchFeeRate,
1, o.MaxBatchFeeRate, tc.accountVersion,
)
// For asks the amount itself, the chain fee

View file

@ -3,6 +3,7 @@ package order
import (
"github.com/btcsuite/btcd/blockchain"
"github.com/btcsuite/btcd/btcutil"
"github.com/lightninglabs/pool/account"
"github.com/lightninglabs/pool/poolscript"
"github.com/lightninglabs/pool/terms"
"github.com/lightningnetwork/lnd/input"
@ -65,8 +66,8 @@ func PerBlockPremium(amt btcutil.Amount, fixedRate uint32) float64 {
// EstimateTraderFee calculates the chain fees a trader has to pay for their
// part of a batch transaction. The more outputs a trader creates (channels),
// the higher fee they will pay.
func EstimateTraderFee(numTraderChans uint32,
feeRate chainfee.SatPerKWeight) btcutil.Amount {
func EstimateTraderFee(numTraderChans uint32, feeRate chainfee.SatPerKWeight,
accountVersion account.Version) btcutil.Amount {
var weightEstimate int64
@ -90,7 +91,13 @@ func EstimateTraderFee(numTraderChans uint32,
// Finally, we tack on the size of the witness spending the account
// outpoint.
weightEstimate += poolscript.MultiSigWitnessSize
switch accountVersion {
case account.VersionTaprootEnabled:
weightEstimate += poolscript.TaprootMultiSigWitnessSize
default:
weightEstimate += poolscript.MultiSigWitnessSize
}
return feeRate.FeeForWeight(weightEstimate)
}
@ -124,7 +131,12 @@ func NewQuote(amt, minChanAmt btcutil.Amount, rate FixedRatePremium,
exeFee := schedule.BaseFee() + schedule.ExecutionFee(amt)
maxNumMatches := amt / minChanAmt
chainFee := maxNumMatches * EstimateTraderFee(1, maxBatchFeeRate)
// For an order quote we always return the worst case fees, which means
// with a legacy account.
chainFee := maxNumMatches * EstimateTraderFee(
1, maxBatchFeeRate, account.VersionInitialNoVersion,
)
return &Quote{
TotalPremium: rate.LumpSumPremium(amt, leaseDuration),
@ -251,8 +263,12 @@ func (t *AccountTally) CalcTakerDelta(feeSchedule terms.FeeSchedule,
// ChainFees estimates the chain fees that need to be paid for the number of
// channels created for this account and subtracts that value from the ending
// balance.
func (t *AccountTally) ChainFees(feeRate chainfee.SatPerKWeight) {
chainFeesDue := EstimateTraderFee(t.NumChansCreated, feeRate)
func (t *AccountTally) ChainFees(feeRate chainfee.SatPerKWeight,
accountVersion account.Version) {
chainFeesDue := EstimateTraderFee(
t.NumChansCreated, feeRate, accountVersion,
)
t.EndingBalance -= chainFeesDue
}

View file

@ -2270,8 +2270,15 @@ func (s *rpcServer) prepareLeasesResponse(ctx context.Context,
// Estimate the chain fees paid for the number of
// channels created in this batch and tally them.
//
// TODO(guggero): This is just an approximation! We
// should properly calculate the fees _per account_ as
// that's what we do on the server side. Then we can
// also take a look at the actual account version at the
// time of the batch.
chainFee := order.EstimateTraderFee(
uint32(numChans), batch.BatchTxFeeRate,
account.VersionInitialNoVersion,
)
// We'll need to compute the chain fee paid for each