@actuallyfair/verifier 0.0.5 → 0.0.7
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/cli/context-codec.js +18 -10
- package/dist/compute-wager.d.ts +21 -10
- package/dist/compute-wager.js +104 -62
- package/dist/duel-plinko-payouts.d.ts +15 -11
- package/dist/duel-plinko-payouts.js +530 -460
- package/dist/generated/context/index.d.ts +164 -189
- package/dist/generated/context/index.js +63 -48
- package/dist/generated/context/plinko.d.ts +53 -95
- package/dist/generated/context/plinko.js +184 -108
- package/package.json +1 -1
- package/protobuf/context/index.proto +10 -8
- package/protobuf/context/plinko.proto +30 -11
- package/src/cli/context-codec.ts +18 -10
- package/src/compute-wager.ts +140 -68
- package/src/duel-plinko-payouts.ts +549 -466
- package/src/generated/context/index.ts +68 -51
- package/src/generated/context/plinko.ts +179 -116
|
@@ -4,10 +4,18 @@ const context_1 = require("../generated/context");
|
|
|
4
4
|
function isMode(value) {
|
|
5
5
|
return value === "decode" || value === "encode";
|
|
6
6
|
}
|
|
7
|
-
function
|
|
8
|
-
const cleaned = input.replace(/\s+/g, "").replace(
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
function normalizeHex(input) {
|
|
8
|
+
const cleaned = input.replace(/\s+/g, "").replace(/^0x/i, "");
|
|
9
|
+
if (!cleaned) {
|
|
10
|
+
return cleaned;
|
|
11
|
+
}
|
|
12
|
+
if (cleaned.length % 2 !== 0) {
|
|
13
|
+
throw new Error("Hex input must have an even length.");
|
|
14
|
+
}
|
|
15
|
+
if (!/^[0-9a-fA-F]+$/.test(cleaned)) {
|
|
16
|
+
throw new Error("Hex input contains non-hex characters.");
|
|
17
|
+
}
|
|
18
|
+
return cleaned;
|
|
11
19
|
}
|
|
12
20
|
function readStdin() {
|
|
13
21
|
return new Promise((resolve, reject) => {
|
|
@@ -32,15 +40,15 @@ async function readInput(args) {
|
|
|
32
40
|
}
|
|
33
41
|
function usage(mode) {
|
|
34
42
|
if (mode === "decode") {
|
|
35
|
-
return ["Usage: npm run decode -- <
|
|
43
|
+
return ["Usage: npm run decode -- <hex>", " cat context.hex | npm run decode"];
|
|
36
44
|
}
|
|
37
45
|
if (mode === "encode") {
|
|
38
46
|
return ["Usage: npm run encode -- '<json>'", " cat context.json | npm run encode"];
|
|
39
47
|
}
|
|
40
48
|
return [
|
|
41
|
-
"Usage: npm run decode -- <
|
|
49
|
+
"Usage: npm run decode -- <hex>",
|
|
42
50
|
" npm run encode -- '<json>'",
|
|
43
|
-
" cat context.
|
|
51
|
+
" cat context.hex | npm run decode",
|
|
44
52
|
" cat context.json | npm run encode",
|
|
45
53
|
];
|
|
46
54
|
}
|
|
@@ -70,8 +78,8 @@ async function main() {
|
|
|
70
78
|
}
|
|
71
79
|
try {
|
|
72
80
|
if (modeRaw === "decode") {
|
|
73
|
-
const
|
|
74
|
-
const bytes = Buffer.from(
|
|
81
|
+
const hex = normalizeHex(input);
|
|
82
|
+
const bytes = Buffer.from(hex, "hex");
|
|
75
83
|
const message = context_1.Context.decode(bytes);
|
|
76
84
|
const json = context_1.Context.toJSON(message);
|
|
77
85
|
process.stdout.write(`${JSON.stringify(json, null, 2)}\n`);
|
|
@@ -80,7 +88,7 @@ async function main() {
|
|
|
80
88
|
const parsed = JSON.parse(input);
|
|
81
89
|
const message = context_1.Context.fromJSON(parsed);
|
|
82
90
|
const encoded = context_1.Context.encode(message).finish();
|
|
83
|
-
process.stdout.write(`${Buffer.from(encoded).toString("
|
|
91
|
+
process.stdout.write(`${Buffer.from(encoded).toString("hex")}\n`);
|
|
84
92
|
}
|
|
85
93
|
catch (error) {
|
|
86
94
|
console.error(`Failed to ${modeRaw} context: ${errorMessage(error)}`);
|
package/dist/compute-wager.d.ts
CHANGED
|
@@ -2,26 +2,37 @@ import { Currency } from "./generated/currency";
|
|
|
2
2
|
import { FairCoinToss, FairCoinToss_Choice } from "./generated/context/fair-coin-toss";
|
|
3
3
|
import { CrashDice } from "./generated/context/crash-dice";
|
|
4
4
|
import { MultiRoulette } from "./generated/context/multi-roulette";
|
|
5
|
-
|
|
6
|
-
export
|
|
7
|
-
export declare function
|
|
5
|
+
export type CompressedSeed = number;
|
|
6
|
+
export type RandomSource = Uint8Array;
|
|
7
|
+
export declare function splitHelixHash(hash: Uint8Array): {
|
|
8
|
+
lhs: Uint8Array;
|
|
9
|
+
rhs: Uint8Array;
|
|
10
|
+
};
|
|
11
|
+
export declare function computeCompressedSeed(clientSeed: string, lhsHash: Uint8Array): CompressedSeed;
|
|
12
|
+
export declare function computeCompressedSeedFromGameHash(clientSeed: string, gameHash: Uint8Array): CompressedSeed;
|
|
13
|
+
export declare function computeRandomSourceFromCompressedSeed(compressedSeed: CompressedSeed, rhsHash: Uint8Array): RandomSource;
|
|
14
|
+
export declare function computeRandomSource(clientSeed: string, gameHash: Uint8Array): RandomSource;
|
|
15
|
+
export declare function computeFairCoinTossResult(randomSource: RandomSource): FairCoinToss_Choice.HEADS | FairCoinToss_Choice.TAILS;
|
|
16
|
+
export declare function computeFairCoinTossOutcome(randomSource: RandomSource, w: FairCoinToss): {
|
|
8
17
|
result: FairCoinToss_Choice;
|
|
9
18
|
playerProfit: {
|
|
10
19
|
currency: Currency;
|
|
11
20
|
amount: number;
|
|
12
21
|
};
|
|
13
22
|
};
|
|
14
|
-
export declare function computeCrashResult(
|
|
23
|
+
export declare function computeCrashResult(hash: Uint8Array, gameHash: Uint8Array, // This is the hash of the next from the hash chain
|
|
15
24
|
houseEdge?: number): number;
|
|
16
25
|
export type CrashDiceOutcome = {
|
|
17
26
|
multiplier: number;
|
|
18
27
|
target: number;
|
|
19
28
|
win: boolean;
|
|
20
29
|
};
|
|
30
|
+
export declare function computeCrashDiceResultFromRandomSource(randomSource: RandomSource): number;
|
|
21
31
|
export declare function computeCrashDiceResult(hash: Uint8Array, clientSeed: string): number;
|
|
32
|
+
export declare function computeCrashDiceOutcomeFromRandomSource(randomSource: RandomSource, bet: CrashDice): CrashDiceOutcome;
|
|
22
33
|
export declare function computeCrashDiceOutcome(hash: Uint8Array, clientSeed: string, bet: CrashDice): CrashDiceOutcome;
|
|
23
|
-
export declare function computeMultiRouletteResult(
|
|
24
|
-
export declare function computeMineLocations(
|
|
34
|
+
export declare function computeMultiRouletteResult(randomSource: RandomSource, bet: MultiRoulette): number | undefined;
|
|
35
|
+
export declare function computeMineLocations(randomSource: RandomSource, revealedCells: Set<number>, // tiles we know are safe
|
|
25
36
|
cells: number, // how many cells in total
|
|
26
37
|
mines: number): Set<number>;
|
|
27
38
|
export declare function computeMinesMultiplier(mines: number, // how many mines in the game
|
|
@@ -30,13 +41,13 @@ turn: number, // which turn they have finished
|
|
|
30
41
|
houseEdge?: number): number;
|
|
31
42
|
type PlinkoPath = ("L" | "R")[];
|
|
32
43
|
export declare function computePinkoPossibilityIndexFromPath(path: PlinkoPath): number;
|
|
33
|
-
export declare function computePlinkoPath(
|
|
44
|
+
export declare function computePlinkoPath(randomSource: RandomSource, possibilities: number): PlinkoPath;
|
|
34
45
|
export declare function computePlinkoPascalsProbabilities(rowNumber: number): number[];
|
|
35
|
-
export declare function computePlinkoHouseEdge(
|
|
46
|
+
export declare function computePlinkoHouseEdge(payouts: number[]): number;
|
|
36
47
|
export type PlinkoResult = {
|
|
37
48
|
slot: number;
|
|
38
49
|
multiplier: number;
|
|
39
|
-
win: boolean;
|
|
40
50
|
};
|
|
41
|
-
export declare function
|
|
51
|
+
export declare function computePlinkoResultFromRandomSource(randomSource: RandomSource, payouts: number[]): PlinkoResult;
|
|
52
|
+
export declare function computePlinkoResult(hash: Uint8Array, clientSeed: string, payouts: number[]): PlinkoResult;
|
|
42
53
|
export {};
|
package/dist/compute-wager.js
CHANGED
|
@@ -1,16 +1,47 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.computePlinkoResult = exports.computePlinkoHouseEdge = exports.computePlinkoPascalsProbabilities = exports.computePlinkoPath = exports.computePinkoPossibilityIndexFromPath = exports.computeMinesMultiplier = exports.computeMineLocations = exports.computeMultiRouletteResult = exports.computeCrashDiceOutcome = exports.computeCrashDiceResult = exports.computeCrashResult = exports.computeFairCoinTossOutcome = exports.computeFairCoinTossResult = void 0;
|
|
3
|
+
exports.computePlinkoResult = exports.computePlinkoResultFromRandomSource = exports.computePlinkoHouseEdge = exports.computePlinkoPascalsProbabilities = exports.computePlinkoPath = exports.computePinkoPossibilityIndexFromPath = exports.computeMinesMultiplier = exports.computeMineLocations = exports.computeMultiRouletteResult = exports.computeCrashDiceOutcome = exports.computeCrashDiceOutcomeFromRandomSource = exports.computeCrashDiceResult = exports.computeCrashDiceResultFromRandomSource = exports.computeCrashResult = exports.computeFairCoinTossOutcome = exports.computeFairCoinTossResult = exports.computeRandomSource = exports.computeRandomSourceFromCompressedSeed = exports.computeCompressedSeedFromGameHash = exports.computeCompressedSeed = exports.splitHelixHash = void 0;
|
|
4
4
|
const sha256_1 = require("@noble/hashes/sha256");
|
|
5
5
|
const hmac_1 = require("@noble/hashes/hmac");
|
|
6
6
|
const utils_1 = require("@noble/curves/abstract/utils");
|
|
7
7
|
const currency_1 = require("./generated/currency");
|
|
8
8
|
const fair_coin_toss_1 = require("./generated/context/fair-coin-toss");
|
|
9
9
|
const utils_2 = require("@noble/hashes/utils");
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
function splitHelixHash(hash) {
|
|
11
|
+
if (hash.length % 2 !== 0) {
|
|
12
|
+
throw new Error("Helix hash input must have an even number of bytes.");
|
|
13
|
+
}
|
|
14
|
+
const mid = hash.length >>> 1;
|
|
15
|
+
return {
|
|
16
|
+
lhs: hash.subarray(0, mid),
|
|
17
|
+
rhs: hash.subarray(mid),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
exports.splitHelixHash = splitHelixHash;
|
|
21
|
+
function computeCompressedSeed(clientSeed, lhsHash) {
|
|
22
|
+
const seedHash = hmacSha256(lhsHash, clientSeed);
|
|
23
|
+
return u32FromHash(seedHash);
|
|
24
|
+
}
|
|
25
|
+
exports.computeCompressedSeed = computeCompressedSeed;
|
|
26
|
+
function computeCompressedSeedFromGameHash(clientSeed, gameHash) {
|
|
27
|
+
const { lhs } = splitHelixHash(gameHash);
|
|
28
|
+
return computeCompressedSeed(clientSeed, lhs);
|
|
29
|
+
}
|
|
30
|
+
exports.computeCompressedSeedFromGameHash = computeCompressedSeedFromGameHash;
|
|
31
|
+
function computeRandomSourceFromCompressedSeed(compressedSeed, rhsHash) {
|
|
32
|
+
const seedBytes = u32ToBytes(compressedSeed);
|
|
33
|
+
return hmacSha256Bytes(rhsHash, seedBytes);
|
|
34
|
+
}
|
|
35
|
+
exports.computeRandomSourceFromCompressedSeed = computeRandomSourceFromCompressedSeed;
|
|
36
|
+
function computeRandomSource(clientSeed, gameHash) {
|
|
37
|
+
const { lhs, rhs } = splitHelixHash(gameHash);
|
|
38
|
+
const compressedSeed = computeCompressedSeed(clientSeed, lhs);
|
|
39
|
+
return computeRandomSourceFromCompressedSeed(compressedSeed, rhs);
|
|
40
|
+
}
|
|
41
|
+
exports.computeRandomSource = computeRandomSource;
|
|
42
|
+
function computeFairCoinTossResult(randomSource) {
|
|
43
|
+
// We're going to hash the random source just to really be sure its fairly distributed
|
|
44
|
+
const hash = (0, sha256_1.sha256)(randomSource);
|
|
14
45
|
const result = hash[0] % 2;
|
|
15
46
|
if (result == 0) {
|
|
16
47
|
return fair_coin_toss_1.FairCoinToss_Choice.HEADS;
|
|
@@ -20,8 +51,8 @@ function computeFairCoinTossResult(sig) {
|
|
|
20
51
|
}
|
|
21
52
|
}
|
|
22
53
|
exports.computeFairCoinTossResult = computeFairCoinTossResult;
|
|
23
|
-
function computeFairCoinTossOutcome(
|
|
24
|
-
const result = computeFairCoinTossResult(
|
|
54
|
+
function computeFairCoinTossOutcome(randomSource, w) {
|
|
55
|
+
const result = computeFairCoinTossResult(randomSource);
|
|
25
56
|
const win = w.playerChoice === result;
|
|
26
57
|
const profit = win ? 1 : -1;
|
|
27
58
|
return {
|
|
@@ -30,10 +61,10 @@ function computeFairCoinTossOutcome(sig, w) {
|
|
|
30
61
|
};
|
|
31
62
|
}
|
|
32
63
|
exports.computeFairCoinTossOutcome = computeFairCoinTossOutcome;
|
|
33
|
-
function doComputeCrashResult(
|
|
64
|
+
function doComputeCrashResult(randomSource, houseEdge) {
|
|
34
65
|
const nBits = 52;
|
|
35
|
-
const
|
|
36
|
-
const seed =
|
|
66
|
+
const randomSourceHex = (0, utils_2.bytesToHex)(randomSource);
|
|
67
|
+
const seed = randomSourceHex.slice(0, nBits / 4);
|
|
37
68
|
const r = Number.parseInt(seed, 16);
|
|
38
69
|
let X = r / 2 ** nBits; // uniformly distributed in [0; 1)
|
|
39
70
|
let Y = 1 - X; // Now it's uniformly distributed in (0; 1], so it's safe to divide by it
|
|
@@ -42,18 +73,25 @@ function doComputeCrashResult(hash, houseEdge) {
|
|
|
42
73
|
result = Math.max(1, result);
|
|
43
74
|
return result;
|
|
44
75
|
}
|
|
45
|
-
function computeCrashResult(
|
|
76
|
+
function computeCrashResult(hash, gameHash, // This is the hash of the next from the hash chain
|
|
46
77
|
houseEdge = 0) {
|
|
47
|
-
return doComputeCrashResult((0, hmac_1.hmac)(sha256_1.sha256,
|
|
78
|
+
return doComputeCrashResult((0, hmac_1.hmac)(sha256_1.sha256, hash, gameHash), houseEdge);
|
|
48
79
|
}
|
|
49
80
|
exports.computeCrashResult = computeCrashResult;
|
|
81
|
+
function computeCrashDiceResultFromRandomSource(randomSource) {
|
|
82
|
+
const normalized = u32FromHash(randomSource);
|
|
83
|
+
const max = 2 ** 32;
|
|
84
|
+
const multiplierTimes100 = Math.floor((100 * max) / (max - normalized));
|
|
85
|
+
return multiplierTimes100 / 100;
|
|
86
|
+
}
|
|
87
|
+
exports.computeCrashDiceResultFromRandomSource = computeCrashDiceResultFromRandomSource;
|
|
50
88
|
function computeCrashDiceResult(hash, clientSeed) {
|
|
51
|
-
const
|
|
52
|
-
return
|
|
89
|
+
const randomSource = computeRandomSource(clientSeed, hash);
|
|
90
|
+
return computeCrashDiceResultFromRandomSource(randomSource);
|
|
53
91
|
}
|
|
54
92
|
exports.computeCrashDiceResult = computeCrashDiceResult;
|
|
55
|
-
function
|
|
56
|
-
const multiplier =
|
|
93
|
+
function computeCrashDiceOutcomeFromRandomSource(randomSource, bet) {
|
|
94
|
+
const multiplier = computeCrashDiceResultFromRandomSource(randomSource);
|
|
57
95
|
const target = bet.target;
|
|
58
96
|
return {
|
|
59
97
|
multiplier,
|
|
@@ -61,12 +99,17 @@ function computeCrashDiceOutcome(hash, clientSeed, bet) {
|
|
|
61
99
|
win: multiplier >= target,
|
|
62
100
|
};
|
|
63
101
|
}
|
|
102
|
+
exports.computeCrashDiceOutcomeFromRandomSource = computeCrashDiceOutcomeFromRandomSource;
|
|
103
|
+
function computeCrashDiceOutcome(hash, clientSeed, bet) {
|
|
104
|
+
const randomSource = computeRandomSource(clientSeed, hash);
|
|
105
|
+
return computeCrashDiceOutcomeFromRandomSource(randomSource, bet);
|
|
106
|
+
}
|
|
64
107
|
exports.computeCrashDiceOutcome = computeCrashDiceOutcome;
|
|
65
108
|
// returns the index of which roulette outcome was picked
|
|
66
|
-
function computeMultiRouletteResult(
|
|
67
|
-
const
|
|
109
|
+
function computeMultiRouletteResult(randomSource, bet) {
|
|
110
|
+
const seedHash = (0, sha256_1.sha256)(randomSource);
|
|
68
111
|
const nBits = 52;
|
|
69
|
-
const hashHex = (0, utils_2.bytesToHex)(
|
|
112
|
+
const hashHex = (0, utils_2.bytesToHex)(seedHash);
|
|
70
113
|
const seed = hashHex.slice(0, nBits / 4);
|
|
71
114
|
const n = Number.parseInt(seed, 16);
|
|
72
115
|
const v = n / 2 ** nBits; // uniform in [0; 1)
|
|
@@ -79,7 +122,7 @@ function computeMultiRouletteResult(vxSignature, bet) {
|
|
|
79
122
|
}
|
|
80
123
|
}
|
|
81
124
|
exports.computeMultiRouletteResult = computeMultiRouletteResult;
|
|
82
|
-
function computeMineLocations(
|
|
125
|
+
function computeMineLocations(randomSource, revealedCells, // tiles we know are safe
|
|
83
126
|
cells, // how many cells in total
|
|
84
127
|
mines // how many mines there are going to be in total
|
|
85
128
|
) {
|
|
@@ -90,7 +133,7 @@ mines // how many mines there are going to be in total
|
|
|
90
133
|
console.warn("Trying to place more mines than there are available locations.");
|
|
91
134
|
break;
|
|
92
135
|
}
|
|
93
|
-
let mineIndex = Number((0, utils_1.bytesToNumberBE)(
|
|
136
|
+
let mineIndex = Number((0, utils_1.bytesToNumberBE)(randomSource) % BigInt(cellsLeft));
|
|
94
137
|
let adjustedIndex = 0;
|
|
95
138
|
for (let i = 0; i < cells; i++) {
|
|
96
139
|
if (revealedCells.has(i) || mineLocations.has(i))
|
|
@@ -152,14 +195,14 @@ function computePinkoPossibilityIndexFromPath(path) {
|
|
|
152
195
|
exports.computePinkoPossibilityIndexFromPath = computePinkoPossibilityIndexFromPath;
|
|
153
196
|
// return a path (saying 'L' or 'R', where 'L' means go left, and 'R' means going right)
|
|
154
197
|
// of possibilities-1 length
|
|
155
|
-
function computePlinkoPath(
|
|
198
|
+
function computePlinkoPath(randomSource, possibilities) {
|
|
156
199
|
if (!Number.isSafeInteger(possibilities) ||
|
|
157
200
|
possibilities < 2 ||
|
|
158
201
|
possibilities > 256) {
|
|
159
202
|
throw new Error("invalid possibilities ");
|
|
160
203
|
}
|
|
161
|
-
const
|
|
162
|
-
let n = (0, utils_1.bytesToNumberBE)(
|
|
204
|
+
const pathHash = (0, sha256_1.sha256)(randomSource);
|
|
205
|
+
let n = (0, utils_1.bytesToNumberBE)(pathHash);
|
|
163
206
|
let ret = [];
|
|
164
207
|
for (let i = 0; i < possibilities - 1; i++) {
|
|
165
208
|
ret.push(n % 2n == 0n ? "R" : "L");
|
|
@@ -193,65 +236,64 @@ function computePlinkoPascalsProbabilities(rowNumber) {
|
|
|
193
236
|
return lastRow.map((v) => v / sum);
|
|
194
237
|
}
|
|
195
238
|
exports.computePlinkoPascalsProbabilities = computePlinkoPascalsProbabilities;
|
|
196
|
-
function computePlinkoHouseEdge(
|
|
197
|
-
const odds = computePlinkoPascalsProbabilities(
|
|
239
|
+
function computePlinkoHouseEdge(payouts) {
|
|
240
|
+
const odds = computePlinkoPascalsProbabilities(payouts.length);
|
|
198
241
|
let ev = 1; // you start off by betting everything
|
|
199
|
-
for (let i = 0; i <
|
|
200
|
-
ev -=
|
|
242
|
+
for (let i = 0; i < payouts.length; i++) {
|
|
243
|
+
ev -= payouts[i] * odds[i];
|
|
201
244
|
}
|
|
202
245
|
return ev;
|
|
203
246
|
}
|
|
204
247
|
exports.computePlinkoHouseEdge = computePlinkoHouseEdge;
|
|
205
|
-
function
|
|
206
|
-
|
|
207
|
-
if (possibilities.length < 2) {
|
|
248
|
+
function computePlinkoResultFromRandomSource(randomSource, payouts) {
|
|
249
|
+
if (payouts.length < 2) {
|
|
208
250
|
throw new Error("invalid possibilities ");
|
|
209
251
|
}
|
|
210
|
-
const probabilities = computePlinkoPascalsProbabilities(
|
|
211
|
-
const
|
|
212
|
-
const roll = uniformFromHash(rollHash);
|
|
252
|
+
const probabilities = computePlinkoPascalsProbabilities(payouts.length);
|
|
253
|
+
const roll = uniformFromHash(randomSource);
|
|
213
254
|
const slot = pickSlot(probabilities, roll);
|
|
214
|
-
const multiplier =
|
|
255
|
+
const multiplier = payouts[slot] ?? 0;
|
|
215
256
|
return {
|
|
216
257
|
slot,
|
|
217
258
|
multiplier,
|
|
218
|
-
win: multiplier >= 1,
|
|
219
259
|
};
|
|
220
260
|
}
|
|
221
|
-
exports.
|
|
222
|
-
function
|
|
223
|
-
const
|
|
224
|
-
|
|
225
|
-
return [];
|
|
226
|
-
}
|
|
227
|
-
if (payouts.duel) {
|
|
228
|
-
return (0, duel_plinko_payouts_1.getDuelPlinkoPayoutsFromEnum)(payouts.duel.rows, payouts.duel.risk);
|
|
229
|
-
}
|
|
230
|
-
if (payouts.custom) {
|
|
231
|
-
return payouts.custom.possibilities;
|
|
232
|
-
}
|
|
233
|
-
return [];
|
|
261
|
+
exports.computePlinkoResultFromRandomSource = computePlinkoResultFromRandomSource;
|
|
262
|
+
function computePlinkoResult(hash, clientSeed, payouts) {
|
|
263
|
+
const randomSource = computeRandomSource(clientSeed, hash);
|
|
264
|
+
return computePlinkoResultFromRandomSource(randomSource, payouts);
|
|
234
265
|
}
|
|
266
|
+
exports.computePlinkoResult = computePlinkoResult;
|
|
235
267
|
function hmacSha256(key, message) {
|
|
236
268
|
const data = new TextEncoder().encode(message);
|
|
237
269
|
return (0, hmac_1.hmac)(sha256_1.sha256, key, data);
|
|
238
270
|
}
|
|
239
|
-
function
|
|
240
|
-
|
|
241
|
-
|
|
271
|
+
function hmacSha256Bytes(key, message) {
|
|
272
|
+
return (0, hmac_1.hmac)(sha256_1.sha256, key, message);
|
|
273
|
+
}
|
|
274
|
+
function u32FromHash(randomSource) {
|
|
275
|
+
if (randomSource.length < 4) {
|
|
276
|
+
throw new Error("Random source must be at least 4 bytes.");
|
|
242
277
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
278
|
+
return (((randomSource[0] << 24) |
|
|
279
|
+
(randomSource[1] << 16) |
|
|
280
|
+
(randomSource[2] << 8) |
|
|
281
|
+
randomSource[3]) >>>
|
|
282
|
+
0);
|
|
248
283
|
}
|
|
249
|
-
function
|
|
250
|
-
if (
|
|
251
|
-
throw new Error("
|
|
284
|
+
function u32ToBytes(value) {
|
|
285
|
+
if (!Number.isInteger(value) || value < 0 || value > 0xffffffff) {
|
|
286
|
+
throw new Error("Value must be a 32-bit unsigned integer.");
|
|
252
287
|
}
|
|
253
|
-
const
|
|
254
|
-
|
|
288
|
+
const buffer = new Uint8Array(4);
|
|
289
|
+
buffer[0] = (value >>> 24) & 0xff;
|
|
290
|
+
buffer[1] = (value >>> 16) & 0xff;
|
|
291
|
+
buffer[2] = (value >>> 8) & 0xff;
|
|
292
|
+
buffer[3] = value & 0xff;
|
|
293
|
+
return buffer;
|
|
294
|
+
}
|
|
295
|
+
function uniformFromHash(randomSource) {
|
|
296
|
+
const normalized = u32FromHash(randomSource);
|
|
255
297
|
return normalized / 2 ** 32;
|
|
256
298
|
}
|
|
257
299
|
function pickSlot(probabilities, roll) {
|
|
@@ -1,14 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const
|
|
3
|
-
export type
|
|
4
|
-
export declare const
|
|
5
|
-
export
|
|
1
|
+
import { PlinkoPayoutTable } from "./generated/context/plinko";
|
|
2
|
+
export declare const PLINKO_RISK_KEYS: readonly ["low", "medium", "high"];
|
|
3
|
+
export type PlinkoRiskKey = (typeof PLINKO_RISK_KEYS)[number];
|
|
4
|
+
export declare const PLINKO_PAYOUT_TABLE_KEYS: readonly ["duel_8_low", "duel_8_medium", "duel_8_high", "duel_9_low", "duel_9_medium", "duel_9_high", "duel_10_low", "duel_10_medium", "duel_10_high", "duel_11_low", "duel_11_medium", "duel_11_high", "duel_12_low", "duel_12_medium", "duel_12_high", "duel_13_low", "duel_13_medium", "duel_13_high", "duel_14_low", "duel_14_medium", "duel_14_high", "duel_15_low", "duel_15_medium", "duel_15_high", "duel_16_low", "duel_16_medium", "duel_16_high"];
|
|
5
|
+
export type PlinkoPayoutTableKey = (typeof PLINKO_PAYOUT_TABLE_KEYS)[number];
|
|
6
|
+
export declare const PLINKO_PAYOUT_TABLES: Record<PlinkoPayoutTableKey, number[]>;
|
|
7
|
+
export declare const PLINKO_ROWS: {
|
|
6
8
|
readonly min: number;
|
|
7
9
|
readonly max: number;
|
|
8
10
|
};
|
|
9
|
-
export declare function
|
|
10
|
-
export declare function
|
|
11
|
-
export declare function
|
|
12
|
-
export declare function
|
|
13
|
-
export declare function
|
|
14
|
-
export declare function
|
|
11
|
+
export declare function isPlinkoRiskKey(value: string): value is PlinkoRiskKey;
|
|
12
|
+
export declare function isPlinkoRows(rows: number): boolean;
|
|
13
|
+
export declare function getPlinkoPayoutTableKey(rows: number, risk: PlinkoRiskKey): PlinkoPayoutTableKey | null;
|
|
14
|
+
export declare function getPlinkoPayouts(key: PlinkoPayoutTableKey): number[];
|
|
15
|
+
export declare function isPlinkoPayoutTableKey(value: string): value is PlinkoPayoutTableKey;
|
|
16
|
+
export declare function plinkoPayoutTableToEnum(value: PlinkoPayoutTableKey): PlinkoPayoutTable;
|
|
17
|
+
export declare function plinkoPayoutTableFromEnum(value: PlinkoPayoutTable): PlinkoPayoutTableKey | null;
|
|
18
|
+
export declare function getPlinkoPayoutsFromEnum(value: PlinkoPayoutTable): number[];
|