@actuallyfair/verifier 0.0.6 → 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.
@@ -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 normalizeBase64(input) {
8
- const cleaned = input.replace(/\s+/g, "").replace(/-/g, "+").replace(/_/g, "/");
9
- const padding = cleaned.length % 4;
10
- return padding === 0 ? cleaned : `${cleaned}${"=".repeat(4 - padding)}`;
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 -- <base64>", " cat context.b64 | 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 -- <base64>",
49
+ "Usage: npm run decode -- <hex>",
42
50
  " npm run encode -- '<json>'",
43
- " cat context.b64 | npm run decode",
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 base64 = normalizeBase64(input);
74
- const bytes = Buffer.from(base64, "base64");
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("base64")}\n`);
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)}`);
@@ -2,8 +2,18 @@ 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
- export declare function computeFairCoinTossResult(sig: Uint8Array): FairCoinToss_Choice.HEADS | FairCoinToss_Choice.TAILS;
6
- export declare function computeFairCoinTossOutcome(sig: Uint8Array, w: FairCoinToss): {
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): {
7
17
  result: FairCoinToss_Choice;
8
18
  playerProfit: {
9
19
  currency: Currency;
@@ -17,10 +27,12 @@ export type CrashDiceOutcome = {
17
27
  target: number;
18
28
  win: boolean;
19
29
  };
30
+ export declare function computeCrashDiceResultFromRandomSource(randomSource: RandomSource): number;
20
31
  export declare function computeCrashDiceResult(hash: Uint8Array, clientSeed: string): number;
32
+ export declare function computeCrashDiceOutcomeFromRandomSource(randomSource: RandomSource, bet: CrashDice): CrashDiceOutcome;
21
33
  export declare function computeCrashDiceOutcome(hash: Uint8Array, clientSeed: string, bet: CrashDice): CrashDiceOutcome;
22
- export declare function computeMultiRouletteResult(hash: Uint8Array, bet: MultiRoulette): number | undefined;
23
- export declare function computeMineLocations(hash: Uint8Array, revealedCells: Set<number>, // tiles we know are safe
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
24
36
  cells: number, // how many cells in total
25
37
  mines: number): Set<number>;
26
38
  export declare function computeMinesMultiplier(mines: number, // how many mines in the game
@@ -29,12 +41,13 @@ turn: number, // which turn they have finished
29
41
  houseEdge?: number): number;
30
42
  type PlinkoPath = ("L" | "R")[];
31
43
  export declare function computePinkoPossibilityIndexFromPath(path: PlinkoPath): number;
32
- export declare function computePlinkoPath(hash: Uint8Array, possibilities: number): PlinkoPath;
44
+ export declare function computePlinkoPath(randomSource: RandomSource, possibilities: number): PlinkoPath;
33
45
  export declare function computePlinkoPascalsProbabilities(rowNumber: number): number[];
34
46
  export declare function computePlinkoHouseEdge(payouts: number[]): number;
35
47
  export type PlinkoResult = {
36
48
  slot: number;
37
49
  multiplier: number;
38
50
  };
51
+ export declare function computePlinkoResultFromRandomSource(randomSource: RandomSource, payouts: number[]): PlinkoResult;
39
52
  export declare function computePlinkoResult(hash: Uint8Array, clientSeed: string, payouts: number[]): PlinkoResult;
40
53
  export {};
@@ -1,15 +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
- function computeFairCoinTossResult(sig) {
11
- // We're going to hash the signature just to really be sure its fairly distributed
12
- const hash = (0, sha256_1.sha256)(sig);
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);
13
45
  const result = hash[0] % 2;
14
46
  if (result == 0) {
15
47
  return fair_coin_toss_1.FairCoinToss_Choice.HEADS;
@@ -19,8 +51,8 @@ function computeFairCoinTossResult(sig) {
19
51
  }
20
52
  }
21
53
  exports.computeFairCoinTossResult = computeFairCoinTossResult;
22
- function computeFairCoinTossOutcome(sig, w) {
23
- const result = computeFairCoinTossResult(sig);
54
+ function computeFairCoinTossOutcome(randomSource, w) {
55
+ const result = computeFairCoinTossResult(randomSource);
24
56
  const win = w.playerChoice === result;
25
57
  const profit = win ? 1 : -1;
26
58
  return {
@@ -29,10 +61,10 @@ function computeFairCoinTossOutcome(sig, w) {
29
61
  };
30
62
  }
31
63
  exports.computeFairCoinTossOutcome = computeFairCoinTossOutcome;
32
- function doComputeCrashResult(hash, houseEdge) {
64
+ function doComputeCrashResult(randomSource, houseEdge) {
33
65
  const nBits = 52;
34
- const hashHex = (0, utils_2.bytesToHex)(hash);
35
- const seed = hashHex.slice(0, nBits / 4);
66
+ const randomSourceHex = (0, utils_2.bytesToHex)(randomSource);
67
+ const seed = randomSourceHex.slice(0, nBits / 4);
36
68
  const r = Number.parseInt(seed, 16);
37
69
  let X = r / 2 ** nBits; // uniformly distributed in [0; 1)
38
70
  let Y = 1 - X; // Now it's uniformly distributed in (0; 1], so it's safe to divide by it
@@ -46,13 +78,20 @@ houseEdge = 0) {
46
78
  return doComputeCrashResult((0, hmac_1.hmac)(sha256_1.sha256, hash, gameHash), houseEdge);
47
79
  }
48
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;
49
88
  function computeCrashDiceResult(hash, clientSeed) {
50
- const rollHash = hmacSha256(hash, clientSeed);
51
- return multiplierFromHash(rollHash);
89
+ const randomSource = computeRandomSource(clientSeed, hash);
90
+ return computeCrashDiceResultFromRandomSource(randomSource);
52
91
  }
53
92
  exports.computeCrashDiceResult = computeCrashDiceResult;
54
- function computeCrashDiceOutcome(hash, clientSeed, bet) {
55
- const multiplier = computeCrashDiceResult(hash, clientSeed);
93
+ function computeCrashDiceOutcomeFromRandomSource(randomSource, bet) {
94
+ const multiplier = computeCrashDiceResultFromRandomSource(randomSource);
56
95
  const target = bet.target;
57
96
  return {
58
97
  multiplier,
@@ -60,10 +99,15 @@ function computeCrashDiceOutcome(hash, clientSeed, bet) {
60
99
  win: multiplier >= target,
61
100
  };
62
101
  }
102
+ exports.computeCrashDiceOutcomeFromRandomSource = computeCrashDiceOutcomeFromRandomSource;
103
+ function computeCrashDiceOutcome(hash, clientSeed, bet) {
104
+ const randomSource = computeRandomSource(clientSeed, hash);
105
+ return computeCrashDiceOutcomeFromRandomSource(randomSource, bet);
106
+ }
63
107
  exports.computeCrashDiceOutcome = computeCrashDiceOutcome;
64
108
  // returns the index of which roulette outcome was picked
65
- function computeMultiRouletteResult(hash, bet) {
66
- const seedHash = (0, sha256_1.sha256)(hash);
109
+ function computeMultiRouletteResult(randomSource, bet) {
110
+ const seedHash = (0, sha256_1.sha256)(randomSource);
67
111
  const nBits = 52;
68
112
  const hashHex = (0, utils_2.bytesToHex)(seedHash);
69
113
  const seed = hashHex.slice(0, nBits / 4);
@@ -78,7 +122,7 @@ function computeMultiRouletteResult(hash, bet) {
78
122
  }
79
123
  }
80
124
  exports.computeMultiRouletteResult = computeMultiRouletteResult;
81
- function computeMineLocations(hash, revealedCells, // tiles we know are safe
125
+ function computeMineLocations(randomSource, revealedCells, // tiles we know are safe
82
126
  cells, // how many cells in total
83
127
  mines // how many mines there are going to be in total
84
128
  ) {
@@ -89,7 +133,7 @@ mines // how many mines there are going to be in total
89
133
  console.warn("Trying to place more mines than there are available locations.");
90
134
  break;
91
135
  }
92
- let mineIndex = Number((0, utils_1.bytesToNumberBE)(hash) % BigInt(cellsLeft));
136
+ let mineIndex = Number((0, utils_1.bytesToNumberBE)(randomSource) % BigInt(cellsLeft));
93
137
  let adjustedIndex = 0;
94
138
  for (let i = 0; i < cells; i++) {
95
139
  if (revealedCells.has(i) || mineLocations.has(i))
@@ -151,13 +195,13 @@ function computePinkoPossibilityIndexFromPath(path) {
151
195
  exports.computePinkoPossibilityIndexFromPath = computePinkoPossibilityIndexFromPath;
152
196
  // return a path (saying 'L' or 'R', where 'L' means go left, and 'R' means going right)
153
197
  // of possibilities-1 length
154
- function computePlinkoPath(hash, possibilities) {
198
+ function computePlinkoPath(randomSource, possibilities) {
155
199
  if (!Number.isSafeInteger(possibilities) ||
156
200
  possibilities < 2 ||
157
201
  possibilities > 256) {
158
202
  throw new Error("invalid possibilities ");
159
203
  }
160
- const pathHash = (0, sha256_1.sha256)(hash);
204
+ const pathHash = (0, sha256_1.sha256)(randomSource);
161
205
  let n = (0, utils_1.bytesToNumberBE)(pathHash);
162
206
  let ret = [];
163
207
  for (let i = 0; i < possibilities - 1; i++) {
@@ -201,13 +245,12 @@ function computePlinkoHouseEdge(payouts) {
201
245
  return ev;
202
246
  }
203
247
  exports.computePlinkoHouseEdge = computePlinkoHouseEdge;
204
- function computePlinkoResult(hash, clientSeed, payouts) {
248
+ function computePlinkoResultFromRandomSource(randomSource, payouts) {
205
249
  if (payouts.length < 2) {
206
250
  throw new Error("invalid possibilities ");
207
251
  }
208
252
  const probabilities = computePlinkoPascalsProbabilities(payouts.length);
209
- const rollHash = hmacSha256(hash, clientSeed);
210
- const roll = uniformFromHash(rollHash);
253
+ const roll = uniformFromHash(randomSource);
211
254
  const slot = pickSlot(probabilities, roll);
212
255
  const multiplier = payouts[slot] ?? 0;
213
256
  return {
@@ -215,27 +258,42 @@ function computePlinkoResult(hash, clientSeed, payouts) {
215
258
  multiplier,
216
259
  };
217
260
  }
261
+ exports.computePlinkoResultFromRandomSource = computePlinkoResultFromRandomSource;
262
+ function computePlinkoResult(hash, clientSeed, payouts) {
263
+ const randomSource = computeRandomSource(clientSeed, hash);
264
+ return computePlinkoResultFromRandomSource(randomSource, payouts);
265
+ }
218
266
  exports.computePlinkoResult = computePlinkoResult;
219
267
  function hmacSha256(key, message) {
220
268
  const data = new TextEncoder().encode(message);
221
269
  return (0, hmac_1.hmac)(sha256_1.sha256, key, data);
222
270
  }
223
- function multiplierFromHash(hash) {
224
- if (hash.length < 4) {
225
- throw new Error("Hash must be at least 4 bytes.");
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.");
226
277
  }
227
- const value = (hash[0] << 24) | (hash[1] << 16) | (hash[2] << 8) | hash[3];
228
- const normalized = value >>> 0;
229
- const max = 2 ** 32;
230
- const multiplierTimes100 = Math.floor((100 * max) / (max - normalized));
231
- return multiplierTimes100 / 100;
278
+ return (((randomSource[0] << 24) |
279
+ (randomSource[1] << 16) |
280
+ (randomSource[2] << 8) |
281
+ randomSource[3]) >>>
282
+ 0);
232
283
  }
233
- function uniformFromHash(hash) {
234
- if (hash.length < 4) {
235
- throw new Error("Hash must be at least 4 bytes.");
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.");
236
287
  }
237
- const value = (hash[0] << 24) | (hash[1] << 16) | (hash[2] << 8) | hash[3];
238
- const normalized = value >>> 0;
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);
239
297
  return normalized / 2 ** 32;
240
298
  }
241
299
  function pickSlot(probabilities, roll) {