From 8177e63f41af666bb401482f56d0b9d38af9c8a5 Mon Sep 17 00:00:00 2001 From: Stefan Thomas Date: Tue, 10 Sep 2024 08:43:10 -0400 Subject: [PATCH] feat(reactive): provide deterministic randomness --- .../src/test/mocks/test-environment.ts | 5 ++-- packages/lib-reactive/src/index.ts | 4 +++ .../src/mocks/deterministic-crypto.ts} | 18 ++++++------- .../src/test/deterministic-crypto.test.ts | 25 +++++++++++++++++++ 4 files changed, 41 insertions(+), 11 deletions(-) rename packages/{lib-protocol-stream/src/test/mocks/crypto-context.ts => lib-reactive/src/mocks/deterministic-crypto.ts} (75%) create mode 100644 packages/lib-reactive/src/test/deterministic-crypto.test.ts diff --git a/packages/lib-protocol-stream/src/test/mocks/test-environment.ts b/packages/lib-protocol-stream/src/test/mocks/test-environment.ts index f5ae918f..b24f1a17 100644 --- a/packages/lib-protocol-stream/src/test/mocks/test-environment.ts +++ b/packages/lib-protocol-stream/src/test/mocks/test-environment.ts @@ -17,14 +17,15 @@ import { type Crypto, type DisposableScope, createMockClock, + createMockDeterministicCrypto, createScope, createTopic, } from "@dassie/lib-reactive" +import { createCrypto } from "@dassie/lib-reactive-io" import type { StreamProtocolContext } from "../../context/context" import { DEFAULT_POLICY, type StreamPolicy } from "../../context/policy" import { getPskEnvironment } from "../../crypto/functions" -import { createMockCryptoContext } from "./crypto-context" interface EnvironmentOptions { // Environment options @@ -67,7 +68,7 @@ export function createTestEnvironment({ maxPacketsInFlight = Infinity, scope = createScope("test-environment"), logger = createLogger("das:test:stream"), - crypto = createMockCryptoContext(), + crypto = createMockDeterministicCrypto(createCrypto()), clock = createMockClock(), policy = DEFAULT_POLICY, }: EnvironmentOptions = {}) { diff --git a/packages/lib-reactive/src/index.ts b/packages/lib-reactive/src/index.ts index 52eaf8ce..0b062e4d 100644 --- a/packages/lib-reactive/src/index.ts +++ b/packages/lib-reactive/src/index.ts @@ -35,6 +35,10 @@ export { MockClockImplementation, DEFAULT_MOCK_CLOCK_TIME, } from "./mocks/clock" +export { + createMockDeterministicCrypto, + Xoshiro128PlusPlus, +} from "./mocks/deterministic-crypto" export { delay, delayWithAbortSignal } from "./tools/delay" export type { Topic, ReadonlyTopic, InferTopicType } from "./topic" diff --git a/packages/lib-protocol-stream/src/test/mocks/crypto-context.ts b/packages/lib-reactive/src/mocks/deterministic-crypto.ts similarity index 75% rename from packages/lib-protocol-stream/src/test/mocks/crypto-context.ts rename to packages/lib-reactive/src/mocks/deterministic-crypto.ts index 8f40cc3b..83ce05fc 100644 --- a/packages/lib-protocol-stream/src/test/mocks/crypto-context.ts +++ b/packages/lib-reactive/src/mocks/deterministic-crypto.ts @@ -1,14 +1,16 @@ -import type { Crypto } from "@dassie/lib-reactive" -import { createCrypto } from "@dassie/lib-reactive-io" +import type { Crypto } from "../types/base-modules/crypto" const DEFAULT_SEED = new Uint32Array([ 0x13_67_bd_3b, 0x83_31_fa_de, 0x7b_96_f6_b5, 0xb4_9e_9f_58, ]) -export function createMockCryptoContext(): Crypto { - const randomGenerator = new Xoshiro128PlusPlus() +export function createMockDeterministicCrypto( + crypto: Crypto, + seed: Uint32Array = DEFAULT_SEED, +): Crypto { + const randomGenerator = new Xoshiro128PlusPlus(seed) - return Object.assign(createCrypto(), { + return Object.assign(crypto, { getRandomBytes: (length: number) => { const data = new Uint32Array(Math.ceil(length / 4)) @@ -16,14 +18,12 @@ export function createMockCryptoContext(): Crypto { data[index] = randomGenerator.next() } - const result = new Uint8Array(data.buffer) - - return result.subarray(0, length) + return new Uint8Array(data.buffer, 0, length) }, }) } -class Xoshiro128PlusPlus { +export class Xoshiro128PlusPlus { private state: Uint32Array constructor(seed: Uint32Array = DEFAULT_SEED) { diff --git a/packages/lib-reactive/src/test/deterministic-crypto.test.ts b/packages/lib-reactive/src/test/deterministic-crypto.test.ts new file mode 100644 index 00000000..6ec5d3a9 --- /dev/null +++ b/packages/lib-reactive/src/test/deterministic-crypto.test.ts @@ -0,0 +1,25 @@ +import { uint8ArrayToHex } from "uint8array-extras" +import { describe, test } from "vitest" + +import { createMockDeterministicCrypto } from "../mocks/deterministic-crypto" +import type { Crypto } from "../types/base-modules/crypto" + +describe("Deterministic Crypto", () => { + test("should generate (not so) random bytes", ({ expect }) => { + const crypto = createMockDeterministicCrypto({} as Crypto) + const randomNumber = crypto.getRandomBytes(32) + expect(uint8ArrayToHex(randomNumber)).toMatchInlineSnapshot( + `"1f0796160c164e1fdcc306caed8497f7c424a4e4d66f5653f50d1f35e47638d9"`, + ) + }) + + test("should generate (not so) random bytes of a length that is not a multiple of 4", ({ + expect, + }) => { + const crypto = createMockDeterministicCrypto({} as Crypto) + const randomNumber = crypto.getRandomBytes(31) + expect(uint8ArrayToHex(randomNumber)).toMatchInlineSnapshot( + `"1f0796160c164e1fdcc306caed8497f7c424a4e4d66f5653f50d1f35e47638"`, + ) + }) +})