@moneypot/hub 1.4.4 → 1.4.5
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/db/types.d.ts +0 -1
- package/dist/src/hash-chain/plugins/hub-create-hash-chain.js +4 -22
- package/dist/src/pg-versions/008-move-client-seed.sql +9 -0
- package/dist/src/plugins/hub-make-outcome-bet.d.ts +5 -1
- package/dist/src/plugins/hub-make-outcome-bet.js +15 -8
- package/package.json +1 -1
package/dist/src/db/types.d.ts
CHANGED
|
@@ -1,47 +1,31 @@
|
|
|
1
1
|
import { context, object, sideEffect } from "@moneypot/hub/grafast";
|
|
2
2
|
import { gql, makeExtendSchemaPlugin } from "@moneypot/hub/graphile";
|
|
3
3
|
import { GraphQLError } from "graphql";
|
|
4
|
-
import { z } from "zod";
|
|
5
4
|
import { PgAdvisoryLock } from "../../pg-advisory-lock.js";
|
|
6
5
|
import { exactlyOneRow, superuserPool, withPgPoolTransaction, } from "@moneypot/hub/db";
|
|
7
6
|
import * as HashCommon from "../get-hash.js";
|
|
8
7
|
import { DbHashKind } from "../../db/types.js";
|
|
9
8
|
import * as config from "../../config.js";
|
|
10
|
-
const InputSchema = z.object({
|
|
11
|
-
clientSeed: z.string(),
|
|
12
|
-
});
|
|
13
9
|
export const HubCreateHashChainPlugin = makeExtendSchemaPlugin((build) => {
|
|
14
10
|
const hashChainTable = build.input.pgRegistry.pgResources.hub_hash_chain;
|
|
15
11
|
return {
|
|
16
12
|
typeDefs: gql `
|
|
17
|
-
input HubCreateHashChainInput {
|
|
18
|
-
clientSeed: String!
|
|
19
|
-
}
|
|
20
|
-
|
|
21
13
|
type HubCreateHashChainPayload {
|
|
22
14
|
hashChain: HubHashChain!
|
|
23
15
|
}
|
|
24
16
|
|
|
25
17
|
extend type Mutation {
|
|
26
|
-
hubCreateHashChain
|
|
27
|
-
input: HubCreateHashChainInput!
|
|
28
|
-
): HubCreateHashChainPayload!
|
|
18
|
+
hubCreateHashChain: HubCreateHashChainPayload!
|
|
29
19
|
}
|
|
30
20
|
`,
|
|
31
21
|
plans: {
|
|
32
22
|
Mutation: {
|
|
33
|
-
hubCreateHashChain: (
|
|
23
|
+
hubCreateHashChain: () => {
|
|
34
24
|
const $identity = context().get("identity");
|
|
35
|
-
const $hashChainId = sideEffect([$
|
|
25
|
+
const $hashChainId = sideEffect([$identity], ([identity]) => {
|
|
36
26
|
if (identity?.kind !== "user") {
|
|
37
27
|
throw new GraphQLError("Unauthorized");
|
|
38
28
|
}
|
|
39
|
-
const result = InputSchema.safeParse(rawInput);
|
|
40
|
-
if (!result.success) {
|
|
41
|
-
const message = result.error.errors[0].message;
|
|
42
|
-
throw new GraphQLError(message);
|
|
43
|
-
}
|
|
44
|
-
const { clientSeed } = result.data;
|
|
45
29
|
return withPgPoolTransaction(superuserPool, async (pgClient) => {
|
|
46
30
|
await PgAdvisoryLock.forNewHashChain(pgClient, {
|
|
47
31
|
userId: identity.session.user_id,
|
|
@@ -66,18 +50,16 @@ export const HubCreateHashChainPlugin = makeExtendSchemaPlugin((build) => {
|
|
|
66
50
|
user_id,
|
|
67
51
|
experience_id,
|
|
68
52
|
casino_id,
|
|
69
|
-
client_seed,
|
|
70
53
|
active,
|
|
71
54
|
max_iteration,
|
|
72
55
|
current_iteration
|
|
73
56
|
)
|
|
74
|
-
VALUES ($1, $2, $3,
|
|
57
|
+
VALUES ($1, $2, $3, true, $4, $4)
|
|
75
58
|
RETURNING *
|
|
76
59
|
`, [
|
|
77
60
|
identity.session.user_id,
|
|
78
61
|
identity.session.experience_id,
|
|
79
62
|
identity.session.casino_id,
|
|
80
|
-
clientSeed,
|
|
81
63
|
config.HASHCHAINSERVER_MAX_ITERATIONS,
|
|
82
64
|
])
|
|
83
65
|
.then(exactlyOneRow);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
alter table hub.outcome_bet add column client_seed text;
|
|
2
|
+
|
|
3
|
+
update hub.outcome_bet
|
|
4
|
+
set client_seed = hash_chain.client_seed
|
|
5
|
+
from hub.hash_chain where outcome_bet.hash_chain_id = hash_chain.id;
|
|
6
|
+
|
|
7
|
+
alter table hub.outcome_bet alter column client_seed set not null;
|
|
8
|
+
|
|
9
|
+
alter table hub.hash_chain drop column client_seed;
|
|
@@ -12,6 +12,7 @@ declare const OutcomeSchema: z.ZodObject<{
|
|
|
12
12
|
}>;
|
|
13
13
|
declare const InputSchema: z.ZodObject<{
|
|
14
14
|
kind: z.ZodString;
|
|
15
|
+
clientSeed: z.ZodString;
|
|
15
16
|
wager: z.ZodNumber;
|
|
16
17
|
currency: z.ZodString;
|
|
17
18
|
outcomes: z.ZodEffects<z.ZodEffects<z.ZodArray<z.ZodObject<{
|
|
@@ -42,6 +43,7 @@ declare const InputSchema: z.ZodObject<{
|
|
|
42
43
|
currency: string;
|
|
43
44
|
hashChainId: string;
|
|
44
45
|
kind: string;
|
|
46
|
+
clientSeed: string;
|
|
45
47
|
wager: number;
|
|
46
48
|
outcomes: {
|
|
47
49
|
profit: number;
|
|
@@ -52,6 +54,7 @@ declare const InputSchema: z.ZodObject<{
|
|
|
52
54
|
currency: string;
|
|
53
55
|
hashChainId: string;
|
|
54
56
|
kind: string;
|
|
57
|
+
clientSeed: string;
|
|
55
58
|
wager: number;
|
|
56
59
|
outcomes: {
|
|
57
60
|
profit: number;
|
|
@@ -69,11 +72,12 @@ type FinalizeMetadataData = {
|
|
|
69
72
|
hash: Uint8Array;
|
|
70
73
|
outcomes: Outcome[];
|
|
71
74
|
outcomeIdx: number;
|
|
75
|
+
roll: number;
|
|
72
76
|
};
|
|
73
77
|
export type OutcomeBetConfig = {
|
|
74
78
|
houseEdge: number;
|
|
75
79
|
saveOutcomes: boolean;
|
|
76
|
-
allowLossBeyondWager
|
|
80
|
+
allowLossBeyondWager?: boolean;
|
|
77
81
|
initializeMetadataFromUntrustedUserInput?: (input: Input) => Result<Metadata, string>;
|
|
78
82
|
finalizeMetadata?: (validatedMetadata: Metadata, data: FinalizeMetadataData) => Metadata;
|
|
79
83
|
};
|
|
@@ -27,6 +27,7 @@ const OutcomeSchema = z
|
|
|
27
27
|
const InputSchema = z
|
|
28
28
|
.object({
|
|
29
29
|
kind: z.string(),
|
|
30
|
+
clientSeed: z.string(),
|
|
30
31
|
wager: z
|
|
31
32
|
.number()
|
|
32
33
|
.int("Wager must be an integer")
|
|
@@ -53,7 +54,7 @@ const BetConfigsSchema = z.record(BetKindSchema, z.object({
|
|
|
53
54
|
.gte(0, "House edge must be >= 0")
|
|
54
55
|
.lte(1, "House edge must be <= 1"),
|
|
55
56
|
saveOutcomes: z.boolean(),
|
|
56
|
-
allowLossBeyondWager: z.boolean().
|
|
57
|
+
allowLossBeyondWager: z.boolean().default(false),
|
|
57
58
|
initializeMetadataFromUntrustedUserInput: z
|
|
58
59
|
.function()
|
|
59
60
|
.optional(),
|
|
@@ -77,6 +78,7 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
77
78
|
currency: String!
|
|
78
79
|
outcomes: [HubOutcomeInput!]!
|
|
79
80
|
hashChainId: UUID!
|
|
81
|
+
clientSeed: String!
|
|
80
82
|
metadata: JSON
|
|
81
83
|
}
|
|
82
84
|
|
|
@@ -246,13 +248,13 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
246
248
|
}
|
|
247
249
|
const finalHash = (() => {
|
|
248
250
|
const serverHash = betHashResult.hash;
|
|
249
|
-
const clientSeed =
|
|
251
|
+
const clientSeed = input.clientSeed;
|
|
250
252
|
const finalHash = createHmac("sha256", serverHash)
|
|
251
253
|
.update(clientSeed)
|
|
252
254
|
.digest();
|
|
253
255
|
return finalHash;
|
|
254
256
|
})();
|
|
255
|
-
const { outcome, outcomeIdx } = pickRandomOutcome({
|
|
257
|
+
const { outcome, outcomeIdx, roll } = pickRandomOutcome({
|
|
256
258
|
outcomes: input.outcomes,
|
|
257
259
|
hash: finalHash,
|
|
258
260
|
});
|
|
@@ -293,10 +295,11 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
293
295
|
const immutableData = structuredClone({
|
|
294
296
|
wager: input.wager,
|
|
295
297
|
currencyKey: dbCurrency.key,
|
|
296
|
-
clientSeed:
|
|
298
|
+
clientSeed: input.clientSeed,
|
|
297
299
|
hash: betHashResult.hash,
|
|
298
300
|
outcomes: input.outcomes,
|
|
299
301
|
outcomeIdx,
|
|
302
|
+
roll,
|
|
300
303
|
});
|
|
301
304
|
const finalizedMetadata = betConfig.finalizeMetadata
|
|
302
305
|
? betConfig.finalizeMetadata(initializedMetadata, immutableData)
|
|
@@ -310,6 +313,7 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
310
313
|
user_id: session.user_id,
|
|
311
314
|
casino_id: session.casino_id,
|
|
312
315
|
experience_id: session.experience_id,
|
|
316
|
+
client_seed: input.clientSeed,
|
|
313
317
|
metadata: finalizedMetadata || {},
|
|
314
318
|
...(betConfig.saveOutcomes
|
|
315
319
|
? {
|
|
@@ -335,9 +339,10 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
335
339
|
profit,
|
|
336
340
|
outcomes,
|
|
337
341
|
outcome_idx,
|
|
342
|
+
client_seed,
|
|
338
343
|
metadata
|
|
339
344
|
)
|
|
340
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
|
|
345
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
341
346
|
RETURNING id
|
|
342
347
|
`,
|
|
343
348
|
values: [
|
|
@@ -351,6 +356,7 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
|
|
|
351
356
|
newBet.profit,
|
|
352
357
|
newBet.outcomes.map((o) => `(${o.weight},${o.profit})`),
|
|
353
358
|
newBet.outcome_idx,
|
|
359
|
+
newBet.client_seed,
|
|
354
360
|
newBet.metadata,
|
|
355
361
|
],
|
|
356
362
|
})
|
|
@@ -402,18 +408,19 @@ function pickRandomOutcome({ outcomes, hash, }) {
|
|
|
402
408
|
}));
|
|
403
409
|
const totalProb = outcomesWithProbability.reduce((sum, outcome) => sum + outcome.probability, 0);
|
|
404
410
|
assert(Math.abs(totalProb - 1.0) < FLOAT_EPSILON, "Probabilities must sum to ~1");
|
|
405
|
-
const
|
|
411
|
+
const roll = normalizeHash(hash);
|
|
406
412
|
let cumulativeProb = 0;
|
|
407
413
|
for (let i = 0; i < outcomesWithProbability.length; i++) {
|
|
408
414
|
const outcome = outcomesWithProbability[i];
|
|
409
415
|
cumulativeProb += outcome.probability;
|
|
410
|
-
if (
|
|
411
|
-
return { outcome, outcomeIdx: i };
|
|
416
|
+
if (roll <= cumulativeProb) {
|
|
417
|
+
return { outcome, outcomeIdx: i, roll };
|
|
412
418
|
}
|
|
413
419
|
}
|
|
414
420
|
return {
|
|
415
421
|
outcome: outcomes[outcomes.length - 1],
|
|
416
422
|
outcomeIdx: outcomes.length - 1,
|
|
423
|
+
roll,
|
|
417
424
|
};
|
|
418
425
|
}
|
|
419
426
|
async function finishHashChainInBackground({ hashChainId, }) {
|