@verifiedonchain-protocol/sdk 0.1.1 → 0.1.2

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.ts CHANGED
@@ -259,6 +259,8 @@ declare function checkFaceOcclusionRaw(landmarks: NormalizedLandmark[], ctx: Can
259
259
  /** @deprecated Use tracker + checkFaceOcclusionRaw */
260
260
  declare function checkFaceOcclusion(landmarks: NormalizedLandmark[], ctx: CanvasRenderingContext2D, width: number, height: number): FaceOcclusionResult;
261
261
 
262
+ declare const EMBEDDING_DIMENSIONS = 1024;
263
+ declare const SIGNATURE_BITS = 256;
262
264
  /**
263
265
  * Generates deterministic random hyperplanes
264
266
  * These chop the high-dimensional space into 256 regions
@@ -560,4 +562,4 @@ interface SocialConnectionProps {
560
562
  }
561
563
  declare const SocialConnection: React.FC<SocialConnectionProps>;
562
564
 
563
- export { type AleoCircuitInputs, type AleoCircuitInputsComplete, type AleoEmbeddingChunks, type AleoSimHashInputs, BACKEND_PRIORITY, type BiometricSimHashPayload, CAPTURE_QUALITY, type CaptureQualityResult, type ChainType, type DataQuality, EmbeddingQuantizer, type FaceBox, type FaceOcclusionResult, FaceOcclusionTracker, FaceZKInner as FaceZK, type FlashColor, GlassesDetector, HUMAN_CONFIG, type LivenessData, LivenessEngine, type LivenessResult, MaskDetector, type NormalizedLandmark, type OcclusionCheckOptions, ProofOfHumanity, type RawFacialData, type RelayerConfig, type RelayerResponse, SocialConnection, VERIFICATION_CONSTANTS, type VerificationPayload, type VerificationResult, type VerifiedIdentityData, VerifiedOnchainFlow, type VerifiedOnchainFlowProps, VerifiedRelayerClient, type VisionModels, WalletSelection, averageEmbeddings, calculateHammingDistance, checkFaceOcclusion, checkFaceOcclusionRaw, computeSimHash, createCalibratedQuantizer, createLivenessData, evaluateCaptureQuality, extractRawFacialData, generateBiometricSimHash, generateHyperplanes, generateRandomSalt, getFaceBox, getFaceLuminance, getHyperplanes, getRegionLuminance, hammingDistance, normalizeEmbedding, processFacialDataForAleo, similarityFromDistance, useCameraStream, useVisionModels, validateDataQuality };
565
+ export { type AleoCircuitInputs, type AleoCircuitInputsComplete, type AleoEmbeddingChunks, type AleoSimHashInputs, BACKEND_PRIORITY, type BiometricSimHashPayload, CAPTURE_QUALITY, type CaptureQualityResult, type ChainType, type DataQuality, EMBEDDING_DIMENSIONS, EmbeddingQuantizer, type FaceBox, type FaceOcclusionResult, FaceOcclusionTracker, FaceZKInner as FaceZK, type FlashColor, GlassesDetector, HUMAN_CONFIG, type LivenessData, LivenessEngine, type LivenessResult, MaskDetector, type NormalizedLandmark, type OcclusionCheckOptions, ProofOfHumanity, type RawFacialData, type RelayerConfig, type RelayerResponse, SIGNATURE_BITS, SocialConnection, VERIFICATION_CONSTANTS, type VerificationPayload, type VerificationResult, type VerifiedIdentityData, VerifiedOnchainFlow, type VerifiedOnchainFlowProps, VerifiedRelayerClient, type VisionModels, WalletSelection, averageEmbeddings, calculateHammingDistance, checkFaceOcclusion, checkFaceOcclusionRaw, computeSimHash, createCalibratedQuantizer, createLivenessData, evaluateCaptureQuality, extractRawFacialData, generateBiometricSimHash, generateHyperplanes, generateRandomSalt, getFaceBox, getFaceLuminance, getHyperplanes, getRegionLuminance, hammingDistance, normalizeEmbedding, processFacialDataForAleo, similarityFromDistance, useCameraStream, useVisionModels, validateDataQuality };
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  // src/core/biometrics/simHash.ts
2
2
  var SEED_PREFIX = "HumanityGen_SimHash_Hyperplane_";
3
+ var EMBEDDING_DIMENSIONS = 1024;
4
+ var SIGNATURE_BITS = 256;
3
5
  function generateHyperplanes(dimensions, count) {
4
6
  const hyperplanes = [];
5
7
  for (let i = 0; i < count; i++) {
@@ -34,12 +36,24 @@ function simpleHash(str) {
34
36
  }
35
37
  function computeSimHash(embedding, hyperplanes) {
36
38
  if (embedding.length === 0) return "";
37
- const dims = Math.min(embedding.length, hyperplanes[0].length);
39
+ const hyperplaneDim = hyperplanes[0].length;
40
+ let workingEmbedding = embedding;
41
+ if (embedding.length !== hyperplaneDim) {
42
+ console.warn(
43
+ `\u26A0\uFE0F SimHash dimension mismatch: embedding has ${embedding.length} dims, hyperplanes expect ${hyperplaneDim}. ` + (embedding.length < hyperplaneDim ? `Zero-padding embedding with ${hyperplaneDim - embedding.length} extra dims.` : `Truncating embedding to ${hyperplaneDim} dims.`)
44
+ );
45
+ if (embedding.length < hyperplaneDim) {
46
+ workingEmbedding = [...embedding, ...new Array(hyperplaneDim - embedding.length).fill(0)];
47
+ } else {
48
+ workingEmbedding = embedding.slice(0, hyperplaneDim);
49
+ }
50
+ }
51
+ const dims = hyperplaneDim;
38
52
  let signatureBytes = new Uint8Array(hyperplanes.length / 8);
39
53
  for (let i = 0; i < hyperplanes.length; i++) {
40
54
  let dotProduct = 0;
41
55
  for (let j = 0; j < dims; j++) {
42
- dotProduct += embedding[j] * hyperplanes[i][j];
56
+ dotProduct += workingEmbedding[j] * hyperplanes[i][j];
43
57
  }
44
58
  if (dotProduct > 0) {
45
59
  const byteIndex = Math.floor(i / 8);
@@ -72,7 +86,7 @@ function hammingDistance(hex1, hex2) {
72
86
  return distance;
73
87
  }
74
88
  var cachedHyperplanes = null;
75
- function getHyperplanes(dimensions = 1024, count = 256) {
89
+ function getHyperplanes(dimensions = EMBEDDING_DIMENSIONS, count = SIGNATURE_BITS) {
76
90
  if (!cachedHyperplanes || cachedHyperplanes[0].length !== dimensions || cachedHyperplanes.length !== count) {
77
91
  console.time("Generating Hyperplanes");
78
92
  cachedHyperplanes = generateHyperplanes(dimensions, count);
@@ -110,7 +124,7 @@ function averageEmbeddings(embeddings) {
110
124
  return { index, distance: Math.sqrt(sumSq) };
111
125
  });
112
126
  distances.sort((a, b) => a.distance - b.distance);
113
- const keepCount = Math.max(1, Math.floor(count * 0.7));
127
+ const keepCount = Math.max(1, Math.floor(count * 0.85));
114
128
  const bestIndices = new Set(distances.slice(0, keepCount).map((d) => d.index));
115
129
  const finalAvg = new Array(dim).fill(0);
116
130
  for (let i = 0; i < count; i++) {
@@ -123,7 +137,7 @@ function averageEmbeddings(embeddings) {
123
137
  for (let d = 0; d < dim; d++) {
124
138
  finalAvg[d] /= keepCount;
125
139
  }
126
- console.log(`Averaged ${keepCount} clean embeddings (discarded ${count - keepCount} outliers)`);
140
+ console.log(`Averaged ${keepCount}/${count} clean embeddings (discarded ${count - keepCount} outliers)`);
127
141
  return finalAvg;
128
142
  }
129
143
  function safeNumber(value, fallback = 0) {
@@ -507,7 +521,7 @@ function generateBiometricSimHash(face, livenessData, averagedEmbedding) {
507
521
  const rawData = extractRawFacialData(face);
508
522
  const sourceEmbedding = averagedEmbedding && averagedEmbedding.length > 0 ? averagedEmbedding : rawData.faceEmbedding;
509
523
  const normalized = normalizeEmbedding(sourceEmbedding);
510
- const hyperplanes = getHyperplanes(1024, 256);
524
+ const hyperplanes = getHyperplanes(normalized.length || EMBEDDING_DIMENSIONS);
511
525
  const simHashHex = computeSimHash(normalized, hyperplanes);
512
526
  const hasBlink = livenessData.blinkCount >= 1;
513
527
  const hasEmotionTransition = livenessData.emotionTransitions.length > 0;
@@ -1570,10 +1584,16 @@ var FaceZKInner = ({
1570
1584
  }
1571
1585
  return dot / (Math.sqrt(normA) * Math.sqrt(normB));
1572
1586
  };
1573
- const captureMultipleEmbeddings = async (video, sampleCount = 10, intervalMs = 200) => {
1587
+ const captureMultipleEmbeddings = async (video, sampleCount = 10, intervalMs = 200, assertFaceClear) => {
1574
1588
  const embeddings = [];
1575
1589
  console.log(`\u{1F4F8} Starting multi-frame capture: ${sampleCount} samples, ${intervalMs}ms apart`);
1576
1590
  for (let i = 0; i < sampleCount; i++) {
1591
+ if (assertFaceClear) {
1592
+ const status = assertFaceClear();
1593
+ if (!status.aligned) {
1594
+ throw new Error(status.message || "Face obstruction detected during capture");
1595
+ }
1596
+ }
1577
1597
  try {
1578
1598
  const emb = await extractFaceEmbedding(video);
1579
1599
  embeddings.push(emb);
@@ -1815,8 +1835,22 @@ var FaceZKInner = ({
1815
1835
  setLiveFeedback(null);
1816
1836
  const yaw = getYawDegrees(latestMatrix.current);
1817
1837
  if (condition(yaw)) {
1818
- await new Promise((r) => setTimeout(r, 300));
1819
- if (latestMatrix.current && condition(getYawDegrees(latestMatrix.current))) {
1838
+ const holdUntil = Date.now() + 300;
1839
+ let holdBroken = false;
1840
+ while (Date.now() < holdUntil) {
1841
+ if (!latestMatrix.current || !condition(getYawDegrees(latestMatrix.current))) {
1842
+ holdBroken = true;
1843
+ break;
1844
+ }
1845
+ const holdStatus = checkFaceAlignment();
1846
+ if (!holdStatus.aligned) {
1847
+ setLiveFeedback({ message: holdStatus.message, isGood: false });
1848
+ holdBroken = true;
1849
+ break;
1850
+ }
1851
+ await new Promise((r) => setTimeout(r, 50));
1852
+ }
1853
+ if (!holdBroken && latestMatrix.current && condition(getYawDegrees(latestMatrix.current)) && checkFaceAlignment().aligned) {
1820
1854
  return true;
1821
1855
  }
1822
1856
  }
@@ -1846,8 +1880,31 @@ var FaceZKInner = ({
1846
1880
  setCurrentStep("capturing");
1847
1881
  const video = videoRef.current;
1848
1882
  let embedding;
1883
+ const occlusionCanvas = document.createElement("canvas");
1884
+ const checkFaceReadyAndOcclusion = (duringFlash = false) => {
1885
+ const alignment2 = checkFaceAlignment();
1886
+ if (!alignment2.aligned) return alignment2;
1887
+ if (occlusionCanvas.width !== video.videoWidth) {
1888
+ occlusionCanvas.width = video.videoWidth;
1889
+ occlusionCanvas.height = video.videoHeight;
1890
+ }
1891
+ const ctx = occlusionCanvas.getContext("2d", { willReadFrequently: true });
1892
+ if (!ctx) return { aligned: true, message: "Perfect! Hold still." };
1893
+ ctx.drawImage(video, 0, 0, occlusionCanvas.width, occlusionCanvas.height);
1894
+ const occlusion = checkFaceOcclusionRaw(
1895
+ latestLandmarks.current,
1896
+ ctx,
1897
+ occlusionCanvas.width,
1898
+ occlusionCanvas.height,
1899
+ { yawDegrees: 0, duringLivenessFlash: duringFlash }
1900
+ );
1901
+ if (!occlusion.ok) {
1902
+ return { aligned: false, message: occlusion.message ?? "Please remove obstructions from your face." };
1903
+ }
1904
+ return { aligned: true, message: "Perfect! Hold still." };
1905
+ };
1849
1906
  try {
1850
- embedding = await captureMultipleEmbeddings(video, 10, 200);
1907
+ embedding = await captureMultipleEmbeddings(video, 10, 200, () => checkFaceReadyAndOcclusion(false));
1851
1908
  } catch (e) {
1852
1909
  handleFailure(e.message || "Failed to capture face data");
1853
1910
  return;
@@ -1857,13 +1914,16 @@ var FaceZKInner = ({
1857
1914
  handleFailure("Face lost during final check");
1858
1915
  return;
1859
1916
  }
1860
- const alignment = checkFaceAlignment();
1917
+ const alignment = checkFaceReadyAndOcclusion(false);
1861
1918
  if (!alignment.aligned) {
1862
1919
  handleFailure(`Alignment lost: ${alignment.message}`);
1863
1920
  return;
1864
1921
  }
1865
1922
  setCurrentStep("light-flash");
1866
- const livenessResult = await livenessEngine.runLivenessSequence(bbox);
1923
+ const livenessResult = await livenessEngine.runLivenessSequence(
1924
+ bbox,
1925
+ () => checkFaceReadyAndOcclusion(true)
1926
+ );
1867
1927
  if (!livenessResult.passed) {
1868
1928
  handleFailure(livenessResult.message || "Liveness check failed");
1869
1929
  return;
@@ -2963,6 +3023,7 @@ var VerifiedOnchainFlow_default = VerifiedOnchainFlow;
2963
3023
  export {
2964
3024
  BACKEND_PRIORITY,
2965
3025
  CAPTURE_QUALITY,
3026
+ EMBEDDING_DIMENSIONS,
2966
3027
  EmbeddingQuantizer,
2967
3028
  FaceOcclusionTracker,
2968
3029
  FaceZK_default as FaceZK,
@@ -2971,6 +3032,7 @@ export {
2971
3032
  LivenessEngine,
2972
3033
  MaskDetector,
2973
3034
  ProofOfHumanity_default as ProofOfHumanity,
3035
+ SIGNATURE_BITS,
2974
3036
  SocialConnection_default as SocialConnection,
2975
3037
  VERIFICATION_CONSTANTS,
2976
3038
  VerifiedOnchainFlow_default as VerifiedOnchainFlow,