1
+ from contextlib import asynccontextmanager
1
2
from pathlib import Path
2
3
from typing import Optional , Set , List , Tuple , Dict
3
4
@@ -48,9 +49,23 @@ async def connect(self):
48
49
"CREATE TABLE IF NOT EXISTS partial(launcher_id text, timestamp bigint, difficulty bigint)"
49
50
)
50
51
52
+ await self .connection .execute (
53
+ """
54
+ CREATE TABLE IF NOT EXISTS payment(
55
+ launcher_id text,
56
+ puzzle_hash text,
57
+ amount bigint,
58
+ points bigint,
59
+ timestamp bigint,
60
+ is_payment tinyint
61
+ )
62
+ """
63
+ )
64
+
51
65
await self .connection .execute ("CREATE INDEX IF NOT EXISTS scan_ph on farmer(p2_singleton_puzzle_hash)" )
52
66
await self .connection .execute ("CREATE INDEX IF NOT EXISTS timestamp_index on partial(timestamp)" )
53
67
await self .connection .execute ("CREATE INDEX IF NOT EXISTS launcher_id_index on partial(launcher_id)" )
68
+ await self .connection .execute ("CREATE INDEX IF NOT EXISTS launcher_id_index on payment(launcher_id)" )
54
69
55
70
await self .connection .commit ()
56
71
@@ -70,6 +85,15 @@ def _row_to_farmer_record(row) -> FarmerRecord:
70
85
True if row [10 ] == 1 else False ,
71
86
)
72
87
88
+ @asynccontextmanager
89
+ async def tx (self ):
90
+ try :
91
+ yield
92
+ await self .connection .commit ()
93
+ except Exception :
94
+ await self .connection .rollback ()
95
+ raise
96
+
73
97
async def add_farmer_record (self , farmer_record : FarmerRecord , metadata : RequestMetadata ):
74
98
cursor = await self .connection .execute (
75
99
f"INSERT OR REPLACE INTO farmer VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)" ,
@@ -146,21 +170,22 @@ async def get_farmer_records_for_p2_singleton_phs(self, puzzle_hashes: Set[bytes
146
170
rows = await cursor .fetchall ()
147
171
return [self ._row_to_farmer_record (row ) for row in rows ]
148
172
149
- async def get_farmer_points_and_payout_instructions (self ) -> List [Tuple [uint64 , bytes ]]:
150
- cursor = await self .connection .execute (f"SELECT points, payout_instructions from farmer" )
173
+ async def get_farmer_launcher_id_and_points_and_payout_instructions (self ) -> List [Tuple [bytes32 , uint64 , bytes32 ]]:
174
+ cursor = await self .connection .execute (f"SELECT launcher_id, points, payout_instructions from farmer" )
151
175
rows = await cursor .fetchall ()
152
- accumulated : Dict [bytes32 , uint64 ] = {}
176
+ accumulated : Dict [Tuple [ bytes32 , bytes32 ] , uint64 ] = {}
153
177
for row in rows :
154
- points : uint64 = uint64 (row [0 ])
155
- ph : bytes32 = bytes32 (bytes .fromhex (row [1 ]))
178
+ launcher_id : bytes32 = bytes32 (bytes .fromhex (row [0 ]))
179
+ points : uint64 = uint64 (row [1 ])
180
+ ph : bytes32 = bytes32 (bytes .fromhex (row [2 ]))
156
181
if ph in accumulated :
157
- accumulated [ph ] += points
182
+ accumulated [( launcher_id , ph ) ] += points
158
183
else :
159
- accumulated [ph ] = points
184
+ accumulated [( launcher_id , ph ) ] = points
160
185
161
- ret : List [Tuple [uint64 , bytes32 ]] = []
162
- for ph , total_points in accumulated .items ():
163
- ret .append ((total_points , ph ))
186
+ ret : List [Tuple [bytes32 , uint64 , bytes32 ]] = []
187
+ for ( launcher_id , ph ) , total_points in accumulated .items ():
188
+ ret .append ((launcher_id , total_points , ph ))
164
189
return ret
165
190
166
191
async def clear_farmer_points (self ) -> None :
@@ -192,3 +217,81 @@ async def get_recent_partials(self, launcher_id: bytes32, count: int) -> List[Tu
192
217
rows = await cursor .fetchall ()
193
218
ret : List [Tuple [uint64 , uint64 ]] = [(uint64 (timestamp ), uint64 (difficulty )) for timestamp , difficulty in rows ]
194
219
return ret
220
+
221
+ async def add_payment (
222
+ self ,
223
+ launcher_id : bytes32 ,
224
+ puzzle_hash : bytes32 ,
225
+ amount : uint64 ,
226
+ points : int ,
227
+ timestamp : uint64 ,
228
+ is_payment : bool ,
229
+ ):
230
+ cursor = await self .connection .execute (
231
+ "INSERT INTO payment VALUES(?, ?, ?, ?, ?, ?)" ,
232
+ (launcher_id .hex (), puzzle_hash .hex (), amount , points , timestamp , int (is_payment )),
233
+ )
234
+ await cursor .close ()
235
+ await self .connection .commit ()
236
+
237
+ async def update_is_payment (
238
+ self ,
239
+ launcher_id_and_points_and_timestamp : List [Tuple [bytes32 , uint64 , uint64 ]],
240
+ is_payment : bool ,
241
+ auto_commit : bool = True ,
242
+ ):
243
+ cursor = await self .connection .executemany (
244
+ "UPDATE payment SET is_payment=? WHERE launcher_id=? AND timestamp=?" ,
245
+ tuple (
246
+ (int (is_payment ), launcher_id .hex (), timestamp )
247
+ for launcher_id , _ , timestamp in launcher_id_and_points_and_timestamp
248
+ ),
249
+ )
250
+ await cursor .close ()
251
+
252
+ if is_payment is True :
253
+ cursor = await self .connection .executemany (
254
+ "UPDATE farmer SET points=points-? WHERE launcher_id=?" ,
255
+ tuple (
256
+ (points , launcher_id .hex ())
257
+ for launcher_id , points , _ in launcher_id_and_points_and_timestamp
258
+ ),
259
+ )
260
+ await cursor .close ()
261
+
262
+ if auto_commit is True :
263
+ await self .connection .commit ()
264
+
265
+ async def get_pending_payment_records (self , count : int ) -> List [
266
+ Tuple [bytes32 , bytes32 , uint64 , uint64 , uint64 , bool ]
267
+ ]:
268
+ cursor = await self .connection .execute (
269
+ """
270
+ SELECT
271
+ launcher_id, puzzle_hash, amount, points, timestamp, is_payment
272
+ FROM payment
273
+ WHERE is_payment=0 ORDER BY timestamp ASC LIMIT ?
274
+ """ ,
275
+ (count ,),
276
+ )
277
+ rows = await cursor .fetchall ()
278
+ await cursor .close ()
279
+ ret : List [
280
+ Tuple [bytes32 , bytes32 , uint64 , uint64 , uint64 , bool ]
281
+ ] = [
282
+ (
283
+ bytes32 (bytes .fromhex (launcher_id )),
284
+ bytes32 (bytes .fromhex (puzzle_hash )),
285
+ uint64 (amount ),
286
+ uint64 (points ),
287
+ uint64 (timestamp ),
288
+ bool (is_payment ),
289
+ ) for launcher_id , puzzle_hash , amount , points , timestamp , is_payment in rows
290
+ ]
291
+ return ret
292
+
293
+ async def get_pending_payment_count (self ) -> int :
294
+ cursor = await self .connection .execute ("SELECT COUNT(*) FROM payment WHERE is_payment=0" )
295
+ count : int = (await cursor .fetchone ())[0 ]
296
+ await cursor .close ()
297
+ return count
0 commit comments