mirror of
https://github.com/lightninglabs/pool.git
synced 2026-08-13 12:33:04 +02:00
The auctioneer will now provide a height hint along with every proposed batch it sends to traders. This height hint is then used by each trader to base off their absolute lease expiration height, such that they can agree on the same value and avoid any divergences due to block propagation discrepancies between them.
199 lines
5.4 KiB
Go
199 lines
5.4 KiB
Go
package order
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/lightninglabs/pool/account"
|
|
"github.com/lightninglabs/pool/auctioneerrpc"
|
|
"github.com/lightningnetwork/lnd/keychain"
|
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
|
)
|
|
|
|
// TestBatchStorer makes sure a batch is prepared correctly for serialization by
|
|
// the batch storer.
|
|
func TestBatchStorer(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var (
|
|
storeMock = newMockStore()
|
|
storer = &batchStorer{
|
|
orderStore: storeMock,
|
|
getAccount: storeMock.getAccount,
|
|
}
|
|
batchID BatchID
|
|
acctIDBig [33]byte
|
|
acctIDSmall [33]byte
|
|
)
|
|
copy(batchID[:], startBatchKey.SerializeCompressed())
|
|
copy(acctIDBig[:], acctKeyBig.SerializeCompressed())
|
|
copy(acctIDSmall[:], acctKeySmall.SerializeCompressed())
|
|
|
|
// We'll create two accounts: A smaller one that has one ask for 4 units
|
|
// that will be completely used up. Then a larger account that has two
|
|
// bids that are both matched to the ask. This account is large enough
|
|
// to be recreated. We assume here that no maker/taker fees are applied
|
|
// and only the matched units are paid for.
|
|
bigAcct := &account.Account{
|
|
TraderKey: &keychain.KeyDescriptor{PubKey: acctKeyBig},
|
|
Value: 1_000_000,
|
|
Expiry: 144,
|
|
State: account.StateOpen,
|
|
BatchKey: startBatchKey,
|
|
}
|
|
smallAcct := &account.Account{
|
|
TraderKey: &keychain.KeyDescriptor{PubKey: acctKeySmall},
|
|
Value: 400_000,
|
|
Expiry: 144,
|
|
State: account.StateOpen,
|
|
BatchKey: startBatchKey,
|
|
}
|
|
ask := &Ask{Kit: newKit(Nonce{0x01}, 4, 1)}
|
|
bid1 := &Bid{Kit: newKit(Nonce{0x02}, 3, 1)}
|
|
bid2 := &Bid{Kit: newKit(Nonce{0x03}, 3, 2)}
|
|
batchTx := &wire.MsgTx{
|
|
Version: 2,
|
|
TxOut: []*wire.TxOut{{
|
|
Value: 600_000,
|
|
PkScript: []byte{77, 88, 99},
|
|
}},
|
|
}
|
|
|
|
// Create a batch for us as if we were the trader for both accounts and
|
|
// were matched against each other (not impossible but unlikely to
|
|
// happen in the real system).
|
|
accountDiffs := []*AccountDiff{
|
|
{
|
|
AccountKeyRaw: acctIDBig,
|
|
AccountKey: acctKeyBig,
|
|
EndingState: auctioneerrpc.AccountDiff_OUTPUT_RECREATED,
|
|
OutpointIndex: 0,
|
|
EndingBalance: 600_000,
|
|
},
|
|
{
|
|
AccountKeyRaw: acctIDSmall,
|
|
AccountKey: acctKeySmall,
|
|
EndingState: auctioneerrpc.AccountDiff_OUTPUT_FULLY_SPENT,
|
|
OutpointIndex: -1,
|
|
EndingBalance: 0,
|
|
},
|
|
}
|
|
matchedOrders := map[Nonce][]*MatchedOrder{
|
|
ask.nonce: {
|
|
{
|
|
Order: bid1,
|
|
UnitsFilled: 2,
|
|
},
|
|
{
|
|
Order: bid2,
|
|
UnitsFilled: 2,
|
|
},
|
|
},
|
|
bid1.nonce: {{
|
|
Order: ask,
|
|
UnitsFilled: 2,
|
|
}},
|
|
bid2.nonce: {{
|
|
Order: ask,
|
|
UnitsFilled: 2,
|
|
}},
|
|
}
|
|
batch := &Batch{
|
|
ID: batchID,
|
|
Version: DefaultBatchVersion,
|
|
MatchedOrders: matchedOrders,
|
|
AccountDiffs: accountDiffs,
|
|
BatchTX: batchTx,
|
|
BatchTxFeeRate: chainfee.FeePerKwFloor,
|
|
HeightHint: 1337,
|
|
}
|
|
|
|
// Create the starting database state now.
|
|
storeMock.accounts = map[[33]byte]*account.Account{
|
|
acctIDBig: bigAcct,
|
|
acctIDSmall: smallAcct,
|
|
}
|
|
storeMock.orders = map[Nonce]Order{
|
|
ask.Nonce(): ask,
|
|
bid1.Nonce(): bid1,
|
|
bid2.Nonce(): bid2,
|
|
}
|
|
|
|
// Pass the assembled batch to the storer now.
|
|
err := storer.StorePendingBatch(batch)
|
|
if err != nil {
|
|
t.Fatalf("error storing batch: %v", err)
|
|
}
|
|
|
|
// Because the store backend is an in-memory mock, all modifications are
|
|
// performed on the actual instances, which makes it easy to check.
|
|
// Check the order states first.
|
|
if ask.State != StateExecuted {
|
|
t.Fatalf("invalid order state, got %v wanted %v",
|
|
ask.State, StateExecuted)
|
|
}
|
|
if ask.UnitsUnfulfilled != 0 {
|
|
t.Fatalf("invalid units unfulfilled, got %d wanted %d",
|
|
ask.UnitsUnfulfilled, 0)
|
|
}
|
|
if bid1.State != StatePartiallyFilled {
|
|
t.Fatalf("invalid order state, got %v wanted %v",
|
|
bid1.State, StatePartiallyFilled)
|
|
}
|
|
if bid1.UnitsUnfulfilled != 1 {
|
|
t.Fatalf("invalid units unfulfilled, got %d wanted %d",
|
|
bid1.UnitsUnfulfilled, 1)
|
|
}
|
|
if bid2.State != StateExecuted {
|
|
t.Fatalf("invalid order state, got %v wanted %v",
|
|
bid2.State, StateExecuted)
|
|
}
|
|
if bid2.UnitsUnfulfilled != 1 {
|
|
t.Fatalf("invalid units unfulfilled, got %d wanted %d",
|
|
bid2.UnitsUnfulfilled, 1)
|
|
}
|
|
|
|
// Check the account states next.
|
|
if smallAcct.State != account.StatePendingClosed {
|
|
t.Fatalf("invalid account state, got %d wanted %d",
|
|
smallAcct.State, account.StatePendingClosed)
|
|
}
|
|
if smallAcct.Value != 0 {
|
|
t.Fatalf("invalid account balance, got %d wanted %d",
|
|
smallAcct.Value, 0)
|
|
}
|
|
if smallAcct.Expiry != 144 {
|
|
t.Fatalf("invalid account expiry, got %d wanted %d",
|
|
smallAcct.Value, 144)
|
|
}
|
|
if smallAcct.HeightHint != batch.HeightHint {
|
|
t.Fatalf("invalid account height hint, got %d wanted %d",
|
|
smallAcct.HeightHint, batch.HeightHint)
|
|
}
|
|
|
|
if bigAcct.State != account.StatePendingBatch {
|
|
t.Fatalf("invalid account state, got %d wanted %d",
|
|
bigAcct.State, account.StatePendingBatch)
|
|
}
|
|
if bigAcct.Value != 600_000 {
|
|
t.Fatalf("invalid account balance, got %d wanted %d",
|
|
bigAcct.Value, 600_000)
|
|
}
|
|
if bigAcct.Expiry != 144 {
|
|
t.Fatalf("invalid account expiry, got %d wanted %d",
|
|
bigAcct.Value, 144)
|
|
}
|
|
if bigAcct.HeightHint != batch.HeightHint {
|
|
t.Fatalf("invalid account height hint, got %d wanted %d",
|
|
bigAcct.HeightHint, batch.HeightHint)
|
|
}
|
|
}
|
|
|
|
func newKit(nonce Nonce, units, minUnitsMatch SupplyUnit) Kit {
|
|
kit := NewKit(nonce)
|
|
kit.Units = units
|
|
kit.UnitsUnfulfilled = units
|
|
kit.MinUnitsMatch = minUnitsMatch
|
|
kit.State = StateSubmitted
|
|
return *kit
|
|
}
|