This repository has been archived by the owner on Apr 2, 2024. It is now read-only.
generated from mrz1836/go-template
-
-
Notifications
You must be signed in to change notification settings - Fork 6
/
action_transaction.go
332 lines (271 loc) · 9.83 KB
/
action_transaction.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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
package bux
import (
"context"
"fmt"
"github.com/BuxOrg/bux/datastore"
"github.com/BuxOrg/bux/utils"
)
// RecordTransaction will parse the transaction and save it into the Datastore
//
// Internal (known) transactions: there is a corresponding `draft_transaction` via `draft_id`
// External (known) transactions: there are output(s) related to the destination `reference_id`, tx is valid (mempool/on-chain)
// External (unknown) transactions: no reference id but some output(s) match known outputs, tx is valid (mempool/on-chain)
// Unknown transactions: no matching outputs, tx will be disregarded
//
// xPubKey is the raw public xPub
// txHex is the raw transaction hex
// draftID is the unique draft id from a previously started New() transaction (draft_transaction.ID)
// opts are model options and can include "metadata"
func (c *Client) RecordTransaction(ctx context.Context, xPubKey, txHex, draftID string,
opts ...ModelOps) (*Transaction, error) {
// Check for existing NewRelic transaction
ctx = c.GetOrStartTxn(ctx, "record_transaction")
// Create the model & set the default options (gives options from client->model)
newOpts := c.DefaultModelOptions(append(opts, WithXPub(xPubKey), New())...)
transaction := newTransactionWithDraftID(
txHex, draftID, newOpts...,
)
// Ensure that we have a transaction id (created from the txHex)
id := transaction.GetID()
if len(id) == 0 {
return nil, ErrMissingTxHex
}
// Create the lock and set the release for after the function completes
unlock, err := newWriteLock(
ctx, fmt.Sprintf(lockKeyRecordTx, id), c.Cachestore(),
)
defer unlock()
if err != nil {
return nil, err
}
// OPTION: check incoming transactions (if enabled, will add to queue for checking on-chain)
if !c.IsITCEnabled() {
transaction.DebugLog("incoming transaction check is disabled")
} else {
// Incoming (external/unknown) transaction (no draft id was given)
if len(transaction.DraftID) == 0 {
// Process & save the model
incomingTx := newIncomingTransaction(
transaction.ID, txHex, newOpts...,
)
if err = incomingTx.Save(ctx); err != nil {
return nil, err
}
// Create the sync transaction model
sync := newSyncTransaction(
transaction.GetID(),
transaction.Client().DefaultSyncConfig(),
transaction.GetOptions(true)...,
)
// Skip broadcasting and skip P2P (incoming tx should have been broadcasted already)
sync.BroadcastStatus = SyncStatusSkipped // todo: this is an assumption
sync.P2PStatus = SyncStatusSkipped // The owner of the Tx should have already notified paymail providers
// Use the same metadata
sync.Metadata = transaction.Metadata
// If all the options are skipped, do not make a new model (ignore the record)
if !sync.isSkipped() {
if err = sync.Save(ctx); err != nil {
return nil, err
}
}
// Added to queue
return newTransactionFromIncomingTransaction(incomingTx), nil
}
// Internal tx (must match draft tx)
if transaction.draftTransaction, err = getDraftTransactionID(
ctx, transaction.xPubID, transaction.DraftID,
transaction.GetOptions(false)...,
); err != nil {
return nil, err
} else if transaction.draftTransaction == nil {
return nil, ErrDraftNotFound
}
}
// Process & save the transaction model
if err = transaction.Save(ctx); err != nil {
return nil, err
}
// Return the response
return transaction, nil
}
// RecordMonitoredTransaction will parse the transaction and save it into the Datastore
//
// This function will try to record the transaction directly, without checking draft ids etc.
func (c *Client) RecordMonitoredTransaction(ctx context.Context, txHex string, opts ...ModelOps) (*Transaction, error) {
// Check for existing NewRelic transaction
ctx = c.GetOrStartTxn(ctx, "record_monitored_transaction")
// Create the model & set the default options (gives options from client->model)
newOpts := c.DefaultModelOptions(append(opts, New())...)
transaction := newTransaction(txHex, newOpts...)
// Ensure that we have a transaction id (created from the txHex)
id := transaction.GetID()
if len(id) == 0 {
return nil, ErrMissingTxHex
}
// Create the lock and set the release for after the function completes
unlock, err := newWriteLock(
ctx, fmt.Sprintf(lockKeyRecordTx, id), c.Cachestore(),
)
defer unlock()
if err != nil {
return nil, err
}
// Process & save the transaction model
if err = transaction.Save(ctx); err != nil {
return nil, err
}
// Return the response
return transaction, nil
}
// NewTransaction will create a new draft transaction and return it
//
// ctx is the context
// rawXpubKey is the raw xPub key
// config is the TransactionConfig
// metadata is added to the model
// opts are additional model options to be applied
func (c *Client) NewTransaction(ctx context.Context, rawXpubKey string, config *TransactionConfig,
opts ...ModelOps) (*DraftTransaction, error) {
// Check for existing NewRelic draftTransaction
ctx = c.GetOrStartTxn(ctx, "new_transaction")
// Create the lock and set the release for after the function completes
unlock, err := newWaitWriteLock(
ctx, fmt.Sprintf(lockKeyProcessXpub, utils.Hash(rawXpubKey)), c.Cachestore(),
)
defer unlock()
if err != nil {
return nil, err
}
// Create the draft tx model
draftTransaction := newDraftTransaction(
rawXpubKey, config,
c.DefaultModelOptions(append(opts, New())...)...,
)
// Save the model
if err = draftTransaction.Save(ctx); err != nil {
return nil, err
}
// Return the created model
return draftTransaction, nil
}
// GetTransaction will get a transaction from the Datastore
//
// ctx is the context
// testTxID is the transaction ID
func (c *Client) GetTransaction(ctx context.Context, xPubID, txID string) (*Transaction, error) {
// Check for existing NewRelic transaction
ctx = c.GetOrStartTxn(ctx, "get_transaction")
// Get the transaction by ID
transaction, err := getTransactionByID(
ctx, xPubID, txID, c.DefaultModelOptions()...,
)
if err != nil {
return nil, err
}
if transaction == nil {
return nil, ErrMissingTransaction
}
return transaction, nil
}
// GetTransactions will get all the transactions from the Datastore
func (c *Client) GetTransactions(ctx context.Context, metadataConditions *Metadata,
conditions *map[string]interface{}, queryParams *datastore.QueryParams, opts ...ModelOps) ([]*Transaction, error) {
// Check for existing NewRelic transaction
ctx = c.GetOrStartTxn(ctx, "get_transactions")
// Get the transactions
transactions, err := getTransactions(
ctx, metadataConditions, conditions, queryParams,
c.DefaultModelOptions(opts...)...,
)
if err != nil {
return nil, err
}
return transactions, nil
}
// GetTransactionsAggregate will get a count of all transactions per aggregate column from the Datastore
func (c *Client) GetTransactionsAggregate(ctx context.Context, metadataConditions *Metadata,
conditions *map[string]interface{}, aggregateColumn string, opts ...ModelOps) (map[string]interface{}, error) {
// Check for existing NewRelic transaction
ctx = c.GetOrStartTxn(ctx, "get_transactions")
// Get the transactionAggregate
transactionAggregate, err := getTransactionsAggregate(
ctx, metadataConditions, conditions, aggregateColumn,
c.DefaultModelOptions(opts...)...,
)
if err != nil {
return nil, err
}
return transactionAggregate, nil
}
// GetTransactionsCount will get a count of all the transactions from the Datastore
func (c *Client) GetTransactionsCount(ctx context.Context, metadataConditions *Metadata,
conditions *map[string]interface{}, opts ...ModelOps) (int64, error) {
// Check for existing NewRelic transaction
ctx = c.GetOrStartTxn(ctx, "get_transactions_count")
// Get the transactions count
count, err := getTransactionsCount(
ctx, metadataConditions, conditions,
c.DefaultModelOptions(opts...)...,
)
if err != nil {
return 0, err
}
return count, nil
}
// GetTransactionsByXpubID will get all transactions for a given xpub from the Datastore
//
// ctx is the context
// rawXpubKey is the raw xPub key
// metadataConditions is added to the request for searching
// conditions is added the request for searching
func (c *Client) GetTransactionsByXpubID(ctx context.Context, xPubID string, metadataConditions *Metadata,
conditions *map[string]interface{}, queryParams *datastore.QueryParams) ([]*Transaction, error) {
// Check for existing NewRelic transaction
ctx = c.GetOrStartTxn(ctx, "get_transaction")
// Get the transaction by ID
// todo: add queryParams for: page size and page (right now it is unlimited)
transactions, err := getTransactionsByXpubID(
ctx, xPubID, metadataConditions, conditions, queryParams,
c.DefaultModelOptions()...,
)
if err != nil {
return nil, err
}
return transactions, nil
}
// GetTransactionsByXpubIDCount will get the count of all transactions matching the search criteria
func (c *Client) GetTransactionsByXpubIDCount(ctx context.Context, xPubID string, metadataConditions *Metadata,
conditions *map[string]interface{}) (int64, error) {
// Check for existing NewRelic transaction
ctx = c.GetOrStartTxn(ctx, "count_transactions")
count, err := getTransactionsCountByXpubID(
ctx, xPubID, metadataConditions, conditions,
c.DefaultModelOptions()...,
)
if err != nil {
return 0, err
}
return count, nil
}
// UpdateTransactionMetadata will update the metadata in an existing transaction
func (c *Client) UpdateTransactionMetadata(ctx context.Context, xPubID, id string,
metadata Metadata) (*Transaction, error) {
// Check for existing NewRelic transaction
ctx = c.GetOrStartTxn(ctx, "update_transaction_by_id")
// Get the transaction
transaction, err := c.GetTransaction(ctx, xPubID, id)
if err != nil {
return nil, err
}
// Update the metadata
if err = transaction.UpdateTransactionMetadata(
xPubID, metadata,
); err != nil {
return nil, err
}
// Save the model
if err = transaction.Save(ctx); err != nil {
return nil, err
}
return transaction, nil
}