mirror of
https://github.com/lightninglabs/pool.git
synced 2026-08-13 12:33:04 +02:00
multi: calculate locked value based on account version
This commit is contained in:
parent
d109f913c4
commit
701b999463
7 changed files with 70 additions and 52 deletions
|
|
@ -8,6 +8,7 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/lightninglabs/pool"
|
||||
"github.com/lightninglabs/pool/account"
|
||||
"github.com/lightninglabs/pool/auctioneer"
|
||||
"github.com/lightninglabs/pool/clientdb"
|
||||
"github.com/lightninglabs/pool/order"
|
||||
|
|
@ -123,6 +124,7 @@ func dumpOrders(ctx *cli.Context) error {
|
|||
UnitsUnfulfilled: uint32(dbDetails.UnitsUnfulfilled),
|
||||
ReservedValueSat: uint64(dbOrder.ReservedValue(
|
||||
terms.NewLinearFeeSchedule(0, 0),
|
||||
account.VersionInitialNoVersion,
|
||||
)),
|
||||
CreationTimestampNs: uint64(0),
|
||||
MinUnitsMatch: uint32(
|
||||
|
|
|
|||
|
|
@ -85,7 +85,9 @@ func (m *marshaler) MarshallAccountsWithAvailableBalance(ctx context.Context,
|
|||
continue
|
||||
}
|
||||
|
||||
debitAmt += o.ReservedValue(auctionFeeSchedule)
|
||||
debitAmt += o.ReservedValue(
|
||||
auctionFeeSchedule, acct.Version,
|
||||
)
|
||||
}
|
||||
|
||||
accountDebits[acctKey] = debitAmt
|
||||
|
|
|
|||
|
|
@ -276,12 +276,9 @@ type Order interface {
|
|||
|
||||
// ReservedValue returns the maximum value that could be deducted from
|
||||
// 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
|
||||
// reserved to ensure the trader can afford it.
|
||||
ReservedValue(feeSchedule terms.FeeSchedule,
|
||||
accountVersion account.Version) btcutil.Amount
|
||||
}
|
||||
|
||||
// Kit stores all the common fields that are used to express the decision to
|
||||
|
|
@ -457,15 +454,12 @@ 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. This always uses the worst-case fee estimation using the version
|
||||
// 0 p2wsh script witness size calculation for the account spend.
|
||||
// being paid.
|
||||
//
|
||||
// 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 {
|
||||
func reservedValue(o Order, perMatchDelta func(btcutil.Amount) btcutil.Amount,
|
||||
accountVersion account.Version) btcutil.Amount {
|
||||
|
||||
// If this order is in a state where it cannot be matched, return 0.
|
||||
if o.Details().State.Archived() {
|
||||
|
|
@ -498,12 +492,10 @@ func reservedValue(o Order,
|
|||
// Subtract the worst case chain fee from the balance.
|
||||
maxFeeRate := o.Details().MaxBatchFeeRate
|
||||
balanceDelta -= maxNumMatches * EstimateTraderFee(
|
||||
1, maxFeeRate, account.VersionInitialNoVersion,
|
||||
1, maxFeeRate, accountVersion,
|
||||
)
|
||||
if rem > 0 {
|
||||
balanceDelta -= EstimateTraderFee(
|
||||
1, maxFeeRate, account.VersionInitialNoVersion,
|
||||
)
|
||||
balanceDelta -= EstimateTraderFee(1, maxFeeRate, accountVersion)
|
||||
}
|
||||
|
||||
// If the balance delta is negative, meaning this order will decrease
|
||||
|
|
@ -519,7 +511,9 @@ func reservedValue(o Order,
|
|||
|
||||
// ReservedValue returns the maximum value that could be deducted from a single
|
||||
// account if the ask is matched under the worst case fee conditions.
|
||||
func (a *Ask) ReservedValue(feeSchedule terms.FeeSchedule) btcutil.Amount {
|
||||
func (a *Ask) ReservedValue(feeSchedule terms.FeeSchedule,
|
||||
accountVersion account.Version) 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.
|
||||
clearingPrice := FixedRatePremium(a.FixedRate)
|
||||
|
|
@ -529,7 +523,7 @@ func (a *Ask) ReservedValue(feeSchedule terms.FeeSchedule) btcutil.Amount {
|
|||
feeSchedule, clearingPrice, amt, a.LeaseDuration,
|
||||
)
|
||||
return delta
|
||||
})
|
||||
}, accountVersion)
|
||||
}
|
||||
|
||||
// NodeTier an enum-like variable that presents which "tier" a node is in. A
|
||||
|
|
@ -693,7 +687,9 @@ func (b *Bid) Digest() ([hashSize]byte, error) {
|
|||
|
||||
// ReservedValue returns the maximum value that could be deducted from a single
|
||||
// account if the bid is matched under the worst case fee conditions.
|
||||
func (b *Bid) ReservedValue(feeSchedule terms.FeeSchedule) btcutil.Amount {
|
||||
func (b *Bid) ReservedValue(feeSchedule terms.FeeSchedule,
|
||||
accountVersion account.Version) 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
|
||||
// bidder.
|
||||
|
|
@ -705,7 +701,7 @@ func (b *Bid) ReservedValue(feeSchedule terms.FeeSchedule) btcutil.Amount {
|
|||
b.LeaseDuration,
|
||||
)
|
||||
return delta
|
||||
})
|
||||
}, accountVersion)
|
||||
}
|
||||
|
||||
// ValidateSelfChanBalance makes sure that all conditions to use the
|
||||
|
|
|
|||
|
|
@ -15,11 +15,11 @@ func TestOrderReservedValue(t *testing.T) {
|
|||
|
||||
simpleFeeSchedule := terms.NewLinearFeeSchedule(1, 100)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
order Order
|
||||
accountVersion account.Version
|
||||
}{
|
||||
type testCase struct {
|
||||
name string
|
||||
order Order
|
||||
}
|
||||
testCases := []*testCase{
|
||||
{
|
||||
name: "bid 1 unit",
|
||||
order: &Bid{
|
||||
|
|
@ -204,9 +204,7 @@ func TestOrderReservedValue(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for i, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
runTestCase := func(t *testing.T, tc *testCase, v account.Version) {
|
||||
// Count the worst case we will expect.
|
||||
var expValue btcutil.Amount
|
||||
|
||||
|
|
@ -238,7 +236,7 @@ func TestOrderReservedValue(t *testing.T) {
|
|||
LumpSumPremium(amt, o.LeaseDuration)
|
||||
exeFee := executionFee(amt, simpleFeeSchedule)
|
||||
chainFee := EstimateTraderFee(
|
||||
1, o.MaxBatchFeeRate, tc.accountVersion,
|
||||
1, o.MaxBatchFeeRate, v,
|
||||
)
|
||||
|
||||
// For bids the lump sum, chain fee and the
|
||||
|
|
@ -276,7 +274,7 @@ func TestOrderReservedValue(t *testing.T) {
|
|||
LumpSumPremium(amt, 144)
|
||||
exeFee := executionFee(amt, simpleFeeSchedule)
|
||||
chainFee := EstimateTraderFee(
|
||||
1, o.MaxBatchFeeRate, tc.accountVersion,
|
||||
1, o.MaxBatchFeeRate, v,
|
||||
)
|
||||
|
||||
// For asks the amount itself, the chain fee
|
||||
|
|
@ -294,18 +292,25 @@ func TestOrderReservedValue(t *testing.T) {
|
|||
expValue = 0
|
||||
}
|
||||
|
||||
// Check the value returned.
|
||||
i := i
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
val := tc.order.ReservedValue(simpleFeeSchedule)
|
||||
if val < 0 {
|
||||
t.Fatalf("reserved value cannot be "+
|
||||
"negative: %v", val)
|
||||
}
|
||||
if val != expValue {
|
||||
t.Fatalf("test #%v: expected reserved value "+
|
||||
"%v, got '%v'", i, expValue, val)
|
||||
}
|
||||
val := tc.order.ReservedValue(simpleFeeSchedule, v)
|
||||
if val < 0 {
|
||||
t.Fatalf("reserved value cannot be "+
|
||||
"negative: %v", val)
|
||||
}
|
||||
if val != expValue {
|
||||
t.Fatalf("%s: expected reserved value "+
|
||||
"%v, got '%v'", tc.name, expValue, val)
|
||||
}
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name+"/version_0", func(t *testing.T) {
|
||||
runTestCase(t, tc, account.VersionInitialNoVersion)
|
||||
})
|
||||
t.Run(tc.name+"/version_1", func(t *testing.T) {
|
||||
runTestCase(t, tc, account.VersionTaprootEnabled)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -317,7 +317,7 @@ func (m *manager) validateOrder(order Order, acct *account.Account,
|
|||
var acctKey [33]byte
|
||||
copy(acctKey[:], acct.TraderKey.PubKey.SerializeCompressed())
|
||||
feeSchedule := terms.FeeSchedule()
|
||||
reserved := order.ReservedValue(feeSchedule)
|
||||
reserved := order.ReservedValue(feeSchedule, acct.Version)
|
||||
for _, o := range dbOrders {
|
||||
// Only tally the reserved balance if this order was submitted
|
||||
// by this account.
|
||||
|
|
@ -325,7 +325,7 @@ func (m *manager) validateOrder(order Order, acct *account.Account,
|
|||
continue
|
||||
}
|
||||
|
||||
reserved += o.ReservedValue(feeSchedule)
|
||||
reserved += o.ReservedValue(feeSchedule, acct.Version)
|
||||
}
|
||||
|
||||
if acct.Value < reserved {
|
||||
|
|
|
|||
|
|
@ -81,17 +81,17 @@ func (mr *MockOrderMockRecorder) Nonce() *gomock.Call {
|
|||
}
|
||||
|
||||
// ReservedValue mocks base method.
|
||||
func (m *MockOrder) ReservedValue(feeSchedule terms.FeeSchedule) btcutil.Amount {
|
||||
func (m *MockOrder) ReservedValue(feeSchedule terms.FeeSchedule, accountVersion account.Version) btcutil.Amount {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ReservedValue", feeSchedule)
|
||||
ret := m.ctrl.Call(m, "ReservedValue", feeSchedule, accountVersion)
|
||||
ret0, _ := ret[0].(btcutil.Amount)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// ReservedValue indicates an expected call of ReservedValue.
|
||||
func (mr *MockOrderMockRecorder) ReservedValue(feeSchedule interface{}) *gomock.Call {
|
||||
func (mr *MockOrderMockRecorder) ReservedValue(feeSchedule, accountVersion interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReservedValue", reflect.TypeOf((*MockOrder)(nil).ReservedValue), feeSchedule)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ReservedValue", reflect.TypeOf((*MockOrder)(nil).ReservedValue), feeSchedule, accountVersion)
|
||||
}
|
||||
|
||||
// Type mocks base method.
|
||||
|
|
|
|||
19
rpcserver.go
19
rpcserver.go
|
|
@ -1564,6 +1564,19 @@ func (s *rpcServer) ListOrders(ctx context.Context,
|
|||
feeSchedule = auctioneerTerms.FeeSchedule()
|
||||
}
|
||||
|
||||
// We also need a map of all account versions for the locked value
|
||||
// estimation below.
|
||||
accountVersions := make(map[[33]byte]account.Version)
|
||||
allAccounts, err := s.server.db.Accounts()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error querying accounts: %v", err)
|
||||
}
|
||||
for _, acct := range allAccounts {
|
||||
var rawKey [33]byte
|
||||
copy(rawKey[:], acct.TraderKey.PubKey.SerializeCompressed())
|
||||
accountVersions[rawKey] = acct.Version
|
||||
}
|
||||
|
||||
// The RPC is split by order type so we have to separate them now.
|
||||
asks := make([]*poolrpc.Ask, 0, len(creationEvents))
|
||||
bids := make([]*poolrpc.Bid, 0, len(creationEvents))
|
||||
|
|
@ -1616,9 +1629,9 @@ func (s *rpcServer) ListOrders(ctx context.Context,
|
|||
State: orderState,
|
||||
Units: uint32(dbDetails.Units),
|
||||
UnitsUnfulfilled: uint32(dbDetails.UnitsUnfulfilled),
|
||||
ReservedValueSat: uint64(
|
||||
dbOrder.ReservedValue(feeSchedule),
|
||||
),
|
||||
ReservedValueSat: uint64(dbOrder.ReservedValue(
|
||||
feeSchedule, accountVersions[dbDetails.AcctKey],
|
||||
)),
|
||||
CreationTimestampNs: uint64(evt.Timestamp().UnixNano()),
|
||||
Events: rpcEvents,
|
||||
MinUnitsMatch: uint32(dbOrder.Details().MinUnitsMatch),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue