@iam-protocol/pulse-sdk 0.1.1 → 0.2.1
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/index.d.mts +30 -5
- package/dist/index.d.ts +30 -5
- package/dist/index.js +124 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +119 -19
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/challenge/lissajous.ts +29 -0
- package/src/challenge/phrase.ts +34 -1
- package/src/config.ts +2 -1
- package/src/extraction/kinematic.ts +28 -5
- package/src/extraction/mfcc.ts +11 -4
- package/src/extraction/statistics.ts +46 -0
- package/src/index.ts +4 -4
- package/src/proof/prover.ts +4 -2
- package/src/proof/types.ts +1 -0
- package/src/pulse.ts +2 -3
- package/src/sensor/audio.ts +1 -9
- package/src/sensor/types.ts +0 -2
- package/test/integration.test.ts +3 -2
- package/test/serializer.test.ts +1 -0
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
declare const FINGERPRINT_BITS = 256;
|
|
2
2
|
declare const DEFAULT_THRESHOLD = 30;
|
|
3
|
+
declare const DEFAULT_MIN_DISTANCE = 3;
|
|
3
4
|
declare const MIN_CAPTURE_MS = 2000;
|
|
4
5
|
declare const MAX_CAPTURE_MS = 60000;
|
|
5
6
|
declare const DEFAULT_CAPTURE_MS = 7000;
|
|
@@ -50,8 +51,6 @@ interface CaptureOptions {
|
|
|
50
51
|
minDurationMs?: number;
|
|
51
52
|
/** Maximum capture duration in ms. Auto-stops if signal hasn't fired. Default: 60000 */
|
|
52
53
|
maxDurationMs?: number;
|
|
53
|
-
/** Called with RMS audio level (0-1) on each buffer during audio capture (~4x per second). */
|
|
54
|
-
onAudioLevel?: (rms: number) => void;
|
|
55
54
|
}
|
|
56
55
|
/** Stage of a capture session */
|
|
57
56
|
type CaptureStage = "audio" | "motion" | "touch";
|
|
@@ -120,7 +119,7 @@ declare class PulseSession {
|
|
|
120
119
|
private motionData;
|
|
121
120
|
private touchData;
|
|
122
121
|
constructor(config: ResolvedConfig, touchElement?: HTMLElement);
|
|
123
|
-
startAudio(
|
|
122
|
+
startAudio(): Promise<void>;
|
|
124
123
|
stopAudio(): Promise<AudioCapture | null>;
|
|
125
124
|
skipAudio(): void;
|
|
126
125
|
startMotion(): Promise<void>;
|
|
@@ -224,6 +223,18 @@ declare function variance(values: number[], mu?: number): number;
|
|
|
224
223
|
declare function skewness(values: number[]): number;
|
|
225
224
|
declare function kurtosis(values: number[]): number;
|
|
226
225
|
declare function condense(values: number[]): StatsSummary;
|
|
226
|
+
/**
|
|
227
|
+
* Shannon entropy over histogram bins. Measures information density.
|
|
228
|
+
* Real human data has moderate entropy (varied but structured).
|
|
229
|
+
* Synthetic data is either too uniform (high entropy) or too structured (low entropy).
|
|
230
|
+
*/
|
|
231
|
+
declare function entropy(values: number[], bins?: number): number;
|
|
232
|
+
/**
|
|
233
|
+
* Autocorrelation at a given lag. Detects periodic synthetic patterns.
|
|
234
|
+
* Real human data has low autocorrelation at most lags (chaotic/noisy).
|
|
235
|
+
* Synthetic data often has high autocorrelation (periodic/smooth).
|
|
236
|
+
*/
|
|
237
|
+
declare function autocorrelation(values: number[], lag?: number): number;
|
|
227
238
|
declare function fuseFeatures(audio: number[], motion: number[], touch: number[]): number[];
|
|
228
239
|
|
|
229
240
|
/** Serialized proof ready for on-chain submission */
|
|
@@ -248,6 +259,7 @@ interface CircuitInput {
|
|
|
248
259
|
commitment_new: string;
|
|
249
260
|
commitment_prev: string;
|
|
250
261
|
threshold: string;
|
|
262
|
+
min_distance: string;
|
|
251
263
|
}
|
|
252
264
|
/** Proof generation result */
|
|
253
265
|
interface ProofResult {
|
|
@@ -271,7 +283,7 @@ declare function serializeProof(proof: RawProof, publicSignals: string[]): Solan
|
|
|
271
283
|
/**
|
|
272
284
|
* Prepare circuit input from current and previous TBH data.
|
|
273
285
|
*/
|
|
274
|
-
declare function prepareCircuitInput(current: TBH, previous: TBH, threshold?: number): CircuitInput;
|
|
286
|
+
declare function prepareCircuitInput(current: TBH, previous: TBH, threshold?: number, minDistance?: number): CircuitInput;
|
|
275
287
|
/**
|
|
276
288
|
* Generate a Groth16 proof for the Hamming distance circuit.
|
|
277
289
|
*
|
|
@@ -348,6 +360,11 @@ declare function loadVerificationData(): StoredVerificationData | null;
|
|
|
348
360
|
* Each phrase is 5-6 syllable pairs, forming nonsensical but speakable words.
|
|
349
361
|
*/
|
|
350
362
|
declare function generatePhrase(wordCount?: number): string;
|
|
363
|
+
/**
|
|
364
|
+
* Generate a sequence of phrases for dynamic mid-session switching.
|
|
365
|
+
* Each phrase uses a different syllable subset to prevent pre-computation.
|
|
366
|
+
*/
|
|
367
|
+
declare function generatePhraseSequence(count?: number, wordCount?: number): string[];
|
|
351
368
|
|
|
352
369
|
/**
|
|
353
370
|
* Generate Lissajous curve points for the touch tracing challenge.
|
|
@@ -374,5 +391,13 @@ declare function randomLissajousParams(): LissajousParams;
|
|
|
374
391
|
* Generate Lissajous curve points normalized to [0, 1] range.
|
|
375
392
|
*/
|
|
376
393
|
declare function generateLissajousPoints(params: LissajousParams): Point2D[];
|
|
394
|
+
/**
|
|
395
|
+
* Generate a sequence of Lissajous curves for dynamic mid-session switching.
|
|
396
|
+
* Each curve uses different parameters, preventing pre-computation.
|
|
397
|
+
*/
|
|
398
|
+
declare function generateLissajousSequence(count?: number): {
|
|
399
|
+
params: LissajousParams;
|
|
400
|
+
points: Point2D[];
|
|
401
|
+
}[];
|
|
377
402
|
|
|
378
|
-
export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_THRESHOLD, FINGERPRINT_BITS, type FeatureVector, type FusedFeatureVector, type IdentityState, type LissajousParams, MAX_CAPTURE_MS, MIN_CAPTURE_MS, type MotionSample, PROGRAM_IDS, type PackedFingerprint, type Point2D, type ProofResult, type PulseConfig, PulseSDK, PulseSession, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, bigintToBytes32, computeCommitment, condense, fetchIdentityState, fuseFeatures, generateLissajousPoints, generatePhrase, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
|
|
403
|
+
export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_MIN_DISTANCE, DEFAULT_THRESHOLD, FINGERPRINT_BITS, type FeatureVector, type FusedFeatureVector, type IdentityState, type LissajousParams, MAX_CAPTURE_MS, MIN_CAPTURE_MS, type MotionSample, PROGRAM_IDS, type PackedFingerprint, type Point2D, type ProofResult, type PulseConfig, PulseSDK, PulseSession, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, autocorrelation, bigintToBytes32, computeCommitment, condense, entropy, fetchIdentityState, fuseFeatures, generateLissajousPoints, generateLissajousSequence, generatePhrase, generatePhraseSequence, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
declare const FINGERPRINT_BITS = 256;
|
|
2
2
|
declare const DEFAULT_THRESHOLD = 30;
|
|
3
|
+
declare const DEFAULT_MIN_DISTANCE = 3;
|
|
3
4
|
declare const MIN_CAPTURE_MS = 2000;
|
|
4
5
|
declare const MAX_CAPTURE_MS = 60000;
|
|
5
6
|
declare const DEFAULT_CAPTURE_MS = 7000;
|
|
@@ -50,8 +51,6 @@ interface CaptureOptions {
|
|
|
50
51
|
minDurationMs?: number;
|
|
51
52
|
/** Maximum capture duration in ms. Auto-stops if signal hasn't fired. Default: 60000 */
|
|
52
53
|
maxDurationMs?: number;
|
|
53
|
-
/** Called with RMS audio level (0-1) on each buffer during audio capture (~4x per second). */
|
|
54
|
-
onAudioLevel?: (rms: number) => void;
|
|
55
54
|
}
|
|
56
55
|
/** Stage of a capture session */
|
|
57
56
|
type CaptureStage = "audio" | "motion" | "touch";
|
|
@@ -120,7 +119,7 @@ declare class PulseSession {
|
|
|
120
119
|
private motionData;
|
|
121
120
|
private touchData;
|
|
122
121
|
constructor(config: ResolvedConfig, touchElement?: HTMLElement);
|
|
123
|
-
startAudio(
|
|
122
|
+
startAudio(): Promise<void>;
|
|
124
123
|
stopAudio(): Promise<AudioCapture | null>;
|
|
125
124
|
skipAudio(): void;
|
|
126
125
|
startMotion(): Promise<void>;
|
|
@@ -224,6 +223,18 @@ declare function variance(values: number[], mu?: number): number;
|
|
|
224
223
|
declare function skewness(values: number[]): number;
|
|
225
224
|
declare function kurtosis(values: number[]): number;
|
|
226
225
|
declare function condense(values: number[]): StatsSummary;
|
|
226
|
+
/**
|
|
227
|
+
* Shannon entropy over histogram bins. Measures information density.
|
|
228
|
+
* Real human data has moderate entropy (varied but structured).
|
|
229
|
+
* Synthetic data is either too uniform (high entropy) or too structured (low entropy).
|
|
230
|
+
*/
|
|
231
|
+
declare function entropy(values: number[], bins?: number): number;
|
|
232
|
+
/**
|
|
233
|
+
* Autocorrelation at a given lag. Detects periodic synthetic patterns.
|
|
234
|
+
* Real human data has low autocorrelation at most lags (chaotic/noisy).
|
|
235
|
+
* Synthetic data often has high autocorrelation (periodic/smooth).
|
|
236
|
+
*/
|
|
237
|
+
declare function autocorrelation(values: number[], lag?: number): number;
|
|
227
238
|
declare function fuseFeatures(audio: number[], motion: number[], touch: number[]): number[];
|
|
228
239
|
|
|
229
240
|
/** Serialized proof ready for on-chain submission */
|
|
@@ -248,6 +259,7 @@ interface CircuitInput {
|
|
|
248
259
|
commitment_new: string;
|
|
249
260
|
commitment_prev: string;
|
|
250
261
|
threshold: string;
|
|
262
|
+
min_distance: string;
|
|
251
263
|
}
|
|
252
264
|
/** Proof generation result */
|
|
253
265
|
interface ProofResult {
|
|
@@ -271,7 +283,7 @@ declare function serializeProof(proof: RawProof, publicSignals: string[]): Solan
|
|
|
271
283
|
/**
|
|
272
284
|
* Prepare circuit input from current and previous TBH data.
|
|
273
285
|
*/
|
|
274
|
-
declare function prepareCircuitInput(current: TBH, previous: TBH, threshold?: number): CircuitInput;
|
|
286
|
+
declare function prepareCircuitInput(current: TBH, previous: TBH, threshold?: number, minDistance?: number): CircuitInput;
|
|
275
287
|
/**
|
|
276
288
|
* Generate a Groth16 proof for the Hamming distance circuit.
|
|
277
289
|
*
|
|
@@ -348,6 +360,11 @@ declare function loadVerificationData(): StoredVerificationData | null;
|
|
|
348
360
|
* Each phrase is 5-6 syllable pairs, forming nonsensical but speakable words.
|
|
349
361
|
*/
|
|
350
362
|
declare function generatePhrase(wordCount?: number): string;
|
|
363
|
+
/**
|
|
364
|
+
* Generate a sequence of phrases for dynamic mid-session switching.
|
|
365
|
+
* Each phrase uses a different syllable subset to prevent pre-computation.
|
|
366
|
+
*/
|
|
367
|
+
declare function generatePhraseSequence(count?: number, wordCount?: number): string[];
|
|
351
368
|
|
|
352
369
|
/**
|
|
353
370
|
* Generate Lissajous curve points for the touch tracing challenge.
|
|
@@ -374,5 +391,13 @@ declare function randomLissajousParams(): LissajousParams;
|
|
|
374
391
|
* Generate Lissajous curve points normalized to [0, 1] range.
|
|
375
392
|
*/
|
|
376
393
|
declare function generateLissajousPoints(params: LissajousParams): Point2D[];
|
|
394
|
+
/**
|
|
395
|
+
* Generate a sequence of Lissajous curves for dynamic mid-session switching.
|
|
396
|
+
* Each curve uses different parameters, preventing pre-computation.
|
|
397
|
+
*/
|
|
398
|
+
declare function generateLissajousSequence(count?: number): {
|
|
399
|
+
params: LissajousParams;
|
|
400
|
+
points: Point2D[];
|
|
401
|
+
}[];
|
|
377
402
|
|
|
378
|
-
export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_THRESHOLD, FINGERPRINT_BITS, type FeatureVector, type FusedFeatureVector, type IdentityState, type LissajousParams, MAX_CAPTURE_MS, MIN_CAPTURE_MS, type MotionSample, PROGRAM_IDS, type PackedFingerprint, type Point2D, type ProofResult, type PulseConfig, PulseSDK, PulseSession, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, bigintToBytes32, computeCommitment, condense, fetchIdentityState, fuseFeatures, generateLissajousPoints, generatePhrase, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
|
|
403
|
+
export { type AudioCapture, type CaptureOptions, type CaptureStage, type CircuitInput, DEFAULT_CAPTURE_MS, DEFAULT_MIN_DISTANCE, DEFAULT_THRESHOLD, FINGERPRINT_BITS, type FeatureVector, type FusedFeatureVector, type IdentityState, type LissajousParams, MAX_CAPTURE_MS, MIN_CAPTURE_MS, type MotionSample, PROGRAM_IDS, type PackedFingerprint, type Point2D, type ProofResult, type PulseConfig, PulseSDK, PulseSession, type SensorData, type SolanaProof, type StageState, type StatsSummary, type StoredVerificationData, type SubmissionResult, type TBH, type TemporalFingerprint, type TouchSample, type VerificationResult, autocorrelation, bigintToBytes32, computeCommitment, condense, entropy, fetchIdentityState, fuseFeatures, generateLissajousPoints, generateLissajousSequence, generatePhrase, generatePhraseSequence, generateProof, generateSalt, generateSolanaProof, generateTBH, hammingDistance, kurtosis, loadVerificationData, mean, packBits, prepareCircuitInput, randomLissajousParams, serializeProof, simhash, skewness, storeVerificationData, submitViaRelayer, submitViaWallet, toBigEndian32, variance };
|
package/dist/index.js
CHANGED
|
@@ -12978,6 +12978,7 @@ var init_esm4 = __esm({
|
|
|
12978
12978
|
var index_exports = {};
|
|
12979
12979
|
__export(index_exports, {
|
|
12980
12980
|
DEFAULT_CAPTURE_MS: () => DEFAULT_CAPTURE_MS,
|
|
12981
|
+
DEFAULT_MIN_DISTANCE: () => DEFAULT_MIN_DISTANCE,
|
|
12981
12982
|
DEFAULT_THRESHOLD: () => DEFAULT_THRESHOLD,
|
|
12982
12983
|
FINGERPRINT_BITS: () => FINGERPRINT_BITS,
|
|
12983
12984
|
MAX_CAPTURE_MS: () => MAX_CAPTURE_MS,
|
|
@@ -12985,13 +12986,17 @@ __export(index_exports, {
|
|
|
12985
12986
|
PROGRAM_IDS: () => PROGRAM_IDS,
|
|
12986
12987
|
PulseSDK: () => PulseSDK,
|
|
12987
12988
|
PulseSession: () => PulseSession,
|
|
12989
|
+
autocorrelation: () => autocorrelation,
|
|
12988
12990
|
bigintToBytes32: () => bigintToBytes32,
|
|
12989
12991
|
computeCommitment: () => computeCommitment,
|
|
12990
12992
|
condense: () => condense,
|
|
12993
|
+
entropy: () => entropy,
|
|
12991
12994
|
fetchIdentityState: () => fetchIdentityState,
|
|
12992
12995
|
fuseFeatures: () => fuseFeatures,
|
|
12993
12996
|
generateLissajousPoints: () => generateLissajousPoints,
|
|
12997
|
+
generateLissajousSequence: () => generateLissajousSequence,
|
|
12994
12998
|
generatePhrase: () => generatePhrase,
|
|
12999
|
+
generatePhraseSequence: () => generatePhraseSequence,
|
|
12995
13000
|
generateProof: () => generateProof,
|
|
12996
13001
|
generateSalt: () => generateSalt,
|
|
12997
13002
|
generateSolanaProof: () => generateSolanaProof,
|
|
@@ -13023,6 +13028,7 @@ var BN254_SCALAR_FIELD = BigInt(
|
|
|
13023
13028
|
);
|
|
13024
13029
|
var FINGERPRINT_BITS = 256;
|
|
13025
13030
|
var DEFAULT_THRESHOLD = 30;
|
|
13031
|
+
var DEFAULT_MIN_DISTANCE = 3;
|
|
13026
13032
|
var PROOF_A_SIZE = 64;
|
|
13027
13033
|
var PROOF_B_SIZE = 128;
|
|
13028
13034
|
var PROOF_C_SIZE = 64;
|
|
@@ -13043,8 +13049,7 @@ async function captureAudio(options = {}) {
|
|
|
13043
13049
|
const {
|
|
13044
13050
|
signal,
|
|
13045
13051
|
minDurationMs = MIN_CAPTURE_MS,
|
|
13046
|
-
maxDurationMs = MAX_CAPTURE_MS
|
|
13047
|
-
onAudioLevel
|
|
13052
|
+
maxDurationMs = MAX_CAPTURE_MS
|
|
13048
13053
|
} = options;
|
|
13049
13054
|
const stream = await navigator.mediaDevices.getUserMedia({
|
|
13050
13055
|
audio: {
|
|
@@ -13064,13 +13069,7 @@ async function captureAudio(options = {}) {
|
|
|
13064
13069
|
const bufferSize = 4096;
|
|
13065
13070
|
const processor = ctx.createScriptProcessor(bufferSize, 1, 1);
|
|
13066
13071
|
processor.onaudioprocess = (e2) => {
|
|
13067
|
-
|
|
13068
|
-
chunks.push(new Float32Array(data));
|
|
13069
|
-
if (onAudioLevel) {
|
|
13070
|
-
let sum = 0;
|
|
13071
|
-
for (let i = 0; i < data.length; i++) sum += data[i] * data[i];
|
|
13072
|
-
onAudioLevel(Math.sqrt(sum / data.length));
|
|
13073
|
-
}
|
|
13072
|
+
chunks.push(new Float32Array(e2.inputBuffer.getChannelData(0)));
|
|
13074
13073
|
};
|
|
13075
13074
|
source.connect(processor);
|
|
13076
13075
|
processor.connect(ctx.destination);
|
|
@@ -13269,6 +13268,37 @@ function condense(values) {
|
|
|
13269
13268
|
kurtosis: kurtosis(values)
|
|
13270
13269
|
};
|
|
13271
13270
|
}
|
|
13271
|
+
function entropy(values, bins = 16) {
|
|
13272
|
+
if (values.length < 2) return 0;
|
|
13273
|
+
const min = Math.min(...values);
|
|
13274
|
+
const max = Math.max(...values);
|
|
13275
|
+
if (min === max) return 0;
|
|
13276
|
+
const counts = new Array(bins).fill(0);
|
|
13277
|
+
const range = max - min;
|
|
13278
|
+
for (const v of values) {
|
|
13279
|
+
const idx = Math.min(Math.floor((v - min) / range * bins), bins - 1);
|
|
13280
|
+
counts[idx]++;
|
|
13281
|
+
}
|
|
13282
|
+
let h = 0;
|
|
13283
|
+
for (const c of counts) {
|
|
13284
|
+
if (c > 0) {
|
|
13285
|
+
const p = c / values.length;
|
|
13286
|
+
h -= p * Math.log2(p);
|
|
13287
|
+
}
|
|
13288
|
+
}
|
|
13289
|
+
return h;
|
|
13290
|
+
}
|
|
13291
|
+
function autocorrelation(values, lag = 1) {
|
|
13292
|
+
if (values.length <= lag) return 0;
|
|
13293
|
+
const m = mean(values);
|
|
13294
|
+
const v = variance(values, m);
|
|
13295
|
+
if (v === 0) return 0;
|
|
13296
|
+
let sum = 0;
|
|
13297
|
+
for (let i = 0; i < values.length - lag; i++) {
|
|
13298
|
+
sum += (values[i] - m) * (values[i + lag] - m);
|
|
13299
|
+
}
|
|
13300
|
+
return sum / ((values.length - lag) * v);
|
|
13301
|
+
}
|
|
13272
13302
|
function fuseFeatures(audio, motion, touch) {
|
|
13273
13303
|
return [...audio, ...motion, ...touch];
|
|
13274
13304
|
}
|
|
@@ -13283,10 +13313,10 @@ function extractMFCC(audio) {
|
|
|
13283
13313
|
try {
|
|
13284
13314
|
Meyda = require("meyda");
|
|
13285
13315
|
} catch {
|
|
13286
|
-
return new Array(NUM_MFCC * 3 * 4).fill(0);
|
|
13316
|
+
return new Array(NUM_MFCC * 3 * 4 + NUM_MFCC).fill(0);
|
|
13287
13317
|
}
|
|
13288
13318
|
const numFrames = Math.floor((samples.length - FRAME_SIZE) / HOP_SIZE) + 1;
|
|
13289
|
-
if (numFrames < 3) return new Array(NUM_MFCC * 3 * 4).fill(0);
|
|
13319
|
+
if (numFrames < 3) return new Array(NUM_MFCC * 3 * 4 + NUM_MFCC).fill(0);
|
|
13290
13320
|
const mfccFrames = [];
|
|
13291
13321
|
for (let i = 0; i < numFrames; i++) {
|
|
13292
13322
|
const start = i * HOP_SIZE;
|
|
@@ -13321,6 +13351,10 @@ function extractMFCC(audio) {
|
|
|
13321
13351
|
const stats = condense(dd);
|
|
13322
13352
|
features.push(stats.mean, stats.variance, stats.skewness, stats.kurtosis);
|
|
13323
13353
|
}
|
|
13354
|
+
for (let c = 0; c < NUM_MFCC; c++) {
|
|
13355
|
+
const raw = mfccFrames.map((f) => f[c] ?? 0);
|
|
13356
|
+
features.push(entropy(raw));
|
|
13357
|
+
}
|
|
13324
13358
|
return features;
|
|
13325
13359
|
}
|
|
13326
13360
|
function computeDeltas(frames) {
|
|
@@ -13335,7 +13369,7 @@ function computeDeltas(frames) {
|
|
|
13335
13369
|
|
|
13336
13370
|
// src/extraction/kinematic.ts
|
|
13337
13371
|
function extractMotionFeatures(samples) {
|
|
13338
|
-
if (samples.length < 5) return new Array(
|
|
13372
|
+
if (samples.length < 5) return new Array(54).fill(0);
|
|
13339
13373
|
const axes = {
|
|
13340
13374
|
ax: samples.map((s) => s.ax),
|
|
13341
13375
|
ay: samples.map((s) => s.ay),
|
|
@@ -13361,10 +13395,19 @@ function extractMotionFeatures(samples) {
|
|
|
13361
13395
|
jounceStats.kurtosis
|
|
13362
13396
|
);
|
|
13363
13397
|
}
|
|
13398
|
+
for (const values of Object.values(axes)) {
|
|
13399
|
+
const jerk = derivative(values);
|
|
13400
|
+
const windowSize = Math.max(5, Math.floor(jerk.length / 4));
|
|
13401
|
+
const windowVariances = [];
|
|
13402
|
+
for (let i = 0; i <= jerk.length - windowSize; i += windowSize) {
|
|
13403
|
+
windowVariances.push(variance(jerk.slice(i, i + windowSize)));
|
|
13404
|
+
}
|
|
13405
|
+
features.push(windowVariances.length >= 2 ? variance(windowVariances) : 0);
|
|
13406
|
+
}
|
|
13364
13407
|
return features;
|
|
13365
13408
|
}
|
|
13366
13409
|
function extractTouchFeatures(samples) {
|
|
13367
|
-
if (samples.length < 5) return new Array(
|
|
13410
|
+
if (samples.length < 5) return new Array(36).fill(0);
|
|
13368
13411
|
const x = samples.map((s) => s.x);
|
|
13369
13412
|
const y = samples.map((s) => s.y);
|
|
13370
13413
|
const pressure = samples.map((s) => s.pressure);
|
|
@@ -13384,6 +13427,14 @@ function extractTouchFeatures(samples) {
|
|
|
13384
13427
|
const jerkY = derivative(accY);
|
|
13385
13428
|
features.push(...Object.values(condense(jerkX)));
|
|
13386
13429
|
features.push(...Object.values(condense(jerkY)));
|
|
13430
|
+
for (const values of [vx, vy, pressure, area]) {
|
|
13431
|
+
const windowSize = Math.max(5, Math.floor(values.length / 4));
|
|
13432
|
+
const windowVariances = [];
|
|
13433
|
+
for (let i = 0; i <= values.length - windowSize; i += windowSize) {
|
|
13434
|
+
windowVariances.push(variance(values.slice(i, i + windowSize)));
|
|
13435
|
+
}
|
|
13436
|
+
features.push(windowVariances.length >= 2 ? variance(windowVariances) : 0);
|
|
13437
|
+
}
|
|
13387
13438
|
return features;
|
|
13388
13439
|
}
|
|
13389
13440
|
function derivative(values) {
|
|
@@ -13565,7 +13616,7 @@ async function getSnarkjs() {
|
|
|
13565
13616
|
}
|
|
13566
13617
|
return snarkjsModule;
|
|
13567
13618
|
}
|
|
13568
|
-
function prepareCircuitInput(current, previous, threshold = DEFAULT_THRESHOLD) {
|
|
13619
|
+
function prepareCircuitInput(current, previous, threshold = DEFAULT_THRESHOLD, minDistance = DEFAULT_MIN_DISTANCE) {
|
|
13569
13620
|
return {
|
|
13570
13621
|
ft_new: current.fingerprint,
|
|
13571
13622
|
ft_prev: previous.fingerprint,
|
|
@@ -13573,7 +13624,8 @@ function prepareCircuitInput(current, previous, threshold = DEFAULT_THRESHOLD) {
|
|
|
13573
13624
|
salt_prev: previous.salt.toString(),
|
|
13574
13625
|
commitment_new: current.commitment.toString(),
|
|
13575
13626
|
commitment_prev: previous.commitment.toString(),
|
|
13576
|
-
threshold: threshold.toString()
|
|
13627
|
+
threshold: threshold.toString(),
|
|
13628
|
+
min_distance: minDistance.toString()
|
|
13577
13629
|
};
|
|
13578
13630
|
}
|
|
13579
13631
|
async function generateProof(input, wasmPath, zkeyPath) {
|
|
@@ -13791,7 +13843,7 @@ var inMemoryStore = null;
|
|
|
13791
13843
|
|
|
13792
13844
|
// src/pulse.ts
|
|
13793
13845
|
function extractFeatures(data) {
|
|
13794
|
-
const audioFeatures = data.audio ? extractMFCC(data.audio) : new Array(
|
|
13846
|
+
const audioFeatures = data.audio ? extractMFCC(data.audio) : new Array(169).fill(0);
|
|
13795
13847
|
const motionFeatures = extractMotionFeatures(data.motion);
|
|
13796
13848
|
const touchFeatures = extractTouchFeatures(data.touch);
|
|
13797
13849
|
return fuseFeatures(audioFeatures, motionFeatures, touchFeatures);
|
|
@@ -13887,14 +13939,13 @@ var PulseSession = class {
|
|
|
13887
13939
|
this.touchElement = touchElement;
|
|
13888
13940
|
}
|
|
13889
13941
|
// --- Audio ---
|
|
13890
|
-
async startAudio(
|
|
13942
|
+
async startAudio() {
|
|
13891
13943
|
if (this.audioStageState !== "idle")
|
|
13892
13944
|
throw new Error("Audio capture already started");
|
|
13893
13945
|
this.audioStageState = "capturing";
|
|
13894
13946
|
this.audioController = new AbortController();
|
|
13895
13947
|
this.audioPromise = captureAudio({
|
|
13896
|
-
signal: this.audioController.signal
|
|
13897
|
-
onAudioLevel
|
|
13948
|
+
signal: this.audioController.signal
|
|
13898
13949
|
}).catch(() => null);
|
|
13899
13950
|
}
|
|
13900
13951
|
async stopAudio() {
|
|
@@ -14142,6 +14193,28 @@ function generatePhrase(wordCount = 5) {
|
|
|
14142
14193
|
}
|
|
14143
14194
|
return words.join(" ");
|
|
14144
14195
|
}
|
|
14196
|
+
function generatePhraseSequence(count = 3, wordCount = 4) {
|
|
14197
|
+
const subsetSize = Math.floor(SYLLABLES.length / count);
|
|
14198
|
+
const phrases = [];
|
|
14199
|
+
for (let p = 0; p < count; p++) {
|
|
14200
|
+
const start = p * subsetSize % SYLLABLES.length;
|
|
14201
|
+
const subset = [
|
|
14202
|
+
...SYLLABLES.slice(start, start + subsetSize),
|
|
14203
|
+
...SYLLABLES.slice(0, Math.max(0, start + subsetSize - SYLLABLES.length))
|
|
14204
|
+
];
|
|
14205
|
+
const words = [];
|
|
14206
|
+
for (let w = 0; w < wordCount; w++) {
|
|
14207
|
+
const syllableCount = 2 + Math.floor(Math.random() * 2);
|
|
14208
|
+
let word = "";
|
|
14209
|
+
for (let s = 0; s < syllableCount; s++) {
|
|
14210
|
+
word += subset[Math.floor(Math.random() * subset.length)];
|
|
14211
|
+
}
|
|
14212
|
+
words.push(word);
|
|
14213
|
+
}
|
|
14214
|
+
phrases.push(words.join(" "));
|
|
14215
|
+
}
|
|
14216
|
+
return phrases;
|
|
14217
|
+
}
|
|
14145
14218
|
|
|
14146
14219
|
// src/challenge/lissajous.ts
|
|
14147
14220
|
function randomLissajousParams() {
|
|
@@ -14172,9 +14245,37 @@ function generateLissajousPoints(params) {
|
|
|
14172
14245
|
}
|
|
14173
14246
|
return result;
|
|
14174
14247
|
}
|
|
14248
|
+
function generateLissajousSequence(count = 2) {
|
|
14249
|
+
const allRatios = [
|
|
14250
|
+
[1, 2],
|
|
14251
|
+
[2, 3],
|
|
14252
|
+
[3, 4],
|
|
14253
|
+
[3, 5],
|
|
14254
|
+
[4, 5],
|
|
14255
|
+
[1, 3],
|
|
14256
|
+
[2, 5],
|
|
14257
|
+
[5, 6],
|
|
14258
|
+
[3, 7],
|
|
14259
|
+
[4, 7]
|
|
14260
|
+
];
|
|
14261
|
+
const shuffled = [...allRatios].sort(() => Math.random() - 0.5);
|
|
14262
|
+
const sequence = [];
|
|
14263
|
+
for (let i = 0; i < count; i++) {
|
|
14264
|
+
const pair = shuffled[i % shuffled.length];
|
|
14265
|
+
const params = {
|
|
14266
|
+
a: pair[0],
|
|
14267
|
+
b: pair[1],
|
|
14268
|
+
delta: Math.PI * (0.1 + Math.random() * 0.8),
|
|
14269
|
+
points: 200
|
|
14270
|
+
};
|
|
14271
|
+
sequence.push({ params, points: generateLissajousPoints(params) });
|
|
14272
|
+
}
|
|
14273
|
+
return sequence;
|
|
14274
|
+
}
|
|
14175
14275
|
// Annotate the CommonJS export names for ESM import in node:
|
|
14176
14276
|
0 && (module.exports = {
|
|
14177
14277
|
DEFAULT_CAPTURE_MS,
|
|
14278
|
+
DEFAULT_MIN_DISTANCE,
|
|
14178
14279
|
DEFAULT_THRESHOLD,
|
|
14179
14280
|
FINGERPRINT_BITS,
|
|
14180
14281
|
MAX_CAPTURE_MS,
|
|
@@ -14182,13 +14283,17 @@ function generateLissajousPoints(params) {
|
|
|
14182
14283
|
PROGRAM_IDS,
|
|
14183
14284
|
PulseSDK,
|
|
14184
14285
|
PulseSession,
|
|
14286
|
+
autocorrelation,
|
|
14185
14287
|
bigintToBytes32,
|
|
14186
14288
|
computeCommitment,
|
|
14187
14289
|
condense,
|
|
14290
|
+
entropy,
|
|
14188
14291
|
fetchIdentityState,
|
|
14189
14292
|
fuseFeatures,
|
|
14190
14293
|
generateLissajousPoints,
|
|
14294
|
+
generateLissajousSequence,
|
|
14191
14295
|
generatePhrase,
|
|
14296
|
+
generatePhraseSequence,
|
|
14192
14297
|
generateProof,
|
|
14193
14298
|
generateSalt,
|
|
14194
14299
|
generateSolanaProof,
|