@moneypot/hub 1.3.0-dev.15 → 1.3.0-dev.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/config.d.ts +13 -8
- package/dist/src/config.js +52 -78
- package/dist/src/db/index.d.ts +0 -1
- package/dist/src/db/index.js +1 -2
- package/dist/src/db/types.d.ts +0 -28
- package/dist/src/db/types.js +1 -5
- package/dist/src/index.js +1 -1
- package/dist/src/pg-advisory-lock.d.ts +4 -9
- package/dist/src/pg-advisory-lock.js +1 -8
- package/dist/src/pg-versions/{006-outcome-bet.sql → 005-outcome-bet.sql} +1 -8
- package/dist/src/plugins/hub-make-outcome-bet.d.ts +9 -36
- package/dist/src/plugins/hub-make-outcome-bet.js +25 -186
- package/dist/src/plugins/hub-user-balance-by-currency.js +40 -15
- package/dist/src/process-transfers.js +1 -1
- package/dist/src/server/graphile.config.js +1 -9
- package/dist/src/server/handle-errors.js +1 -1
- package/dist/src/server/index.js +1 -1
- package/dist/src/take-request/process-take-request.js +3 -3
- package/package.json +1 -1
- package/dist/src/hash-chain/db-hash-chain.d.ts +0 -15
- package/dist/src/hash-chain/db-hash-chain.js +0 -35
- package/dist/src/hash-chain/get-hash.d.ts +0 -17
- package/dist/src/hash-chain/get-hash.js +0 -57
- package/dist/src/hash-chain/plugins/hub-bad-hash-chain-error.d.ts +0 -1
- package/dist/src/hash-chain/plugins/hub-bad-hash-chain-error.js +0 -20
- package/dist/src/hash-chain/plugins/hub-create-hash-chain.d.ts +0 -1
- package/dist/src/hash-chain/plugins/hub-create-hash-chain.js +0 -111
- package/dist/src/hash-chain/plugins/hub-user-active-hash-chain.d.ts +0 -1
- package/dist/src/hash-chain/plugins/hub-user-active-hash-chain.js +0 -46
- package/dist/src/pg-versions/005-hash-chain.sql +0 -84
- package/dist/src/plugins/hub-outcome-input-non-null-fields.d.ts +0 -18
- package/dist/src/plugins/hub-outcome-input-non-null-fields.js +0 -20
|
@@ -1,12 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { context, object, sideEffect } from "postgraphile/grafast";
|
|
2
2
|
import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { GraphQLError } from "graphql";
|
|
5
|
-
import {
|
|
5
|
+
import { exactlyOneRow, maybeOneRow, superuserPool, withPgPoolTransaction, } from "../db/index.js";
|
|
6
6
|
import { assert } from "tsafe";
|
|
7
|
-
import { dbInsertHubHash, dbLockHubHashChain, } from "../hash-chain/db-hash-chain.js";
|
|
8
|
-
import { getIntermediateHash, getPreimageHash, } from "../hash-chain/get-hash.js";
|
|
9
|
-
import { createHmac } from "node:crypto";
|
|
10
7
|
const FLOAT_EPSILON = 1e-10;
|
|
11
8
|
function sum(ns) {
|
|
12
9
|
return ns.reduce((a, b) => a + b, 0);
|
|
@@ -38,8 +35,6 @@ const InputSchema = z
|
|
|
38
35
|
.max(50, "Outcome count must be <= 50")
|
|
39
36
|
.refine((data) => data.some((o) => o.profit < 0), "At least one outcome should have profit < 0")
|
|
40
37
|
.refine((data) => data.some((o) => o.profit > 0), "At least one outcome should have profit > 0"),
|
|
41
|
-
hashChainId: z.string().uuid("Invalid hash chain ID"),
|
|
42
|
-
metadata: z.record(z.string(), z.any()).optional(),
|
|
43
38
|
})
|
|
44
39
|
.strict();
|
|
45
40
|
const BetKindSchema = z
|
|
@@ -53,11 +48,10 @@ const BetConfigsSchema = z.record(BetKindSchema, z.object({
|
|
|
53
48
|
.gte(0, "House edge must be >= 0")
|
|
54
49
|
.lte(1, "House edge must be <= 1"),
|
|
55
50
|
saveOutcomes: z.boolean(),
|
|
56
|
-
|
|
57
|
-
.function()
|
|
58
|
-
.optional(),
|
|
59
|
-
finalizeMetadata: z
|
|
51
|
+
getMetadata: z
|
|
60
52
|
.function()
|
|
53
|
+
.args(InputSchema)
|
|
54
|
+
.returns(z.record(z.string(), z.any()))
|
|
61
55
|
.optional(),
|
|
62
56
|
}));
|
|
63
57
|
export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
@@ -75,18 +69,10 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
75
69
|
wager: Int!
|
|
76
70
|
currency: String!
|
|
77
71
|
outcomes: [HubOutcomeInput!]!
|
|
78
|
-
hashChainId: UUID!
|
|
79
|
-
metadata: JSON
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
type HubMakeOutcomeBetSuccess {
|
|
83
|
-
bet: HubOutcomeBet!
|
|
84
72
|
}
|
|
85
73
|
|
|
86
|
-
union HubMakeOutcomeBetResult = HubMakeOutcomeBetSuccess | HubBadHashChainError
|
|
87
|
-
|
|
88
74
|
type HubMakeOutcomeBetPayload {
|
|
89
|
-
|
|
75
|
+
bet: HubOutcomeBet!
|
|
90
76
|
}
|
|
91
77
|
|
|
92
78
|
extend type Mutation {
|
|
@@ -99,7 +85,7 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
99
85
|
Mutation: {
|
|
100
86
|
hubMakeOutcomeBet: (_, { $input }) => {
|
|
101
87
|
const $identity = context().get("identity");
|
|
102
|
-
const $
|
|
88
|
+
const $betId = sideEffect([$identity, $input], async ([identity, rawInput]) => {
|
|
103
89
|
if (identity?.kind !== "user") {
|
|
104
90
|
throw new GraphQLError("Unauthorized");
|
|
105
91
|
}
|
|
@@ -119,15 +105,8 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
119
105
|
if (!betConfig) {
|
|
120
106
|
throw new GraphQLError(`Invalid bet kind`);
|
|
121
107
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const result = betConfig.initializeMetadataFromUntrustedUserInput(input);
|
|
125
|
-
if (result.ok) {
|
|
126
|
-
initializedMetadata = result.value;
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
throw new GraphQLError(`Invalid input: ${result.error}`);
|
|
130
|
-
}
|
|
108
|
+
if (!betKinds.includes(rawInput.kind)) {
|
|
109
|
+
throw new GraphQLError(`Invalid bet kind`);
|
|
131
110
|
}
|
|
132
111
|
const houseEV = calculateHouseEV(input.outcomes);
|
|
133
112
|
const minHouseEV = Math.max(0, betConfig.houseEdge - FLOAT_EPSILON);
|
|
@@ -179,76 +158,7 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
179
158
|
if (maxPotentialPayout > maxAllowablePayout) {
|
|
180
159
|
throw new GraphQLError(`House risk limit exceeded. Max payout: ${maxPotentialPayout.toFixed(4)}`);
|
|
181
160
|
}
|
|
182
|
-
const
|
|
183
|
-
userId: session.user_id,
|
|
184
|
-
experienceId: session.experience_id,
|
|
185
|
-
casinoId: session.casino_id,
|
|
186
|
-
hashChainId: input.hashChainId,
|
|
187
|
-
});
|
|
188
|
-
if (!dbHashChain || !dbHashChain.active) {
|
|
189
|
-
return {
|
|
190
|
-
__typename: "HubBadHashChainError",
|
|
191
|
-
message: "Active hash chain not found",
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
if (dbHashChain.current_iteration <= 1) {
|
|
195
|
-
if (dbHashChain.current_iteration === 1) {
|
|
196
|
-
finishHashChainInBackground({
|
|
197
|
-
hashChainId: input.hashChainId,
|
|
198
|
-
}).catch((e) => {
|
|
199
|
-
console.error("Error finishing hash chain in background", { hashChainId: input.hashChainId, error: e });
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
return {
|
|
203
|
-
__typename: "HubBadHashChainError",
|
|
204
|
-
message: "Hash chain drained. Create a new one.",
|
|
205
|
-
};
|
|
206
|
-
}
|
|
207
|
-
const betHashIteration = dbHashChain.current_iteration - 1;
|
|
208
|
-
assert(betHashIteration > 0, "Bet hash iteration must be > 0");
|
|
209
|
-
const betHashResult = await getIntermediateHash({
|
|
210
|
-
hashChainId: input.hashChainId,
|
|
211
|
-
iteration: betHashIteration,
|
|
212
|
-
});
|
|
213
|
-
switch (betHashResult.type) {
|
|
214
|
-
case "success":
|
|
215
|
-
break;
|
|
216
|
-
case "bad_hash_chain":
|
|
217
|
-
return {
|
|
218
|
-
__typename: "HubBadHashChainError",
|
|
219
|
-
message: "Hash chain not found",
|
|
220
|
-
};
|
|
221
|
-
default: {
|
|
222
|
-
const _exhaustiveCheck = betHashResult;
|
|
223
|
-
throw new Error(`Unknown bet hash result: ${_exhaustiveCheck}`);
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
await dbInsertHubHash(pgClient, {
|
|
227
|
-
hashChainId: dbHashChain.id,
|
|
228
|
-
kind: DbHashKind.INTERMEDIATE,
|
|
229
|
-
digest: betHashResult.hash,
|
|
230
|
-
iteration: betHashIteration,
|
|
231
|
-
});
|
|
232
|
-
const result = await pgClient.query(`
|
|
233
|
-
UPDATE hub.hash_chain
|
|
234
|
-
SET current_iteration = $2
|
|
235
|
-
WHERE id = $1
|
|
236
|
-
`, [dbHashChain.id, betHashIteration]);
|
|
237
|
-
if (result.rowCount !== 1) {
|
|
238
|
-
throw new GraphQLError("Failed to update hash chain iteration");
|
|
239
|
-
}
|
|
240
|
-
const finalHash = (() => {
|
|
241
|
-
const serverHash = betHashResult.hash;
|
|
242
|
-
const clientSeed = dbHashChain.client_seed;
|
|
243
|
-
const finalHash = createHmac("sha256", serverHash)
|
|
244
|
-
.update(clientSeed)
|
|
245
|
-
.digest();
|
|
246
|
-
return finalHash;
|
|
247
|
-
})();
|
|
248
|
-
const { outcome, outcomeIdx } = pickRandomOutcome({
|
|
249
|
-
outcomes: input.outcomes,
|
|
250
|
-
hash: finalHash,
|
|
251
|
-
});
|
|
161
|
+
const { outcome, outcomeIdx } = pickRandomOutcome(input.outcomes);
|
|
252
162
|
const netPlayerAmount = input.wager * outcome.profit;
|
|
253
163
|
await pgClient.query({
|
|
254
164
|
text: `
|
|
@@ -283,27 +193,17 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
283
193
|
input.wager,
|
|
284
194
|
],
|
|
285
195
|
});
|
|
286
|
-
const immutableData = structuredClone({
|
|
287
|
-
wager: input.wager,
|
|
288
|
-
currencyKey: dbCurrency.key,
|
|
289
|
-
clientSeed: dbHashChain.client_seed,
|
|
290
|
-
hash: betHashResult.hash,
|
|
291
|
-
outcomes: input.outcomes,
|
|
292
|
-
outcomeIdx,
|
|
293
|
-
});
|
|
294
|
-
const finalizedMetadata = betConfig.finalizeMetadata
|
|
295
|
-
? betConfig.finalizeMetadata(initializedMetadata, immutableData)
|
|
296
|
-
: initializedMetadata;
|
|
297
196
|
const newBet = {
|
|
298
197
|
kind: rawInput.kind,
|
|
299
198
|
wager: input.wager,
|
|
300
199
|
profit: outcome.profit,
|
|
301
200
|
currency_key: dbCurrency.key,
|
|
302
|
-
hash_chain_id: input.hashChainId,
|
|
303
201
|
user_id: session.user_id,
|
|
304
202
|
casino_id: session.casino_id,
|
|
305
203
|
experience_id: session.experience_id,
|
|
306
|
-
metadata:
|
|
204
|
+
metadata: betConfig.getMetadata
|
|
205
|
+
? await betConfig.getMetadata(input)
|
|
206
|
+
: {},
|
|
307
207
|
...(betConfig.saveOutcomes
|
|
308
208
|
? {
|
|
309
209
|
outcomes: input.outcomes,
|
|
@@ -321,7 +221,6 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
321
221
|
user_id,
|
|
322
222
|
casino_id,
|
|
323
223
|
experience_id,
|
|
324
|
-
hash_chain_id,
|
|
325
224
|
kind,
|
|
326
225
|
currency_key,
|
|
327
226
|
wager,
|
|
@@ -330,14 +229,13 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
330
229
|
outcome_idx,
|
|
331
230
|
metadata
|
|
332
231
|
)
|
|
333
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10
|
|
232
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
|
334
233
|
RETURNING id
|
|
335
234
|
`,
|
|
336
235
|
values: [
|
|
337
236
|
newBet.user_id,
|
|
338
237
|
newBet.casino_id,
|
|
339
238
|
newBet.experience_id,
|
|
340
|
-
newBet.hash_chain_id,
|
|
341
239
|
newBet.kind,
|
|
342
240
|
newBet.currency_key,
|
|
343
241
|
newBet.wager,
|
|
@@ -348,31 +246,11 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
348
246
|
],
|
|
349
247
|
})
|
|
350
248
|
.then(exactlyOneRow);
|
|
351
|
-
return
|
|
352
|
-
__typename: "HubMakeOutcomeBetSuccess",
|
|
353
|
-
betId: bet.id,
|
|
354
|
-
};
|
|
249
|
+
return bet.id;
|
|
355
250
|
});
|
|
356
251
|
});
|
|
357
252
|
return object({
|
|
358
|
-
|
|
359
|
-
});
|
|
360
|
-
},
|
|
361
|
-
},
|
|
362
|
-
HubMakeOutcomeBetSuccess: {
|
|
363
|
-
__assertStep: ObjectStep,
|
|
364
|
-
bet($data) {
|
|
365
|
-
const $betId = access($data, "betId");
|
|
366
|
-
return outcomeBetTable.get({ id: $betId });
|
|
367
|
-
},
|
|
368
|
-
},
|
|
369
|
-
HubMakeOutcomeBetPayload: {
|
|
370
|
-
__assertStep: ObjectStep,
|
|
371
|
-
result($data) {
|
|
372
|
-
const $result = $data.get("result");
|
|
373
|
-
return polymorphicBranch($result, {
|
|
374
|
-
HubMakeOutcomeBetSuccess: {},
|
|
375
|
-
HubBadHashChainError: {},
|
|
253
|
+
bet: outcomeBetTable.get({ id: $betId }),
|
|
376
254
|
});
|
|
377
255
|
},
|
|
378
256
|
},
|
|
@@ -380,13 +258,12 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
380
258
|
};
|
|
381
259
|
}, "HubMakeOutcomeBetPlugin");
|
|
382
260
|
}
|
|
383
|
-
function
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
return uint32Value / Math.pow(2, 32);
|
|
261
|
+
function generateRandomNumber() {
|
|
262
|
+
const array = new Uint32Array(1);
|
|
263
|
+
crypto.getRandomValues(array);
|
|
264
|
+
return array[0] / 2 ** 32;
|
|
388
265
|
}
|
|
389
|
-
function pickRandomOutcome(
|
|
266
|
+
function pickRandomOutcome(outcomes) {
|
|
390
267
|
assert(outcomes.length >= 2, "Outcome count must be >= 2");
|
|
391
268
|
const totalWeight = sum(outcomes.map((o) => o.weight));
|
|
392
269
|
const outcomesWithProbability = outcomes.map((o) => ({
|
|
@@ -395,12 +272,12 @@ function pickRandomOutcome({ outcomes, hash, }) {
|
|
|
395
272
|
}));
|
|
396
273
|
const totalProb = outcomesWithProbability.reduce((sum, outcome) => sum + outcome.probability, 0);
|
|
397
274
|
assert(Math.abs(totalProb - 1.0) < FLOAT_EPSILON, "Probabilities must sum to ~1");
|
|
398
|
-
const randomValue =
|
|
399
|
-
let
|
|
275
|
+
const randomValue = generateRandomNumber();
|
|
276
|
+
let cumProb = 0;
|
|
400
277
|
for (let i = 0; i < outcomesWithProbability.length; i++) {
|
|
401
278
|
const outcome = outcomesWithProbability[i];
|
|
402
|
-
|
|
403
|
-
if (randomValue <=
|
|
279
|
+
cumProb += outcome.probability;
|
|
280
|
+
if (randomValue <= cumProb) {
|
|
404
281
|
return { outcome, outcomeIdx: i };
|
|
405
282
|
}
|
|
406
283
|
}
|
|
@@ -446,41 +323,3 @@ async function dbLockBalanceAndBankroll(pgClient, { userId, casinoId, experience
|
|
|
446
323
|
dbHouseBankroll: null,
|
|
447
324
|
};
|
|
448
325
|
}
|
|
449
|
-
async function finishHashChainInBackground({ hashChainId, }) {
|
|
450
|
-
console.log("Finishing hash chain in background", { hashChainId });
|
|
451
|
-
const preimageHashResult = await getPreimageHash({
|
|
452
|
-
hashChainId,
|
|
453
|
-
});
|
|
454
|
-
console.log("Preimage hash result", { preimageHashResult });
|
|
455
|
-
if (preimageHashResult.type === "success") {
|
|
456
|
-
console.log("Inserting preimage hash", {
|
|
457
|
-
hashChainId,
|
|
458
|
-
kind: DbHashKind.PREIMAGE,
|
|
459
|
-
digest: preimageHashResult.hash,
|
|
460
|
-
iteration: 0,
|
|
461
|
-
});
|
|
462
|
-
await withPgPoolTransaction(superuserPool, async (pgClient) => {
|
|
463
|
-
await dbInsertHubHash(pgClient, {
|
|
464
|
-
hashChainId,
|
|
465
|
-
kind: DbHashKind.PREIMAGE,
|
|
466
|
-
digest: preimageHashResult.hash,
|
|
467
|
-
iteration: 0,
|
|
468
|
-
});
|
|
469
|
-
const result = await pgClient.query(`
|
|
470
|
-
UPDATE hub.hash_chain
|
|
471
|
-
SET current_iteration = 0,
|
|
472
|
-
active = false
|
|
473
|
-
WHERE id = $1
|
|
474
|
-
`, [hashChainId]);
|
|
475
|
-
if (result.rowCount !== 1) {
|
|
476
|
-
throw new Error("Failed to update hash chain iteration");
|
|
477
|
-
}
|
|
478
|
-
});
|
|
479
|
-
}
|
|
480
|
-
else {
|
|
481
|
-
console.warn("Failed to insert preimage hash in background", {
|
|
482
|
-
hashChainId,
|
|
483
|
-
error: preimageHashResult,
|
|
484
|
-
});
|
|
485
|
-
}
|
|
486
|
-
}
|
|
@@ -1,30 +1,55 @@
|
|
|
1
|
-
import { access,
|
|
1
|
+
import { access, context, loadOne, object, } from "postgraphile/grafast";
|
|
2
2
|
import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { superuserPool } from "../db/index.js";
|
|
4
|
+
import { pgSelectSingleFromRecord, } from "postgraphile/@dataplan/pg";
|
|
5
5
|
export const HubUserBalanceByCurrencyPlugin = makeExtendSchemaPlugin((build) => {
|
|
6
|
-
const
|
|
6
|
+
const balances = build.input.pgRegistry.pgResources.hub_balance;
|
|
7
7
|
return {
|
|
8
8
|
typeDefs: gql `
|
|
9
9
|
extend type HubUser {
|
|
10
|
-
|
|
10
|
+
balanceByCurrency(currency: String!): HubBalance
|
|
11
11
|
}
|
|
12
12
|
`,
|
|
13
13
|
plans: {
|
|
14
14
|
HubUser: {
|
|
15
|
-
|
|
15
|
+
balanceByCurrency: ($record, { $currency }) => {
|
|
16
16
|
const $identity = context().get("identity");
|
|
17
|
-
const $
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
$
|
|
25
|
-
return $balances.single();
|
|
17
|
+
const $params = object({
|
|
18
|
+
currency: $currency,
|
|
19
|
+
targetUserId: $record.get("id"),
|
|
20
|
+
casino_id: access($identity, ["session", "casino_id"]),
|
|
21
|
+
experience_id: access($identity, ["session", "experience_id"]),
|
|
22
|
+
});
|
|
23
|
+
const $balance = loadOne($params, batchGetUserBalanceByCurrency);
|
|
24
|
+
return pgSelectSingleFromRecord(balances, $balance);
|
|
26
25
|
},
|
|
27
26
|
},
|
|
28
27
|
},
|
|
29
28
|
};
|
|
30
29
|
});
|
|
30
|
+
async function batchGetUserBalanceByCurrency(paramsArray) {
|
|
31
|
+
const values = [];
|
|
32
|
+
const valuePlaceholders = [];
|
|
33
|
+
paramsArray.forEach((p, index) => {
|
|
34
|
+
const baseIndex = index * 4 + 1;
|
|
35
|
+
valuePlaceholders.push(`($${baseIndex}, $${baseIndex + 1}::uuid, $${baseIndex + 2}::uuid, $${baseIndex + 3}::uuid)`);
|
|
36
|
+
values.push(p.currency, p.targetUserId, p.casino_id, p.experience_id);
|
|
37
|
+
});
|
|
38
|
+
const sql = `
|
|
39
|
+
SELECT b.*
|
|
40
|
+
FROM hub.balance b
|
|
41
|
+
JOIN (
|
|
42
|
+
VALUES
|
|
43
|
+
${valuePlaceholders.join(",\n ")}
|
|
44
|
+
) AS vals(currency_key, user_id, casino_id, experience_id)
|
|
45
|
+
ON b.currency_key = vals.currency_key
|
|
46
|
+
AND b.user_id = vals.user_id
|
|
47
|
+
AND b.casino_id = vals.casino_id
|
|
48
|
+
AND b.experience_id = vals.experience_id
|
|
49
|
+
`;
|
|
50
|
+
const { rows } = await superuserPool.query(sql, values);
|
|
51
|
+
return paramsArray.map((p) => rows.find((row) => row.currency_key === p.currency &&
|
|
52
|
+
row.user_id === p.targetUserId &&
|
|
53
|
+
row.casino_id === p.casino_id &&
|
|
54
|
+
row.experience_id === p.experience_id));
|
|
55
|
+
}
|
|
@@ -6,7 +6,7 @@ import EventEmitter from "events";
|
|
|
6
6
|
import { superuserPool } from "./db/index.js";
|
|
7
7
|
import { dbGetCasinoById, dbGetCasinoSecretById } from "./db/internal.js";
|
|
8
8
|
import pg from "pg";
|
|
9
|
-
import
|
|
9
|
+
import config from "./config.js";
|
|
10
10
|
import { z } from "zod";
|
|
11
11
|
import { gql } from "./__generated__/gql.js";
|
|
12
12
|
import { logger } from "./logger.js";
|
|
@@ -2,7 +2,7 @@ import "graphile-config";
|
|
|
2
2
|
import "postgraphile";
|
|
3
3
|
import { makePgService } from "postgraphile/adaptors/pg";
|
|
4
4
|
import { PostGraphileAmberPreset } from "postgraphile/presets/amber";
|
|
5
|
-
import
|
|
5
|
+
import config from "../config.js";
|
|
6
6
|
import { maskError } from "./handle-errors.js";
|
|
7
7
|
import * as db from "../db/index.js";
|
|
8
8
|
import { SmartTagsPlugin } from "../smart-tags.js";
|
|
@@ -20,10 +20,6 @@ import { HubAddCasinoPlugin } from "../plugins/hub-add-casino.js";
|
|
|
20
20
|
import { HubBalanceAlertPlugin } from "../plugins/hub-balance-alert.js";
|
|
21
21
|
import { custom as customPgOmitArchivedPlugin } from "@graphile-contrib/pg-omit-archived";
|
|
22
22
|
import { HubCurrentXPlugin } from "../plugins/hub-current-x.js";
|
|
23
|
-
import { HubCreateHashChainPlugin } from "../hash-chain/plugins/hub-create-hash-chain.js";
|
|
24
|
-
import { HubBadHashChainErrorPlugin } from "../hash-chain/plugins/hub-bad-hash-chain-error.js";
|
|
25
|
-
import { HubUserActiveHashChainPlugin } from "../hash-chain/plugins/hub-user-active-hash-chain.js";
|
|
26
|
-
import { HubOutcomeInputNonNullFieldsPlugin } from "../plugins/hub-outcome-input-non-null-fields.js";
|
|
27
23
|
export const requiredPlugins = [
|
|
28
24
|
SmartTagsPlugin,
|
|
29
25
|
IdToNodeIdPlugin,
|
|
@@ -39,10 +35,6 @@ export const requiredPlugins = [
|
|
|
39
35
|
export const defaultPlugins = [
|
|
40
36
|
...(config.NODE_ENV === "development" ? [DebugPlugin] : []),
|
|
41
37
|
...requiredPlugins,
|
|
42
|
-
HubOutcomeInputNonNullFieldsPlugin,
|
|
43
|
-
HubBadHashChainErrorPlugin,
|
|
44
|
-
HubCreateHashChainPlugin,
|
|
45
|
-
HubUserActiveHashChainPlugin,
|
|
46
38
|
HubClaimFaucetPlugin,
|
|
47
39
|
customPgOmitArchivedPlugin("deleted"),
|
|
48
40
|
];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GraphQLError } from "postgraphile/graphql";
|
|
2
|
-
import
|
|
2
|
+
import config from "../config.js";
|
|
3
3
|
const isDev = config.NODE_ENV === "development";
|
|
4
4
|
const isTest = config.NODE_ENV === "test";
|
|
5
5
|
const camelCase = (s) => s.toLowerCase().replace(/(_\\w)/g, (m) => m[1].toUpperCase());
|
package/dist/src/server/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { grafserv } from "grafserv/express/v4";
|
|
|
3
3
|
import postgraphile from "postgraphile";
|
|
4
4
|
import { createPreset, defaultPlugins } from "./graphile.config.js";
|
|
5
5
|
import express from "express";
|
|
6
|
-
import
|
|
6
|
+
import config from "../config.js";
|
|
7
7
|
import { logger } from "../logger.js";
|
|
8
8
|
import cors from "./middleware/cors.js";
|
|
9
9
|
import authentication from "./middleware/authentication.js";
|
|
@@ -2,7 +2,7 @@ import { gql } from "../__generated__/gql.js";
|
|
|
2
2
|
import { TakeRequestStatus as MpTakeRequestStatus, TransferStatusKind as MpTransferStatus, } from "../__generated__/graphql.js";
|
|
3
3
|
import { exactlyOneRow, maybeOneRow, superuserPool, withPgPoolTransaction, } from "../db/index.js";
|
|
4
4
|
import { assert } from "tsafe";
|
|
5
|
-
import {
|
|
5
|
+
import { pgAdvisoryLock } from "../pg-advisory-lock.js";
|
|
6
6
|
const MP_PAGINATE_PENDING_TAKE_REQUESTS = gql(`
|
|
7
7
|
query MpPaginatedPendingTakeRequests($controllerId: UUID!, $after: Cursor) {
|
|
8
8
|
allTakeRequests(
|
|
@@ -178,7 +178,7 @@ async function fetchPendingTakeRequests(graphqlClient, controllerId) {
|
|
|
178
178
|
}
|
|
179
179
|
async function processSingleTakeRequest({ mpTakeRequestId, mpTakeRequest, casinoId, graphqlClient, }) {
|
|
180
180
|
return withPgPoolTransaction(superuserPool, async (pgClient) => {
|
|
181
|
-
await
|
|
181
|
+
await pgAdvisoryLock.forMpTakeRequestProcessing(pgClient, {
|
|
182
182
|
mpTakeRequestId,
|
|
183
183
|
casinoId,
|
|
184
184
|
});
|
|
@@ -457,7 +457,7 @@ async function processPendingTransferCompletions({ casinoId, graphqlClient, abor
|
|
|
457
457
|
}
|
|
458
458
|
async function completeTransfer({ mpTakeRequestId, takeRequestId, mpTransferId, graphqlClient, casinoId, }) {
|
|
459
459
|
return withPgPoolTransaction(superuserPool, async (pgClient) => {
|
|
460
|
-
await
|
|
460
|
+
await pgAdvisoryLock.forMpTakeRequestProcessing(pgClient, {
|
|
461
461
|
mpTakeRequestId,
|
|
462
462
|
casinoId,
|
|
463
463
|
});
|
package/package.json
CHANGED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { DbCasino, DbExperience, DbHash, DbHashChain, DbUser } from "../db/types.js";
|
|
2
|
-
import { PgClientInTransaction } from "../db/index.js";
|
|
3
|
-
export declare function dbLockHubHashChain(pgClient: PgClientInTransaction, { userId, experienceId, casinoId, hashChainId, }: {
|
|
4
|
-
userId: DbUser["id"];
|
|
5
|
-
experienceId: DbExperience["id"];
|
|
6
|
-
casinoId: DbCasino["id"];
|
|
7
|
-
hashChainId: DbHashChain["id"];
|
|
8
|
-
}): Promise<DbHashChain | null>;
|
|
9
|
-
export declare function dbInsertHubHash(pgClient: PgClientInTransaction, { hashChainId, kind, digest, iteration, metadata, }: {
|
|
10
|
-
hashChainId: DbHashChain["id"];
|
|
11
|
-
kind: DbHash["kind"];
|
|
12
|
-
digest: DbHash["digest"];
|
|
13
|
-
iteration: number;
|
|
14
|
-
metadata?: DbHash["metadata"];
|
|
15
|
-
}): Promise<DbHash>;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { exactlyOneRow, maybeOneRow, } from "../db/index.js";
|
|
2
|
-
import { assert } from "tsafe";
|
|
3
|
-
export async function dbLockHubHashChain(pgClient, { userId, experienceId, casinoId, hashChainId, }) {
|
|
4
|
-
assert(pgClient._inTransaction, "dbLockHubHashChain must be called in a transaction");
|
|
5
|
-
return pgClient
|
|
6
|
-
.query(`
|
|
7
|
-
SELECT *
|
|
8
|
-
FROM hub.hash_chain
|
|
9
|
-
WHERE id = $1
|
|
10
|
-
AND user_id = $2
|
|
11
|
-
AND experience_id = $3
|
|
12
|
-
AND casino_id = $4
|
|
13
|
-
AND active = TRUE
|
|
14
|
-
|
|
15
|
-
FOR UPDATE
|
|
16
|
-
`, [hashChainId, userId, experienceId, casinoId])
|
|
17
|
-
.then(maybeOneRow)
|
|
18
|
-
.then((row) => row ?? null);
|
|
19
|
-
}
|
|
20
|
-
export async function dbInsertHubHash(pgClient, { hashChainId, kind, digest, iteration, metadata = {}, }) {
|
|
21
|
-
assert(pgClient._inTransaction, "dbInsertHash must be called in a transaction");
|
|
22
|
-
return pgClient
|
|
23
|
-
.query(`
|
|
24
|
-
INSERT INTO hub.hash (hash_chain_id, kind, digest, iteration, metadata)
|
|
25
|
-
VALUES ($1, $2, $3, $4, $5)
|
|
26
|
-
RETURNING *
|
|
27
|
-
`, [
|
|
28
|
-
hashChainId,
|
|
29
|
-
kind,
|
|
30
|
-
digest,
|
|
31
|
-
iteration,
|
|
32
|
-
metadata,
|
|
33
|
-
])
|
|
34
|
-
.then(exactlyOneRow);
|
|
35
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
export type HashResult = {
|
|
2
|
-
type: "bad_hash_chain";
|
|
3
|
-
reason: "hashchain_too_old" | "empty_response";
|
|
4
|
-
} | {
|
|
5
|
-
type: "success";
|
|
6
|
-
hash: Uint8Array;
|
|
7
|
-
};
|
|
8
|
-
export declare function getIntermediateHash({ hashChainId, iteration, }: {
|
|
9
|
-
hashChainId: string;
|
|
10
|
-
iteration: number;
|
|
11
|
-
}): Promise<HashResult>;
|
|
12
|
-
export declare function getPreimageHash({ hashChainId, }: {
|
|
13
|
-
hashChainId: string;
|
|
14
|
-
}): Promise<HashResult>;
|
|
15
|
-
export declare function getTerminalHash({ hashChainId, }: {
|
|
16
|
-
hashChainId: string;
|
|
17
|
-
}): Promise<HashResult>;
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { getHash } from "@moneypot/hash-herald";
|
|
2
|
-
import * as config from "../config.js";
|
|
3
|
-
const HASH_HERALD_OPTIONS = {
|
|
4
|
-
baseUrl: config.HASHCHAINSERVER_URL,
|
|
5
|
-
applicationSecret: config.HASHCHAINSERVER_APPLICATION_SECRET,
|
|
6
|
-
};
|
|
7
|
-
function resultFromGetHashResponse(response) {
|
|
8
|
-
if (!response.resp) {
|
|
9
|
-
return { type: "bad_hash_chain", reason: "empty_response" };
|
|
10
|
-
}
|
|
11
|
-
const $case = response.resp.$case;
|
|
12
|
-
switch ($case) {
|
|
13
|
-
case "hash":
|
|
14
|
-
return { type: "success", hash: response.resp.value };
|
|
15
|
-
case "hashchainTooOldError":
|
|
16
|
-
return { type: "bad_hash_chain", reason: "hashchain_too_old" };
|
|
17
|
-
default: {
|
|
18
|
-
const _exhaustiveCheck = $case;
|
|
19
|
-
throw new Error(`Unknown hash response: ${_exhaustiveCheck}`);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
export async function getIntermediateHash({ hashChainId, iteration, }) {
|
|
24
|
-
const req = {
|
|
25
|
-
hashchainId: hashChainId,
|
|
26
|
-
iterations: iteration,
|
|
27
|
-
context: undefined,
|
|
28
|
-
};
|
|
29
|
-
return getHash(HASH_HERALD_OPTIONS, req).then(resultFromGetHashResponse);
|
|
30
|
-
}
|
|
31
|
-
export async function getPreimageHash({ hashChainId, }) {
|
|
32
|
-
const req = {
|
|
33
|
-
hashchainId: hashChainId,
|
|
34
|
-
iterations: 0,
|
|
35
|
-
context: {
|
|
36
|
-
event: {
|
|
37
|
-
$case: "fetchingPreimage",
|
|
38
|
-
value: {},
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
return getHash(HASH_HERALD_OPTIONS, req).then(resultFromGetHashResponse);
|
|
43
|
-
}
|
|
44
|
-
export async function getTerminalHash({ hashChainId, }) {
|
|
45
|
-
const iterations = 1_000;
|
|
46
|
-
const req = {
|
|
47
|
-
hashchainId: hashChainId,
|
|
48
|
-
iterations,
|
|
49
|
-
context: {
|
|
50
|
-
event: {
|
|
51
|
-
$case: "fetchingTerminalHash",
|
|
52
|
-
value: {},
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
return getHash(HASH_HERALD_OPTIONS, req).then(resultFromGetHashResponse);
|
|
57
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const HubBadHashChainErrorPlugin: GraphileConfig.Plugin;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { ObjectStep, access } from "postgraphile/grafast";
|
|
2
|
-
import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
|
|
3
|
-
export const HubBadHashChainErrorPlugin = makeExtendSchemaPlugin(() => {
|
|
4
|
-
return {
|
|
5
|
-
typeDefs: gql `
|
|
6
|
-
type HubBadHashChainError {
|
|
7
|
-
message: String
|
|
8
|
-
}
|
|
9
|
-
`,
|
|
10
|
-
plans: {
|
|
11
|
-
HubBadHashChainError: {
|
|
12
|
-
__assertStep: ObjectStep,
|
|
13
|
-
message($data) {
|
|
14
|
-
const $message = access($data, "message");
|
|
15
|
-
return $message;
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
}, "HubBadHashChainErrorPlugin");
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare const HubCreateHashChainPlugin: GraphileConfig.Plugin;
|