mirror of
https://github.com/lightninglabs/pool.git
synced 2026-08-13 12:33:04 +02:00
168 lines
5.1 KiB
Go
168 lines
5.1 KiB
Go
package order
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/btcsuite/btcd/btcec/v2"
|
|
"github.com/btcsuite/btcd/wire"
|
|
"github.com/lightninglabs/pool/account"
|
|
"github.com/lightninglabs/pool/auctioneerrpc"
|
|
)
|
|
|
|
// batchStorer is a type that implements BatchStorer and can persist a batch to
|
|
// the local trader database.
|
|
type batchStorer struct {
|
|
orderStore Store
|
|
getAccount func(*btcec.PublicKey) (*account.Account, error)
|
|
}
|
|
|
|
// StorePendingBatch makes sure all changes executed by a batch are correctly
|
|
// and atomically staged to the database. It is assumed that the batch has
|
|
// previously been fully validated and that all diffs contained are consistent!
|
|
// Once the batch has been finalized/confirmed on-chain, then the stage
|
|
// modifications will be applied atomically as a result of MarkBatchComplete.
|
|
//
|
|
// NOTE: This method is part of the BatchStorer interface.
|
|
func (s *batchStorer) StorePendingBatch(batch *Batch) error {
|
|
// Prepare the order modifications first.
|
|
orders := make([]Nonce, len(batch.MatchedOrders))
|
|
orderModifiers := make([][]Modifier, len(orders))
|
|
orderIndex := 0
|
|
for nonce, theirOrders := range batch.MatchedOrders {
|
|
// Get our order first to find out the number of unfulfilled
|
|
// units.
|
|
ourOrder, err := s.orderStore.GetOrder(nonce)
|
|
if err != nil {
|
|
return fmt.Errorf("error getting order: %v", err)
|
|
}
|
|
orders[orderIndex] = nonce
|
|
|
|
// Find out if the order has unfulfilled units left or not.
|
|
unitsUnfulfilled := ourOrder.Details().UnitsUnfulfilled
|
|
for _, theirOrder := range theirOrders {
|
|
unitsUnfulfilled -= theirOrder.UnitsFilled
|
|
}
|
|
switch {
|
|
// The order has been fully filled and can be archived.
|
|
case unitsUnfulfilled == 0:
|
|
orderModifiers[orderIndex] = []Modifier{
|
|
StateModifier(StateExecuted),
|
|
UnitsFulfilledModifier(0),
|
|
}
|
|
|
|
// The order has not been fully filled, but it cannot be matched
|
|
// again due to its remaining unfulfilled units being below its
|
|
// allowed minimum, so we'll archive it.
|
|
case unitsUnfulfilled < ourOrder.Details().MinUnitsMatch:
|
|
orderModifiers[orderIndex] = []Modifier{
|
|
StateModifier(StateExecuted),
|
|
UnitsFulfilledModifier(unitsUnfulfilled),
|
|
}
|
|
|
|
// Some units were not yet filled.
|
|
default:
|
|
orderModifiers[orderIndex] = []Modifier{
|
|
StateModifier(StatePartiallyFilled),
|
|
UnitsFulfilledModifier(unitsUnfulfilled),
|
|
}
|
|
}
|
|
|
|
orderIndex++
|
|
}
|
|
|
|
// Next create our account modifiers.
|
|
accounts := make([]*account.Account, len(batch.AccountDiffs))
|
|
accountModifiers := make([][]account.Modifier, len(accounts))
|
|
for idx, diff := range batch.AccountDiffs {
|
|
// Get the current state of the account first so we can create
|
|
// a proper diff.
|
|
acct, err := s.getAccount(diff.AccountKey)
|
|
if err != nil {
|
|
return fmt.Errorf("error getting account: %v", err)
|
|
}
|
|
accounts[idx] = acct
|
|
var modifiers []account.Modifier
|
|
|
|
// Determine the new state of the account and set the on-chain
|
|
// attributes accordingly.
|
|
switch diff.EndingState {
|
|
// The account output has been recreated and needs to wait to be
|
|
// confirmed again.
|
|
case auctioneerrpc.AccountDiff_OUTPUT_RECREATED:
|
|
modifiers = append(
|
|
modifiers,
|
|
account.StateModifier(account.StatePendingBatch),
|
|
account.OutPointModifier(wire.OutPoint{
|
|
Index: uint32(diff.OutpointIndex),
|
|
Hash: batch.BatchTX.TxHash(),
|
|
}),
|
|
account.IncrementBatchKey(),
|
|
)
|
|
|
|
// The account expiry needs to be updated only when the
|
|
// client supports it.
|
|
if batch.Version.SupportsAccountExtension() &&
|
|
diff.NewExpiry != 0 {
|
|
|
|
modifiers = append(
|
|
modifiers,
|
|
account.ExpiryModifier(diff.NewExpiry),
|
|
)
|
|
}
|
|
|
|
// The account version can be upgraded during the batch
|
|
// if the client supports it.
|
|
if batch.Version.SupportsAccountTaprootUpgrade() &&
|
|
diff.NewVersion > acct.Version {
|
|
|
|
modifiers = append(
|
|
modifiers,
|
|
account.VersionModifier(diff.NewVersion),
|
|
)
|
|
}
|
|
|
|
// The account was fully spent on-chain. We need to wait for the
|
|
// batch (spend) TX to be confirmed still.
|
|
case auctioneerrpc.AccountDiff_OUTPUT_FULLY_SPENT,
|
|
auctioneerrpc.AccountDiff_OUTPUT_DUST_ADDED_TO_FEES,
|
|
auctioneerrpc.AccountDiff_OUTPUT_DUST_EXTENDED_OFFCHAIN:
|
|
|
|
modifiers = append(
|
|
modifiers,
|
|
account.StateModifier(account.StatePendingClosed),
|
|
)
|
|
|
|
default:
|
|
return fmt.Errorf("invalid ending account state %d",
|
|
diff.EndingState)
|
|
}
|
|
|
|
// Finally update the account value, height hint, and its latest
|
|
// transaction.
|
|
modifiers = append(
|
|
modifiers, account.ValueModifier(diff.EndingBalance),
|
|
)
|
|
modifiers = append(
|
|
modifiers, account.HeightHintModifier(batch.HeightHint),
|
|
)
|
|
modifiers = append(
|
|
modifiers, account.LatestTxModifier(batch.BatchTX),
|
|
)
|
|
|
|
accountModifiers[idx] = modifiers
|
|
}
|
|
|
|
// Everything is ready to be persisted now.
|
|
return s.orderStore.StorePendingBatch(
|
|
batch, orders, orderModifiers, accounts, accountModifiers,
|
|
)
|
|
}
|
|
|
|
// MarkBatchComplete marks a pending batch as complete, allowing a trader to
|
|
// participate in a new batch.
|
|
func (s *batchStorer) MarkBatchComplete() error {
|
|
return s.orderStore.MarkBatchComplete()
|
|
}
|
|
|
|
// A compile-time constraint to ensure batchStorer implements BatchStorer.
|
|
var _ BatchStorer = (*batchStorer)(nil)
|