-
Notifications
You must be signed in to change notification settings - Fork 46
/
batch_storer.go
168 lines (146 loc) · 5.1 KB
/
batch_storer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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)