@omnitronix/bonnys-fortune-game-engine 1.2.2 → 1.2.6
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/README.md +15 -15
- package/dist/__tests__/rng-gli19-compliance.test.js +222 -0
- package/dist/__tests__/rng-gli19-compliance.test.js.map +1 -0
- package/dist/__tests__/rng-seed-security.test.js +263 -0
- package/dist/__tests__/rng-seed-security.test.js.map +1 -0
- package/dist/__tests__/rng-seed-type.test.js +282 -0
- package/dist/__tests__/rng-seed-type.test.js.map +1 -0
- package/dist/__tests__/rng-stress-boundary.test.js +386 -0
- package/dist/__tests__/rng-stress-boundary.test.js.map +1 -0
- package/dist/bonnys-fortune-v1.game-engine.js +3 -1
- package/dist/bonnys-fortune-v1.game-engine.js.map +1 -1
- package/dist/config/reel-strips-config/reels-BASE.csv +0 -0
- package/dist/config/reel-strips-config/reels-BONUS.csv +0 -0
- package/dist/logic/handlers/base-game.handler.js +19 -1
- package/dist/logic/handlers/base-game.handler.js.map +1 -1
- package/dist/logic/handlers/collect-feature-bonus.handler.js +2 -2
- package/dist/logic/handlers/collect-feature-bonus.handler.js.map +1 -1
- package/dist/logic/handlers/free-spins-bonus.handler.js +4 -2
- package/dist/logic/handlers/free-spins-bonus.handler.js.map +1 -1
- package/dist/logic/handlers/steering-to-the-fortune-bonus.handler.js +4 -2
- package/dist/logic/handlers/steering-to-the-fortune-bonus.handler.js.map +1 -1
- package/dist/logic/handlers/treasure-hunt-bonus.handler.js +4 -2
- package/dist/logic/handlers/treasure-hunt-bonus.handler.js.map +1 -1
- package/dist/rng/rng-client.factory.js +45 -1
- package/dist/rng/rng-client.factory.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -241,19 +241,19 @@ The game engine supports 6 different command types:
|
|
|
241
241
|
|
|
242
242
|
**Returns**: Updated state
|
|
243
243
|
|
|
244
|
-
#### 5.
|
|
244
|
+
#### 5. DEBUG_TRIGGER_BONUS (Testing/Admin)
|
|
245
245
|
|
|
246
246
|
**Purpose**: Force trigger specific bonus game
|
|
247
247
|
|
|
248
|
-
**Payload**: `
|
|
248
|
+
**Payload**: `DebugTriggerBonusCommand`
|
|
249
249
|
|
|
250
250
|
**Returns**: State with pending bonus
|
|
251
251
|
|
|
252
|
-
#### 6.
|
|
252
|
+
#### 6. DEBUG_UPDATE_BONUS_METER_PROGRESS (Testing/Admin)
|
|
253
253
|
|
|
254
254
|
**Purpose**: Manually set bonus meter progress
|
|
255
255
|
|
|
256
|
-
**Payload**: `
|
|
256
|
+
**Payload**: `DebugUpdateBonusMeterProgressCommand`
|
|
257
257
|
|
|
258
258
|
**Returns**: Updated meter state
|
|
259
259
|
|
|
@@ -552,26 +552,26 @@ RNG_CLIENT_GRPC_URL=grpc://rng-prod.omnitronix.com:50051
|
|
|
552
552
|
RNG_CLIENT_POOL_SIZE=5000
|
|
553
553
|
```
|
|
554
554
|
|
|
555
|
-
### Example 4: Testing with
|
|
555
|
+
### Example 4: Testing with Debug Commands
|
|
556
556
|
|
|
557
557
|
```typescript
|
|
558
558
|
async function testBonusGame() {
|
|
559
559
|
const gameEngine = new BonnysFortuneV1GameEngine();
|
|
560
|
-
|
|
560
|
+
|
|
561
561
|
// Initialize session
|
|
562
562
|
const initResult = await gameEngine.processCommand({}, {}, {
|
|
563
563
|
id: 'test-init',
|
|
564
564
|
type: 'INIT_SESSION_STATE',
|
|
565
565
|
payload: { betAmountThresholds: [10], defaultBetAmount: 10 }
|
|
566
566
|
});
|
|
567
|
-
|
|
567
|
+
|
|
568
568
|
// Force trigger treasure hunt bonus
|
|
569
|
-
const
|
|
569
|
+
const debugResult = await gameEngine.processCommand(
|
|
570
570
|
initResult.publicState,
|
|
571
571
|
initResult.privateState,
|
|
572
572
|
{
|
|
573
|
-
id: 'test-
|
|
574
|
-
type: '
|
|
573
|
+
id: 'test-debug',
|
|
574
|
+
type: 'DEBUG_TRIGGER_BONUS',
|
|
575
575
|
payload: {
|
|
576
576
|
sessionId: 'test-session',
|
|
577
577
|
bonusType: 'bonusGame1',
|
|
@@ -579,11 +579,11 @@ async function testBonusGame() {
|
|
|
579
579
|
}
|
|
580
580
|
}
|
|
581
581
|
);
|
|
582
|
-
|
|
582
|
+
|
|
583
583
|
// Now start the bonus
|
|
584
584
|
const bonusResult = await gameEngine.processCommand(
|
|
585
|
-
|
|
586
|
-
|
|
585
|
+
debugResult.publicState,
|
|
586
|
+
debugResult.privateState,
|
|
587
587
|
{
|
|
588
588
|
id: 'test-bonus-start',
|
|
589
589
|
type: 'START_BONUS_ROUND',
|
|
@@ -593,11 +593,11 @@ async function testBonusGame() {
|
|
|
593
593
|
betAmount: 10,
|
|
594
594
|
gameCode: 'bonnys-fortune',
|
|
595
595
|
gameVersion: '1.0.0',
|
|
596
|
-
bonusId:
|
|
596
|
+
bonusId: debugResult.privateState.pendingBonuses[0].bonusId
|
|
597
597
|
}
|
|
598
598
|
}
|
|
599
599
|
);
|
|
600
|
-
|
|
600
|
+
|
|
601
601
|
console.log('Bonus results:', bonusResult.outcome);
|
|
602
602
|
}
|
|
603
603
|
```
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GLI-19 Compliance Tests - Bonny's Fortune
|
|
4
|
+
* Audit Trail & Tamper Detection Verification
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
const bonnys_fortune_v1_game_engine_1 = require("../bonnys-fortune-v1.game-engine");
|
|
41
|
+
const crypto = __importStar(require("crypto"));
|
|
42
|
+
describe('GLI-19 Compliance - Audit Trail & Tamper Detection', () => {
|
|
43
|
+
let engine;
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
engine = new bonnys_fortune_v1_game_engine_1.BonnysFortuneV1GameEngine();
|
|
46
|
+
});
|
|
47
|
+
async function initSession() {
|
|
48
|
+
return engine.processCommand(null, null, {
|
|
49
|
+
id: 'init-gli19',
|
|
50
|
+
type: 'INIT_SESSION_STATE',
|
|
51
|
+
payload: { betAmountThresholds: [1, 2, 5, 10], defaultBetAmount: 1 },
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
async function executeSpin(publicState, privateState) {
|
|
55
|
+
return engine.processCommand(publicState, privateState, {
|
|
56
|
+
id: `spin-${Math.random().toString(36).slice(2)}`,
|
|
57
|
+
type: 'SPIN',
|
|
58
|
+
payload: { sessionId: 'test', betAmount: 1, gameCode: 'bonnys-fortune', gameVersion: '1.0.0', lines: 10, creditsPerLine: 1 },
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
function canonicalizeForHash(outcome) {
|
|
62
|
+
const entries = Object.entries(outcome)
|
|
63
|
+
.map(([actionId, record]) => ({
|
|
64
|
+
actionId, result: Number(record.result), seed: String(record.seed),
|
|
65
|
+
min: Number(record.min), max: Number(record.max),
|
|
66
|
+
}))
|
|
67
|
+
.sort((a, b) => a.actionId.localeCompare(b.actionId));
|
|
68
|
+
return JSON.stringify(entries);
|
|
69
|
+
}
|
|
70
|
+
function generateHash(data) {
|
|
71
|
+
return crypto.createHash('sha256').update(data).digest('hex');
|
|
72
|
+
}
|
|
73
|
+
// ============================================================
|
|
74
|
+
// AUDIT TRAIL COMPLETENESS (GLI-19 3.4)
|
|
75
|
+
// ============================================================
|
|
76
|
+
describe('Section 3.4: Audit Trail Completeness', () => {
|
|
77
|
+
it('should include all required fields in RNG records', async () => {
|
|
78
|
+
const init = await initSession();
|
|
79
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
80
|
+
expect(spin.rngOutcome).toBeDefined();
|
|
81
|
+
for (const [actionId, record] of Object.entries(spin.rngOutcome)) {
|
|
82
|
+
expect(record).toHaveProperty('seed');
|
|
83
|
+
expect(record).toHaveProperty('result');
|
|
84
|
+
expect(record).toHaveProperty('min');
|
|
85
|
+
expect(record).toHaveProperty('max');
|
|
86
|
+
expect(typeof actionId).toBe('string');
|
|
87
|
+
expect(actionId.length).toBeGreaterThan(0);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
it('should track at least one RNG call per spin', async () => {
|
|
91
|
+
const init = await initSession();
|
|
92
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
93
|
+
expect(Object.keys(spin.rngOutcome || {}).length).toBeGreaterThan(0);
|
|
94
|
+
});
|
|
95
|
+
it('should have unique actionIds within command', async () => {
|
|
96
|
+
const init = await initSession();
|
|
97
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
98
|
+
const actionIds = Object.keys(spin.rngOutcome || {});
|
|
99
|
+
expect(actionIds.length).toBe(new Set(actionIds).size);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
// ============================================================
|
|
103
|
+
// TAMPER DETECTION (GLI-19 3.4.2)
|
|
104
|
+
// ============================================================
|
|
105
|
+
describe('Section 3.4.2: Tamper Detection', () => {
|
|
106
|
+
it('should detect modified seed', async () => {
|
|
107
|
+
const init = await initSession();
|
|
108
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
109
|
+
const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
|
|
110
|
+
const tampered = JSON.parse(JSON.stringify(spin.rngOutcome));
|
|
111
|
+
const key = Object.keys(tampered)[0];
|
|
112
|
+
tampered[key].seed = tampered[key].seed + 1;
|
|
113
|
+
expect(generateHash(canonicalizeForHash(tampered))).not.toBe(originalHash);
|
|
114
|
+
});
|
|
115
|
+
it('should detect modified result', async () => {
|
|
116
|
+
const init = await initSession();
|
|
117
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
118
|
+
const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
|
|
119
|
+
const tampered = JSON.parse(JSON.stringify(spin.rngOutcome));
|
|
120
|
+
const key = Object.keys(tampered)[0];
|
|
121
|
+
tampered[key].result = tampered[key].result + 1;
|
|
122
|
+
expect(generateHash(canonicalizeForHash(tampered))).not.toBe(originalHash);
|
|
123
|
+
});
|
|
124
|
+
it('should detect inserted record', async () => {
|
|
125
|
+
const init = await initSession();
|
|
126
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
127
|
+
const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
|
|
128
|
+
const tampered = { ...spin.rngOutcome, injected: { result: 5, seed: 99999, min: 0, max: 100 } };
|
|
129
|
+
expect(generateHash(canonicalizeForHash(tampered))).not.toBe(originalHash);
|
|
130
|
+
});
|
|
131
|
+
it('should detect removed record', async () => {
|
|
132
|
+
const init = await initSession();
|
|
133
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
134
|
+
const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
|
|
135
|
+
const tampered = { ...spin.rngOutcome };
|
|
136
|
+
delete tampered[Object.keys(tampered)[0]];
|
|
137
|
+
expect(generateHash(canonicalizeForHash(tampered))).not.toBe(originalHash);
|
|
138
|
+
});
|
|
139
|
+
it('should detect modified actionId', async () => {
|
|
140
|
+
const init = await initSession();
|
|
141
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
142
|
+
const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
|
|
143
|
+
const entries = Object.entries(spin.rngOutcome);
|
|
144
|
+
const tampered = {};
|
|
145
|
+
entries.forEach(([key, value], i) => {
|
|
146
|
+
tampered[i === 0 ? 'tampered_' + key : key] = value;
|
|
147
|
+
});
|
|
148
|
+
expect(generateHash(canonicalizeForHash(tampered))).not.toBe(originalHash);
|
|
149
|
+
});
|
|
150
|
+
it('should handle record reordering (canonicalization sorts)', () => {
|
|
151
|
+
const outcome1 = {
|
|
152
|
+
action_z: { result: 5, seed: 100, min: 0, max: 10 },
|
|
153
|
+
action_a: { result: 6, seed: 200, min: 0, max: 10 },
|
|
154
|
+
};
|
|
155
|
+
const outcome2 = {
|
|
156
|
+
action_a: { result: 6, seed: 200, min: 0, max: 10 },
|
|
157
|
+
action_z: { result: 5, seed: 100, min: 0, max: 10 },
|
|
158
|
+
};
|
|
159
|
+
expect(generateHash(canonicalizeForHash(outcome1))).toBe(generateHash(canonicalizeForHash(outcome2)));
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
// ============================================================
|
|
163
|
+
// DETERMINISTIC REPLAY (GLI-19 4.2.1)
|
|
164
|
+
// ============================================================
|
|
165
|
+
describe('Section 4.2.1: Deterministic Replay', () => {
|
|
166
|
+
it('should produce identical hash after serialization', async () => {
|
|
167
|
+
const init = await initSession();
|
|
168
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
169
|
+
const originalHash = generateHash(canonicalizeForHash(spin.rngOutcome));
|
|
170
|
+
const serialized = JSON.stringify(spin.rngOutcome);
|
|
171
|
+
const restored = JSON.parse(serialized);
|
|
172
|
+
expect(generateHash(canonicalizeForHash(restored))).toBe(originalHash);
|
|
173
|
+
});
|
|
174
|
+
it('should produce identical hash for number and string seeds', async () => {
|
|
175
|
+
const init = await initSession();
|
|
176
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
177
|
+
const hashWithNumbers = generateHash(canonicalizeForHash(spin.rngOutcome));
|
|
178
|
+
const withStrings = {};
|
|
179
|
+
for (const [k, v] of Object.entries(spin.rngOutcome)) {
|
|
180
|
+
withStrings[k] = { ...v, seed: String(v.seed) };
|
|
181
|
+
}
|
|
182
|
+
expect(generateHash(canonicalizeForHash(withStrings))).toBe(hashWithNumbers);
|
|
183
|
+
});
|
|
184
|
+
it('should be reproducible across multiple calls', async () => {
|
|
185
|
+
const init = await initSession();
|
|
186
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
187
|
+
const hashes = Array(100).fill(0).map(() => generateHash(canonicalizeForHash(spin.rngOutcome)));
|
|
188
|
+
expect(new Set(hashes).size).toBe(1);
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
// ============================================================
|
|
192
|
+
// DATA INTEGRITY
|
|
193
|
+
// ============================================================
|
|
194
|
+
describe('Data Integrity', () => {
|
|
195
|
+
it('should verify result within bounds', async () => {
|
|
196
|
+
const init = await initSession();
|
|
197
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
198
|
+
for (const [, record] of Object.entries(spin.rngOutcome)) {
|
|
199
|
+
expect(record.result).toBeGreaterThanOrEqual(record.min);
|
|
200
|
+
expect(record.result).toBeLessThanOrEqual(record.max);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
it('should verify all values are finite', async () => {
|
|
204
|
+
const init = await initSession();
|
|
205
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
206
|
+
for (const [, record] of Object.entries(spin.rngOutcome)) {
|
|
207
|
+
expect(Number.isFinite(record.seed)).toBe(true);
|
|
208
|
+
expect(Number.isFinite(record.result)).toBe(true);
|
|
209
|
+
expect(Number.isFinite(record.min)).toBe(true);
|
|
210
|
+
expect(Number.isFinite(record.max)).toBe(true);
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
it('should produce consistent SHA-256 hash format', async () => {
|
|
214
|
+
const init = await initSession();
|
|
215
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
216
|
+
const hash = generateHash(canonicalizeForHash(spin.rngOutcome));
|
|
217
|
+
expect(hash.length).toBe(64);
|
|
218
|
+
expect(/^[a-f0-9]+$/.test(hash)).toBe(true);
|
|
219
|
+
});
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
//# sourceMappingURL=rng-gli19-compliance.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rng-gli19-compliance.test.js","sourceRoot":"","sources":["../../src/__tests__/rng-gli19-compliance.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RNG Seed Security Tests - Bonny's Fortune
|
|
4
|
+
* Comprehensive Attack Vector Coverage for GLI-19 Compliance
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
const bonnys_fortune_v1_game_engine_1 = require("../bonnys-fortune-v1.game-engine");
|
|
8
|
+
const bonnys_fortune_types_1 = require("../logic/bonnys-fortune.types");
|
|
9
|
+
describe('RNG Seed Security - Attack Vector Tests', () => {
|
|
10
|
+
let engine;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
engine = new bonnys_fortune_v1_game_engine_1.BonnysFortuneV1GameEngine();
|
|
13
|
+
});
|
|
14
|
+
async function initSession() {
|
|
15
|
+
return engine.processCommand(null, null, {
|
|
16
|
+
id: 'init-security',
|
|
17
|
+
type: 'INIT_SESSION_STATE',
|
|
18
|
+
payload: { betAmountThresholds: [1, 2, 5, 10], defaultBetAmount: 1 },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
async function executeSpin(publicState, privateState) {
|
|
22
|
+
return engine.processCommand(publicState, privateState, {
|
|
23
|
+
id: `spin-${Math.random().toString(36).slice(2)}`,
|
|
24
|
+
type: 'SPIN',
|
|
25
|
+
payload: { sessionId: 'test', betAmount: 1, gameCode: 'bonnys-fortune', gameVersion: '1.0.0', lines: 10, creditsPerLine: 1 },
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async function executeBonusRound(publicState, privateState) {
|
|
29
|
+
const pendingBonus = privateState.pendingBonuses?.[0];
|
|
30
|
+
if (!pendingBonus) {
|
|
31
|
+
throw new Error('No pending bonus found');
|
|
32
|
+
}
|
|
33
|
+
return engine.processCommand(publicState, privateState, {
|
|
34
|
+
id: `bonus-${Math.random().toString(36).slice(2)}`,
|
|
35
|
+
type: 'START_BONUS_ROUND',
|
|
36
|
+
payload: {
|
|
37
|
+
sessionId: 'test',
|
|
38
|
+
betAmount: pendingBonus.betAmount || 1,
|
|
39
|
+
bonusId: pendingBonus.bonusId,
|
|
40
|
+
bonusType: pendingBonus.bonusType,
|
|
41
|
+
gameCode: 'bonnys-fortune',
|
|
42
|
+
gameVersion: '1.0.0',
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
async function executeSpinWithBonusHandling(publicState, privateState) {
|
|
47
|
+
const results = [];
|
|
48
|
+
let pub = publicState;
|
|
49
|
+
let priv = privateState;
|
|
50
|
+
const spin = await executeSpin(pub, priv);
|
|
51
|
+
results.push(spin);
|
|
52
|
+
pub = spin.publicState;
|
|
53
|
+
priv = spin.privateState;
|
|
54
|
+
// Handle bonus rounds - any non-BASE_GAME_SPIN with pending bonuses
|
|
55
|
+
while (priv.nextSpinType !== bonnys_fortune_types_1.SpinType.BASE_GAME_SPIN && priv.pendingBonuses?.length > 0) {
|
|
56
|
+
const bonus = await executeBonusRound(pub, priv);
|
|
57
|
+
results.push(bonus);
|
|
58
|
+
pub = bonus.publicState;
|
|
59
|
+
priv = bonus.privateState;
|
|
60
|
+
}
|
|
61
|
+
return { results, publicState: pub, privateState: priv };
|
|
62
|
+
}
|
|
63
|
+
function canonicalizeForHash(outcome) {
|
|
64
|
+
const entries = Object.entries(outcome)
|
|
65
|
+
.map(([actionId, record]) => ({
|
|
66
|
+
actionId,
|
|
67
|
+
result: Number(record.result),
|
|
68
|
+
seed: String(record.seed),
|
|
69
|
+
min: Number(record.min),
|
|
70
|
+
max: Number(record.max),
|
|
71
|
+
}))
|
|
72
|
+
.sort((a, b) => a.actionId.localeCompare(b.actionId));
|
|
73
|
+
return JSON.stringify(entries);
|
|
74
|
+
}
|
|
75
|
+
// ============================================================
|
|
76
|
+
// MALFORMED SEED INJECTION
|
|
77
|
+
// ============================================================
|
|
78
|
+
describe('Malformed Seed Injection', () => {
|
|
79
|
+
it('should handle whitespace-padded seeds differently', () => {
|
|
80
|
+
const outcome1 = { test: { result: 5, seed: 123, min: 0, max: 10 } };
|
|
81
|
+
const outcome2 = { test: { result: 5, seed: ' 123', min: 0, max: 10 } };
|
|
82
|
+
expect(canonicalizeForHash(outcome1)).not.toBe(canonicalizeForHash(outcome2));
|
|
83
|
+
});
|
|
84
|
+
it('should escape special JSON characters in actionIds', () => {
|
|
85
|
+
const outcome = { 'action\n"test': { result: 5, seed: 123, min: 0, max: 10 } };
|
|
86
|
+
const canonical = canonicalizeForHash(outcome);
|
|
87
|
+
expect(() => JSON.parse(canonical)).not.toThrow();
|
|
88
|
+
});
|
|
89
|
+
it('should handle zero-width unicode characters', () => {
|
|
90
|
+
const outcome = { test: { result: 5, seed: '123\u200B456', min: 0, max: 10 } };
|
|
91
|
+
const canonical = canonicalizeForHash(outcome);
|
|
92
|
+
expect(canonical.length).toBeGreaterThan(0);
|
|
93
|
+
});
|
|
94
|
+
it('should not interpret hex strings as numbers', () => {
|
|
95
|
+
const outcome = { test: { result: 5, seed: '0xFF', min: 0, max: 10 } };
|
|
96
|
+
expect(canonicalizeForHash(outcome)).toContain('"seed":"0xFF"');
|
|
97
|
+
});
|
|
98
|
+
it('should not interpret octal strings as numbers', () => {
|
|
99
|
+
const outcome = { test: { result: 5, seed: '0777', min: 0, max: 10 } };
|
|
100
|
+
expect(canonicalizeForHash(outcome)).toContain('"seed":"0777"');
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
// ============================================================
|
|
104
|
+
// TYPE CONFUSION
|
|
105
|
+
// ============================================================
|
|
106
|
+
describe('Type Confusion', () => {
|
|
107
|
+
it('should handle array seeds', () => {
|
|
108
|
+
const outcome = { test: { result: 5, seed: [123], min: 0, max: 10 } };
|
|
109
|
+
expect(canonicalizeForHash(outcome)).toContain('"seed":"123"');
|
|
110
|
+
});
|
|
111
|
+
it('should handle null seeds', () => {
|
|
112
|
+
const outcome = { test: { result: 5, seed: null, min: 0, max: 10 } };
|
|
113
|
+
expect(canonicalizeForHash(outcome)).toContain('"seed":"null"');
|
|
114
|
+
});
|
|
115
|
+
it('should handle undefined seeds', () => {
|
|
116
|
+
const outcome = { test: { result: 5, seed: undefined, min: 0, max: 10 } };
|
|
117
|
+
expect(canonicalizeForHash(outcome)).toContain('"seed":"undefined"');
|
|
118
|
+
});
|
|
119
|
+
it('should handle boolean seeds', () => {
|
|
120
|
+
const outcome = { test: { result: 5, seed: true, min: 0, max: 10 } };
|
|
121
|
+
expect(canonicalizeForHash(outcome)).toContain('"seed":"true"');
|
|
122
|
+
});
|
|
123
|
+
it('should handle BigInt seeds', () => {
|
|
124
|
+
const outcome = { test: { result: 5, seed: BigInt(123), min: 0, max: 10 } };
|
|
125
|
+
expect(canonicalizeForHash(outcome)).toContain('"seed":"123"');
|
|
126
|
+
});
|
|
127
|
+
it('should handle Symbol seeds', () => {
|
|
128
|
+
const symbol = Symbol('test');
|
|
129
|
+
expect(String(symbol)).toBe('Symbol(test)');
|
|
130
|
+
});
|
|
131
|
+
it('should handle object with custom toString', () => {
|
|
132
|
+
const malicious = { toString: () => '123', valueOf: () => 456 };
|
|
133
|
+
const outcome = { test: { result: 5, seed: malicious, min: 0, max: 10 } };
|
|
134
|
+
expect(canonicalizeForHash(outcome)).toContain('"seed":"123"');
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
// ============================================================
|
|
138
|
+
// INTEGER BOUNDARIES
|
|
139
|
+
// ============================================================
|
|
140
|
+
describe('Integer Boundaries', () => {
|
|
141
|
+
it('should handle MAX_SAFE_INTEGER', () => {
|
|
142
|
+
const outcome = { test: { result: 5, seed: Number.MAX_SAFE_INTEGER, min: 0, max: 10 } };
|
|
143
|
+
expect(canonicalizeForHash(outcome)).toContain(`"seed":"${Number.MAX_SAFE_INTEGER}"`);
|
|
144
|
+
});
|
|
145
|
+
it('should handle MIN_SAFE_INTEGER', () => {
|
|
146
|
+
const outcome = { test: { result: 5, seed: Number.MIN_SAFE_INTEGER, min: -100, max: 100 } };
|
|
147
|
+
expect(canonicalizeForHash(outcome)).toContain(`"seed":"${Number.MIN_SAFE_INTEGER}"`);
|
|
148
|
+
});
|
|
149
|
+
it('should handle zero seed', () => {
|
|
150
|
+
const outcome = { test: { result: 5, seed: 0, min: 0, max: 10 } };
|
|
151
|
+
expect(canonicalizeForHash(outcome)).toContain('"seed":"0"');
|
|
152
|
+
});
|
|
153
|
+
it('should handle negative seeds', () => {
|
|
154
|
+
const outcome = { test: { result: 5, seed: -12345, min: -100, max: 100 } };
|
|
155
|
+
expect(canonicalizeForHash(outcome)).toContain('"seed":"-12345"');
|
|
156
|
+
});
|
|
157
|
+
it('should handle NaN in result', () => {
|
|
158
|
+
const outcome = { test: { result: NaN, seed: 123, min: 0, max: 10 } };
|
|
159
|
+
expect(canonicalizeForHash(outcome)).toContain('"result":null');
|
|
160
|
+
});
|
|
161
|
+
it('should handle Infinity', () => {
|
|
162
|
+
const outcome = { test: { result: Infinity, seed: 123, min: 0, max: 10 } };
|
|
163
|
+
expect(canonicalizeForHash(outcome)).toContain('"result":null');
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
// ============================================================
|
|
167
|
+
// HASH COLLISION PREVENTION
|
|
168
|
+
// ============================================================
|
|
169
|
+
describe('Hash Collision Prevention', () => {
|
|
170
|
+
it('should produce different hashes for adjacent seeds', () => {
|
|
171
|
+
const outcome1 = { action: { result: 5, seed: 123, min: 0, max: 10 } };
|
|
172
|
+
const outcome2 = { action: { result: 5, seed: 124, min: 0, max: 10 } };
|
|
173
|
+
expect(canonicalizeForHash(outcome1)).not.toBe(canonicalizeForHash(outcome2));
|
|
174
|
+
});
|
|
175
|
+
it('should produce different hashes for different actionIds', () => {
|
|
176
|
+
const outcome1 = { action_a: { result: 5, seed: 123, min: 0, max: 10 } };
|
|
177
|
+
const outcome2 = { action_b: { result: 5, seed: 123, min: 0, max: 10 } };
|
|
178
|
+
expect(canonicalizeForHash(outcome1)).not.toBe(canonicalizeForHash(outcome2));
|
|
179
|
+
});
|
|
180
|
+
it('should produce same hash for number and string seeds after canonicalization', () => {
|
|
181
|
+
const outcome1 = { action: { result: 5, seed: 123, min: 0, max: 10 } };
|
|
182
|
+
const outcome2 = { action: { result: 5, seed: '123', min: 0, max: 10 } };
|
|
183
|
+
expect(canonicalizeForHash(outcome1)).toBe(canonicalizeForHash(outcome2));
|
|
184
|
+
});
|
|
185
|
+
it('should resist JSON injection in actionId', () => {
|
|
186
|
+
const outcome1 = { 'a","seed":"999': { result: 5, seed: 123, min: 0, max: 10 } };
|
|
187
|
+
const outcome2 = { 'a': { result: 5, seed: 999, min: 0, max: 10 } };
|
|
188
|
+
expect(canonicalizeForHash(outcome1)).not.toBe(canonicalizeForHash(outcome2));
|
|
189
|
+
});
|
|
190
|
+
});
|
|
191
|
+
// ============================================================
|
|
192
|
+
// PROTOTYPE POLLUTION
|
|
193
|
+
// ============================================================
|
|
194
|
+
describe('Prototype Pollution Prevention', () => {
|
|
195
|
+
it('should handle constructor as actionId', () => {
|
|
196
|
+
const outcome = { 'constructor': { result: 5, seed: 123, min: 0, max: 10 } };
|
|
197
|
+
const canonical = canonicalizeForHash(outcome);
|
|
198
|
+
expect(() => JSON.parse(canonical)).not.toThrow();
|
|
199
|
+
});
|
|
200
|
+
it('should handle toString as actionId', () => {
|
|
201
|
+
const outcome = { 'toString': { result: 5, seed: 123, min: 0, max: 10 } };
|
|
202
|
+
expect(() => canonicalizeForHash(outcome)).not.toThrow();
|
|
203
|
+
});
|
|
204
|
+
it('should not pollute prototype', () => {
|
|
205
|
+
const outcome = { 'normal': { result: 6, seed: 456, min: 0, max: 10 } };
|
|
206
|
+
canonicalizeForHash(outcome);
|
|
207
|
+
expect({}.__proto__.result).toBeUndefined();
|
|
208
|
+
});
|
|
209
|
+
});
|
|
210
|
+
// ============================================================
|
|
211
|
+
// ENGINE RNG VALIDATION
|
|
212
|
+
// ============================================================
|
|
213
|
+
describe('Engine RNG Validation', () => {
|
|
214
|
+
it('should return numeric seeds from engine', async () => {
|
|
215
|
+
const init = await initSession();
|
|
216
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
217
|
+
expect(spin.success).toBe(true);
|
|
218
|
+
if (spin.rngOutcome) {
|
|
219
|
+
for (const [, record] of Object.entries(spin.rngOutcome)) {
|
|
220
|
+
expect(typeof record.seed).toBe('number');
|
|
221
|
+
expect(Number.isSafeInteger(record.seed)).toBe(true);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
});
|
|
225
|
+
it('should have valid result within range', async () => {
|
|
226
|
+
const init = await initSession();
|
|
227
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
228
|
+
if (spin.rngOutcome) {
|
|
229
|
+
for (const [, record] of Object.entries(spin.rngOutcome)) {
|
|
230
|
+
expect(record.result).toBeGreaterThanOrEqual(record.min);
|
|
231
|
+
expect(record.result).toBeLessThanOrEqual(record.max);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
it('should have unique actionIds', async () => {
|
|
236
|
+
const init = await initSession();
|
|
237
|
+
const spin = await executeSpin(init.publicState, init.privateState);
|
|
238
|
+
if (spin.rngOutcome) {
|
|
239
|
+
const actionIds = Object.keys(spin.rngOutcome);
|
|
240
|
+
expect(actionIds.length).toBe(new Set(actionIds).size);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
it('should maintain type consistency across 50 spins', async () => {
|
|
244
|
+
const init = await initSession();
|
|
245
|
+
let pub = init.publicState;
|
|
246
|
+
let priv = init.privateState;
|
|
247
|
+
for (let i = 0; i < 50; i++) {
|
|
248
|
+
const { results, publicState, privateState } = await executeSpinWithBonusHandling(pub, priv);
|
|
249
|
+
// Verify RNG structure for all results (spin + any bonus rounds)
|
|
250
|
+
for (const result of results) {
|
|
251
|
+
if (result.rngOutcome) {
|
|
252
|
+
for (const [, record] of Object.entries(result.rngOutcome)) {
|
|
253
|
+
expect(typeof record.seed).toBe('number');
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
pub = publicState;
|
|
258
|
+
priv = privateState;
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
//# sourceMappingURL=rng-seed-security.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rng-seed-security.test.js","sourceRoot":"","sources":["../../src/__tests__/rng-seed-security.test.ts"],"names":[],"mappings":"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"}
|