yakmesh 2.9.0 → 3.0.0
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/CHANGELOG.md +637 -0
- package/Caddyfile +77 -0
- package/README.md +119 -29
- package/content/api.js +50 -41
- package/content/index.js +1 -2
- package/content/store.js +323 -177
- package/dashboard/index.html +19 -3
- package/database/replication.js +117 -37
- package/docs/CRYPTO-AGILITY.md +204 -0
- package/docs/MTLS-RESEARCH.md +367 -0
- package/docs/NAMCHE-SPEC.md +681 -0
- package/docs/PEERQUANTA-YAKMESH-INTEGRATION.md +407 -0
- package/docs/PRECISION-DISCLOSURE.md +96 -0
- package/docs/README.md +76 -0
- package/docs/ROADMAP-2.4.0.md +447 -0
- package/docs/ROADMAP-2.5.0.md +244 -0
- package/docs/SECURITY-AUDIT-REPORT.md +306 -0
- package/docs/SST-INTEGRATION.md +712 -0
- package/docs/STEADYWATCH-IMPLEMENTATION.md +303 -0
- package/docs/TERNARY-AUDIT-REPORT.md +247 -0
- package/docs/TME-FAQ.md +221 -0
- package/docs/WHITEPAPER.md +623 -0
- package/docs/adapters.html +1001 -0
- package/docs/advanced-systems.html +1045 -0
- package/docs/annex.html +1046 -0
- package/docs/api.html +970 -0
- package/docs/business/response-templates.md +160 -0
- package/docs/c2c.html +1225 -0
- package/docs/cli.html +1332 -0
- package/docs/configuration.html +1248 -0
- package/docs/darshan.html +1085 -0
- package/docs/dharma.html +966 -0
- package/docs/docs-bundle.html +1075 -0
- package/docs/docs.css +3120 -0
- package/docs/docs.js +556 -0
- package/docs/doko.html +969 -0
- package/docs/geo-proof.html +858 -0
- package/docs/getting-started.html +840 -0
- package/docs/gumba-tutorial.html +1144 -0
- package/docs/gumba.html +1098 -0
- package/docs/index.html +914 -0
- package/docs/jhilke.html +1312 -0
- package/docs/karma.html +1100 -0
- package/docs/katha.html +1037 -0
- package/docs/lama.html +978 -0
- package/docs/mandala.html +1067 -0
- package/docs/mani.html +964 -0
- package/docs/mantra.html +967 -0
- package/docs/mesh.html +1409 -0
- package/docs/nakpak.html +869 -0
- package/docs/namche.html +928 -0
- package/docs/nav-order.json +53 -0
- package/docs/prahari.html +1043 -0
- package/docs/prism-bash.min.js +1 -0
- package/docs/prism-javascript.min.js +1 -0
- package/docs/prism-json.min.js +1 -0
- package/docs/prism-tomorrow.min.css +1 -0
- package/docs/prism.min.js +1 -0
- package/docs/privacy.html +699 -0
- package/docs/quick-reference.html +1181 -0
- package/docs/sakshi.html +1402 -0
- package/docs/sandboxing.md +386 -0
- package/docs/seva.html +911 -0
- package/docs/sherpa.html +871 -0
- package/docs/studio.html +860 -0
- package/docs/stupa.html +995 -0
- package/docs/tailwind.min.css +2 -0
- package/docs/tattva.html +1332 -0
- package/docs/terms.html +686 -0
- package/docs/time-server-deployment.md +166 -0
- package/docs/time-sources.html +1392 -0
- package/docs/tivra.html +1127 -0
- package/docs/trademark-policy.html +686 -0
- package/docs/tribhuj.html +1183 -0
- package/docs/trust-security.html +1029 -0
- package/docs/tutorials/backup-recovery.html +654 -0
- package/docs/tutorials/dashboard.html +604 -0
- package/docs/tutorials/domain-setup.html +605 -0
- package/docs/tutorials/host-website.html +456 -0
- package/docs/tutorials/mesh-network.html +505 -0
- package/docs/tutorials/mobile-access.html +445 -0
- package/docs/tutorials/privacy.html +467 -0
- package/docs/tutorials/raspberry-pi.html +600 -0
- package/docs/tutorials/security-basics.html +539 -0
- package/docs/tutorials/share-files.html +431 -0
- package/docs/tutorials/troubleshooting.html +637 -0
- package/docs/tutorials/trust-karma.html +419 -0
- package/docs/tutorials/yak-protocol.html +456 -0
- package/docs/tutorials.html +1034 -0
- package/docs/vani.html +1270 -0
- package/docs/webserver.html +809 -0
- package/docs/yak-protocol.html +940 -0
- package/docs/yak-timeserver-design.md +475 -0
- package/docs/yakapp.html +1015 -0
- package/docs/ypc27.html +1069 -0
- package/docs/yurt.html +1344 -0
- package/embedded-docs/bundle.js +274 -114
- package/gossip/protocol.js +247 -27
- package/identity/key-resolver.js +262 -0
- package/identity/machine-seed.js +632 -0
- package/identity/node-key.js +669 -368
- package/identity/tribhuj-ratchet.js +506 -0
- package/knowledge-base.js +37 -8
- package/launcher/yakmesh.bat +62 -0
- package/launcher/yakmesh.sh +70 -0
- package/mesh/annex.js +462 -108
- package/mesh/beacon-broadcast.js +4 -1
- package/mesh/darshan.js +17 -5
- package/mesh/gumba.js +47 -13
- package/mesh/jhilke.js +651 -0
- package/mesh/katha.js +5 -2
- package/mesh/nakpak-routing.js +8 -5
- package/mesh/network.js +724 -34
- package/mesh/pulse-sync.js +4 -1
- package/mesh/seva.js +526 -0
- package/mesh/sherpa-discovery.js +89 -8
- package/mesh/sybil-defense.js +19 -5
- package/mesh/temporal-encoder.js +4 -3
- package/mesh/yurt.js +72 -17
- package/models/entropy-sentinel.onnx +0 -0
- package/models/karma-trust.onnx +0 -0
- package/models/manifest.json +43 -0
- package/models/sakshi-anomaly.onnx +0 -0
- package/oracle/code-proof-protocol.js +7 -6
- package/oracle/codebase-lock.js +257 -28
- package/oracle/index.js +74 -15
- package/oracle/ma902-snmp.js +678 -0
- package/oracle/module-sealer.js +5 -3
- package/oracle/packet-checksum.js +201 -0
- package/oracle/ternary-144t.js +714 -0
- package/oracle/ternary-ml.js +481 -0
- package/oracle/time-api.js +239 -0
- package/oracle/time-source.js +137 -47
- package/oracle/validation-oracle-hardened.js +1111 -1071
- package/oracle/validation-oracle.js +4 -2
- package/oracle/ypc27.js +211 -0
- package/package.json +20 -3
- package/protocol/yak-handler.js +35 -9
- package/protocol/yak-protocol.js +6 -5
- package/reference/cpp/yakmesh_mceliece_shard.cpp +168 -0
- package/reference/cpp/yakmesh_ypc27.cpp +179 -0
- package/sbom.json +87 -0
- package/scripts/security-audit.mjs +264 -0
- package/scripts/update-docs-sidebar.cjs +164 -0
- package/security/crypto-config.js +4 -3
- package/security/dharma-moderation.js +4 -3
- package/security/doko-identity.js +193 -143
- package/security/domain-consensus.js +86 -85
- package/security/fs-hardening.js +620 -0
- package/security/hardware-attestation.js +5 -3
- package/security/hybrid-trust.js +227 -87
- package/security/karma-rate-limiter.js +692 -0
- package/security/khata-protocol.js +22 -21
- package/security/khata-trust-integration.js +277 -150
- package/security/memory-safety.js +635 -0
- package/security/mesh-auth.js +11 -10
- package/security/mesh-revocation.js +18 -5
- package/security/namche-gateway.js +298 -69
- package/security/sakshi.js +102 -3
- package/security/sangha.js +770 -0
- package/security/secure-config.js +473 -0
- package/security/silicon-parity.js +13 -10
- package/security/steadywatch.js +1142 -0
- package/security/strike-system.js +32 -3
- package/security/temporal-signing.js +488 -0
- package/security/trit-commitment.js +464 -0
- package/server/crypto/annex.js +247 -0
- package/server/darshan-api.js +343 -0
- package/server/index.js +3259 -362
- package/server/komm-api.js +668 -0
- package/utils/accel.js +2273 -0
- package/utils/ternary-id.js +79 -0
- package/utils/verify-worker.js +57 -0
- package/webserver/index.js +95 -5
- package/assets/yakmesh-logo.png +0 -0
- package/assets/yakmesh-logo.svg +0 -80
- package/assets/yakmesh-logo2.png +0 -0
- package/assets/yakmesh-logo2sm.png +0 -0
- package/assets/ymsm.png +0 -0
- package/scripts/update-docs-nav.cjs +0 -194
- package/update-docs-nav.cjs +0 -18
- package/update-nav.ps1 +0 -16
- package/website/assets/silhouettes/adapters.svg +0 -107
- package/website/assets/silhouettes/api-endpoints.svg +0 -115
- package/website/assets/silhouettes/atomic-clock.svg +0 -83
- package/website/assets/silhouettes/base-camp.svg +0 -81
- package/website/assets/silhouettes/bridge.svg +0 -69
- package/website/assets/silhouettes/docs-bundle.svg +0 -113
- package/website/assets/silhouettes/doko-basket.svg +0 -70
- package/website/assets/silhouettes/fortress.svg +0 -93
- package/website/assets/silhouettes/gateway.svg +0 -54
- package/website/assets/silhouettes/gears.svg +0 -93
- package/website/assets/silhouettes/globe-satellite.svg +0 -67
- package/website/assets/silhouettes/karma-wheel.svg +0 -137
- package/website/assets/silhouettes/lama-council.svg +0 -141
- package/website/assets/silhouettes/mandala-network.svg +0 -169
- package/website/assets/silhouettes/mani-stones.svg +0 -149
- package/website/assets/silhouettes/mantra-wheel.svg +0 -116
- package/website/assets/silhouettes/mesh-nodes.svg +0 -113
- package/website/assets/silhouettes/nakpak.svg +0 -56
- package/website/assets/silhouettes/peak-lightning.svg +0 -73
- package/website/assets/silhouettes/sherpa.svg +0 -69
- package/website/assets/silhouettes/stupa-tower.svg +0 -119
- package/website/assets/silhouettes/tattva-eye.svg +0 -78
- package/website/assets/silhouettes/terminal.svg +0 -74
- package/website/assets/silhouettes/webserver.svg +0 -145
- package/website/assets/silhouettes/yak.svg +0 -78
- package/website/assets/yakmesh-logo.png +0 -0
- package/website/assets/yakmesh-logo.webp +0 -0
- package/website/assets/yakmesh-logo128x140.webp +0 -0
- package/website/assets/yakmesh-logo2.png +0 -0
- package/website/assets/yakmesh-logo2.svg +0 -51
- package/website/assets/yakmesh-logo40x44.webp +0 -0
- package/website/assets/yakmesh.gif +0 -0
- package/website/assets/yakmesh.ico +0 -0
- package/website/assets/yakmesh.jpg +0 -0
- package/website/assets/yakmesh.pdf +0 -0
- package/website/assets/yakmesh.png +0 -0
- package/website/assets/yakmesh.svg +0 -70
- package/website/assets/yakmesh128.webp +0 -0
- package/website/assets/yakmesh32.png +0 -0
- package/website/assets/yakmesh32.svg +0 -65
- package/website/assets/yakmesh32o.ico +0 -2
- package/website/assets/yakmesh32o.svg +0 -65
- package/website/assets/yakmesh32o.svgz +0 -0
|
@@ -11,12 +11,18 @@
|
|
|
11
11
|
* @version 1.0.0
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import { sha3_256 } from '@noble/hashes/sha3.js';
|
|
14
|
+
import { sha3_256 as _nobleSha3 } from '@noble/hashes/sha3.js';
|
|
15
15
|
import { bytesToHex, hexToBytes } from '@noble/hashes/utils.js';
|
|
16
16
|
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
|
|
17
|
+
// ACCEL: Hardware-accelerated crypto
|
|
18
|
+
import { sha3_256, mlDsa65Sign, mlDsa65Verify } from '../utils/accel.js';
|
|
17
19
|
import { EventEmitter } from 'events';
|
|
18
20
|
import { createLogger } from '../utils/logger.js';
|
|
19
21
|
|
|
22
|
+
// ═══ TRIBHUJ — Balanced ternary for revocation status ═══
|
|
23
|
+
// POSITIVE: retained (not revoked), NEUTRAL: forming (insufficient network), NEGATIVE: revoked
|
|
24
|
+
import { POSITIVE, NEUTRAL, NEGATIVE } from '../oracle/tribhuj.js';
|
|
25
|
+
|
|
20
26
|
const log = createLogger('security:mesh-revocation');
|
|
21
27
|
|
|
22
28
|
/**
|
|
@@ -85,7 +91,7 @@ export class Attestation {
|
|
|
85
91
|
*/
|
|
86
92
|
sign(privateKey) {
|
|
87
93
|
const bytes = this.getSignableBytes();
|
|
88
|
-
const sig =
|
|
94
|
+
const sig = mlDsa65Sign(bytes, privateKey);
|
|
89
95
|
this.signature = bytesToHex(sig);
|
|
90
96
|
return this;
|
|
91
97
|
}
|
|
@@ -100,7 +106,7 @@ export class Attestation {
|
|
|
100
106
|
const bytes = this.getSignableBytes();
|
|
101
107
|
const sig = hexToBytes(this.signature);
|
|
102
108
|
const pubKey = typeof publicKey === 'string' ? hexToBytes(publicKey) : publicKey;
|
|
103
|
-
return
|
|
109
|
+
return mlDsa65Verify(sig, bytes, pubKey);
|
|
104
110
|
} catch (e) {
|
|
105
111
|
return false;
|
|
106
112
|
}
|
|
@@ -220,11 +226,16 @@ export class RevocationState {
|
|
|
220
226
|
|
|
221
227
|
/**
|
|
222
228
|
* Check if threshold is met
|
|
223
|
-
* This is THE mathematical determination
|
|
229
|
+
* This is THE mathematical determination.
|
|
230
|
+
*
|
|
231
|
+
* Returns `revoked` boolean (backward compat) plus `status` trit:
|
|
232
|
+
* POSITIVE (+1): retained — below threshold, DOKO is valid
|
|
233
|
+
* NEUTRAL ( 0): forming — insufficient network to determine
|
|
234
|
+
* NEGATIVE (-1): revoked — threshold met, DOKO is revoked
|
|
224
235
|
*/
|
|
225
236
|
isRevoked(activeNodeCount, config = DEFAULT_CONFIG) {
|
|
226
237
|
if (activeNodeCount < config.minNodes) {
|
|
227
|
-
return { revoked: false, reason: 'INSUFFICIENT_NETWORK' };
|
|
238
|
+
return { revoked: false, status: NEUTRAL, reason: 'INSUFFICIENT_NETWORK' };
|
|
228
239
|
}
|
|
229
240
|
|
|
230
241
|
const threshold = Math.ceil(activeNodeCount * config.threshold);
|
|
@@ -233,6 +244,7 @@ export class RevocationState {
|
|
|
233
244
|
if (count >= threshold) {
|
|
234
245
|
return {
|
|
235
246
|
revoked: true,
|
|
247
|
+
status: NEGATIVE,
|
|
236
248
|
reason: this.primaryReason,
|
|
237
249
|
attestationCount: count,
|
|
238
250
|
threshold,
|
|
@@ -243,6 +255,7 @@ export class RevocationState {
|
|
|
243
255
|
|
|
244
256
|
return {
|
|
245
257
|
revoked: false,
|
|
258
|
+
status: POSITIVE,
|
|
246
259
|
reason: 'BELOW_THRESHOLD',
|
|
247
260
|
attestationCount: count,
|
|
248
261
|
threshold,
|
|
@@ -5,30 +5,38 @@
|
|
|
5
5
|
* "Math as Authority - No human in the loop, no human weakness."
|
|
6
6
|
*
|
|
7
7
|
* ═══════════════════════════════════════════════════════════════════════════════
|
|
8
|
-
* ⚠️ SECURITY: This module implements the
|
|
8
|
+
* ⚠️ SECURITY: This module implements the 8-step verification flow.
|
|
9
9
|
* All trust decisions are mathematical computations - no exceptions.
|
|
10
|
+
* Defense-in-depth: NIST (Gate 2) + 144T (Gate 8) must BOTH verify.
|
|
10
11
|
* ═══════════════════════════════════════════════════════════════════════════════
|
|
11
12
|
*
|
|
12
|
-
* The
|
|
13
|
-
* 1. STRUCTURE_OK
|
|
14
|
-
* 2. SIGNATURE_OK
|
|
15
|
-
* 3. NODEID_OK
|
|
16
|
-
* 4. TEMPORAL_OK
|
|
17
|
-
* 5. NETWORK_OK
|
|
18
|
-
* 6. NOT_REVOKED
|
|
19
|
-
* 7. DOMAINS_OK
|
|
13
|
+
* The 8 Gates of Verification:
|
|
14
|
+
* 1. STRUCTURE_OK - Valid DOKO format
|
|
15
|
+
* 2. SIGNATURE_OK - ML-DSA-65 signature verifies (NIST)
|
|
16
|
+
* 3. NODEID_OK - NodeID matches two-part derivation (network + instance)
|
|
17
|
+
* 4. TEMPORAL_OK - Not expired, not from future
|
|
18
|
+
* 5. NETWORK_OK - Correct network name
|
|
19
|
+
* 6. NOT_REVOKED - Not in revocation log
|
|
20
|
+
* 7. DOMAINS_OK - Quorum verified domain claims (if applicable)
|
|
21
|
+
* 8. TRIT_COMMITMENT_OK - 144T backbone verification (if present)
|
|
20
22
|
*
|
|
21
23
|
* @module security/namche-gateway
|
|
22
24
|
* @version 1.0.0
|
|
23
25
|
*/
|
|
24
26
|
|
|
25
27
|
import { ml_dsa65 } from '@noble/post-quantum/ml-dsa.js';
|
|
26
|
-
import { sha3_256 } from '@noble/hashes/sha3.js';
|
|
28
|
+
import { sha3_256 as _nobleSha3 } from '@noble/hashes/sha3.js';
|
|
27
29
|
import { bytesToHex, hexToBytes, utf8ToBytes } from '@noble/hashes/utils.js';
|
|
30
|
+
// ACCEL: Hardware-accelerated crypto
|
|
31
|
+
import { sha3_256, mlDsa65Verify } from '../utils/accel.js';
|
|
28
32
|
import { EventEmitter } from 'events';
|
|
29
|
-
import { generateNodeId, getCodebaseHash } from '../identity/node-key.js';
|
|
33
|
+
import { generateNodeId, getCodebaseHash, signMessage } from '../identity/node-key.js';
|
|
30
34
|
import { deriveNetworkName } from '../oracle/network-identity.js';
|
|
31
35
|
import { createLogger } from '../utils/logger.js';
|
|
36
|
+
// SAKSHI: Observational verification & revocation agreement
|
|
37
|
+
import { NodeWitness, checkMathematicalAgreement, checkRevocationAgreement } from './sakshi.js';
|
|
38
|
+
// 144T: Ternary backbone security (defense-in-depth with NIST)
|
|
39
|
+
import { TritCommitment } from './trit-commitment.js';
|
|
32
40
|
|
|
33
41
|
const log = createLogger('security:namche');
|
|
34
42
|
|
|
@@ -71,6 +79,7 @@ export const VERIFY_RESULT = {
|
|
|
71
79
|
REVOKED: 'REVOKED',
|
|
72
80
|
DOMAIN_VERIFICATION_FAILED: 'DOMAIN_VERIFICATION_FAILED',
|
|
73
81
|
INSUFFICIENT_QUORUM: 'INSUFFICIENT_QUORUM',
|
|
82
|
+
TRIT_COMMITMENT_INVALID: 'TRIT_COMMITMENT_INVALID',
|
|
74
83
|
};
|
|
75
84
|
|
|
76
85
|
/**
|
|
@@ -187,7 +196,8 @@ export class NamcheGateway extends EventEmitter {
|
|
|
187
196
|
this.dokoCache = new LRUCache(this.config.dokoCacheSize);
|
|
188
197
|
this.revocationLog = new RevocationLog();
|
|
189
198
|
this.verifiedDomains = new Map(); // domain -> doko that verified it
|
|
190
|
-
|
|
199
|
+
this.revocationReports = new Map(); // dokoHash -> Array of { witness, evidence }
|
|
200
|
+
|
|
191
201
|
this.stats = {
|
|
192
202
|
verificationsAttempted: 0,
|
|
193
203
|
verificationsSucceeded: 0,
|
|
@@ -312,11 +322,29 @@ export class NamcheGateway extends EventEmitter {
|
|
|
312
322
|
checks.push('DOMAINS_OK');
|
|
313
323
|
}
|
|
314
324
|
|
|
325
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
326
|
+
// GATE 8: 144T COMMITMENT (if present — defense-in-depth)
|
|
327
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
328
|
+
// The 144T commitment provides a second cryptographic layer independent
|
|
329
|
+
// of NIST. Both ML-DSA-65 (Gate 2) AND 144T must verify for full trust.
|
|
330
|
+
// This means an attacker must break BOTH NIST and lattice-hard SIS.
|
|
331
|
+
if (doko.tritCommitment) {
|
|
332
|
+
const tritResult = this.checkTritCommitment(doko);
|
|
333
|
+
if (!tritResult.valid) {
|
|
334
|
+
return this.fail(
|
|
335
|
+
VERIFY_RESULT.TRIT_COMMITMENT_INVALID,
|
|
336
|
+
tritResult.detail,
|
|
337
|
+
{ tritChecks: tritResult.checks }
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
checks.push('TRIT_COMMITMENT_OK');
|
|
341
|
+
}
|
|
342
|
+
|
|
315
343
|
// ═══════════════════════════════════════════════════════════════════════
|
|
316
|
-
// ALL
|
|
344
|
+
// ALL 8 GATES PASSED - MATHEMATICALLY VERIFIED (DUAL-LAYER)
|
|
317
345
|
// ═══════════════════════════════════════════════════════════════════════
|
|
318
346
|
this.stats.verificationsSucceeded++;
|
|
319
|
-
|
|
347
|
+
|
|
320
348
|
// Cache the verified DOKO
|
|
321
349
|
this.dokoCache.set(dokoHash, {
|
|
322
350
|
doko,
|
|
@@ -362,7 +390,7 @@ export class NamcheGateway extends EventEmitter {
|
|
|
362
390
|
*/
|
|
363
391
|
checkStructure(doko) {
|
|
364
392
|
const required = ['version', 'type', 'nodeId', 'publicKey', 'issuedAt', 'expiresAt', 'signature'];
|
|
365
|
-
|
|
393
|
+
|
|
366
394
|
for (const field of required) {
|
|
367
395
|
if (!(field in doko)) {
|
|
368
396
|
return { valid: false, detail: `Missing required field: ${field}` };
|
|
@@ -394,12 +422,12 @@ export class NamcheGateway extends EventEmitter {
|
|
|
394
422
|
const publicKey = hexToBytes(doko.publicKey);
|
|
395
423
|
const signature = hexToBytes(doko.signature);
|
|
396
424
|
|
|
397
|
-
const valid =
|
|
398
|
-
|
|
425
|
+
const valid = mlDsa65Verify(signature, payloadBytes, publicKey);
|
|
426
|
+
|
|
399
427
|
if (!valid) {
|
|
400
428
|
return { valid: false, detail: 'ML-DSA-65 signature verification failed' };
|
|
401
429
|
}
|
|
402
|
-
|
|
430
|
+
|
|
403
431
|
return { valid: true };
|
|
404
432
|
} catch (error) {
|
|
405
433
|
return { valid: false, detail: `Signature check error: ${error.message}` };
|
|
@@ -419,35 +447,35 @@ export class NamcheGateway extends EventEmitter {
|
|
|
419
447
|
try {
|
|
420
448
|
// The nodeId must follow the format: node-[networkName]-[instanceId]
|
|
421
449
|
if (!doko.nodeId.startsWith('node-')) {
|
|
422
|
-
return {
|
|
423
|
-
valid: false,
|
|
450
|
+
return {
|
|
451
|
+
valid: false,
|
|
424
452
|
reason: VERIFY_RESULT.NODEID_MISMATCH,
|
|
425
|
-
detail: 'NodeID must start with "node-"'
|
|
453
|
+
detail: 'NodeID must start with "node-"'
|
|
426
454
|
};
|
|
427
455
|
}
|
|
428
456
|
|
|
429
457
|
// Extract the network name from the nodeId
|
|
430
458
|
const parts = doko.nodeId.split('-');
|
|
431
459
|
if (parts.length < 3) {
|
|
432
|
-
return {
|
|
433
|
-
valid: false,
|
|
460
|
+
return {
|
|
461
|
+
valid: false,
|
|
434
462
|
reason: VERIFY_RESULT.NODEID_MISMATCH,
|
|
435
|
-
detail: 'NodeID must have format node-[networkName]-[instanceId]'
|
|
463
|
+
detail: 'NodeID must have format node-[networkName]-[instanceId]'
|
|
436
464
|
};
|
|
437
465
|
}
|
|
438
466
|
|
|
439
467
|
// Verify the network portion matches our expected network
|
|
440
468
|
const ourNetworkName = this.getNetworkName();
|
|
441
|
-
|
|
469
|
+
|
|
442
470
|
// The network name could be multiple words (e.g., "qubit-lattice-prism")
|
|
443
471
|
// We need to check if the nodeId contains our network name after "node-"
|
|
444
472
|
const nodeIdWithoutPrefix = doko.nodeId.substring(5); // Remove "node-"
|
|
445
|
-
|
|
473
|
+
|
|
446
474
|
if (!nodeIdWithoutPrefix.startsWith(ourNetworkName + '-')) {
|
|
447
|
-
return {
|
|
448
|
-
valid: false,
|
|
475
|
+
return {
|
|
476
|
+
valid: false,
|
|
449
477
|
reason: VERIFY_RESULT.WRONG_NETWORK_IN_NODEID,
|
|
450
|
-
detail: `NodeID network mismatch. Expected: ${ourNetworkName}, Got: ${nodeIdWithoutPrefix.split('-').slice(0, -1).join('-')}`
|
|
478
|
+
detail: `NodeID network mismatch. Expected: ${ourNetworkName}, Got: ${nodeIdWithoutPrefix.split('-').slice(0, -1).join('-')}`
|
|
451
479
|
};
|
|
452
480
|
}
|
|
453
481
|
|
|
@@ -461,24 +489,24 @@ export class NamcheGateway extends EventEmitter {
|
|
|
461
489
|
// Full verification: regenerate the expected nodeId from the publicKey
|
|
462
490
|
const publicKeyBytes = hexToBytes(doko.publicKey);
|
|
463
491
|
const expectedNodeId = generateNodeId(publicKeyBytes, codebaseHash);
|
|
464
|
-
|
|
492
|
+
|
|
465
493
|
if (doko.nodeId !== expectedNodeId) {
|
|
466
|
-
return {
|
|
467
|
-
valid: false,
|
|
494
|
+
return {
|
|
495
|
+
valid: false,
|
|
468
496
|
reason: VERIFY_RESULT.NODEID_MISMATCH,
|
|
469
|
-
detail: `NodeID does not match public key derivation. Expected: ${expectedNodeId}, Got: ${doko.nodeId}`
|
|
497
|
+
detail: `NodeID does not match public key derivation. Expected: ${expectedNodeId}, Got: ${doko.nodeId}`
|
|
470
498
|
};
|
|
471
499
|
}
|
|
472
500
|
}
|
|
473
501
|
// If no codebase hash, we can only verify structure (cross-network scenario)
|
|
474
502
|
// This is a known limitation documented in SECURITY.md
|
|
475
|
-
|
|
503
|
+
|
|
476
504
|
return { valid: true };
|
|
477
505
|
} catch (error) {
|
|
478
|
-
return {
|
|
479
|
-
valid: false,
|
|
506
|
+
return {
|
|
507
|
+
valid: false,
|
|
480
508
|
reason: VERIFY_RESULT.NODEID_MISMATCH,
|
|
481
|
-
detail: `NodeID check error: ${error.message}`
|
|
509
|
+
detail: `NodeID check error: ${error.message}`
|
|
482
510
|
};
|
|
483
511
|
}
|
|
484
512
|
}
|
|
@@ -492,19 +520,19 @@ export class NamcheGateway extends EventEmitter {
|
|
|
492
520
|
|
|
493
521
|
// Check not issued in the future (with clock skew allowance)
|
|
494
522
|
if (doko.issuedAt > now + maxSkew) {
|
|
495
|
-
return {
|
|
496
|
-
valid: false,
|
|
523
|
+
return {
|
|
524
|
+
valid: false,
|
|
497
525
|
reason: VERIFY_RESULT.ISSUED_IN_FUTURE,
|
|
498
|
-
detail: `DOKO issued in future: ${new Date(doko.issuedAt).toISOString()}`
|
|
526
|
+
detail: `DOKO issued in future: ${new Date(doko.issuedAt).toISOString()}`
|
|
499
527
|
};
|
|
500
528
|
}
|
|
501
529
|
|
|
502
530
|
// Check not expired
|
|
503
531
|
if (doko.expiresAt < now) {
|
|
504
|
-
return {
|
|
505
|
-
valid: false,
|
|
532
|
+
return {
|
|
533
|
+
valid: false,
|
|
506
534
|
reason: VERIFY_RESULT.EXPIRED,
|
|
507
|
-
detail: `DOKO expired at: ${new Date(doko.expiresAt).toISOString()}`
|
|
535
|
+
detail: `DOKO expired at: ${new Date(doko.expiresAt).toISOString()}`
|
|
508
536
|
};
|
|
509
537
|
}
|
|
510
538
|
|
|
@@ -516,9 +544,9 @@ export class NamcheGateway extends EventEmitter {
|
|
|
516
544
|
*/
|
|
517
545
|
checkNetwork(doko) {
|
|
518
546
|
if (doko.networkName && doko.networkName !== this.networkName) {
|
|
519
|
-
return {
|
|
520
|
-
valid: false,
|
|
521
|
-
detail: `Network mismatch. Expected: ${this.networkName}, Got: ${doko.networkName}`
|
|
547
|
+
return {
|
|
548
|
+
valid: false,
|
|
549
|
+
detail: `Network mismatch. Expected: ${this.networkName}, Got: ${doko.networkName}`
|
|
522
550
|
};
|
|
523
551
|
}
|
|
524
552
|
return { valid: true };
|
|
@@ -530,9 +558,9 @@ export class NamcheGateway extends EventEmitter {
|
|
|
530
558
|
checkRevocation(dokoHash) {
|
|
531
559
|
if (this.revocationLog.contains(dokoHash)) {
|
|
532
560
|
const revocation = this.revocationLog.get(dokoHash);
|
|
533
|
-
return {
|
|
534
|
-
valid: false,
|
|
535
|
-
detail: `DOKO revoked at ${new Date(revocation.revokedAt).toISOString()}, reason: ${revocation.reason}`
|
|
561
|
+
return {
|
|
562
|
+
valid: false,
|
|
563
|
+
detail: `DOKO revoked at ${new Date(revocation.revokedAt).toISOString()}, reason: ${revocation.reason}`
|
|
536
564
|
};
|
|
537
565
|
}
|
|
538
566
|
return { valid: true };
|
|
@@ -546,12 +574,12 @@ export class NamcheGateway extends EventEmitter {
|
|
|
546
574
|
|
|
547
575
|
for (const domain of doko.domains) {
|
|
548
576
|
const validProofs = await this.verifyDomainProofs(domain);
|
|
549
|
-
|
|
577
|
+
|
|
550
578
|
if (validProofs < quorum) {
|
|
551
|
-
return {
|
|
552
|
-
valid: false,
|
|
579
|
+
return {
|
|
580
|
+
valid: false,
|
|
553
581
|
domain: domain.name,
|
|
554
|
-
detail: `Insufficient quorum for ${domain.name}: have ${validProofs}, need ${quorum}`
|
|
582
|
+
detail: `Insufficient quorum for ${domain.name}: have ${validProofs}, need ${quorum}`
|
|
555
583
|
};
|
|
556
584
|
}
|
|
557
585
|
}
|
|
@@ -559,8 +587,61 @@ export class NamcheGateway extends EventEmitter {
|
|
|
559
587
|
return { valid: true };
|
|
560
588
|
}
|
|
561
589
|
|
|
590
|
+
/**
|
|
591
|
+
* GATE 8: Check 144T commitment (backbone verification)
|
|
592
|
+
*
|
|
593
|
+
* This gate provides DEFENSE-IN-DEPTH alongside NIST (Gate 2).
|
|
594
|
+
* The 144T commitment uses YPC-27 (lattice-hard SIS problem) and
|
|
595
|
+
* polynomial binding to ensure the payload is tied to the sender's
|
|
596
|
+
* 144T mesh address.
|
|
597
|
+
*
|
|
598
|
+
* Security properties:
|
|
599
|
+
* - YPC-27 operates in ring Z[x]/(x^27-1) mod 3
|
|
600
|
+
* - Forging requires solving the Shortest Vector Problem
|
|
601
|
+
* - Independent of NIST — if NIST is backdoored, 144T still holds
|
|
602
|
+
* - Both layers must be broken to compromise a message
|
|
603
|
+
*
|
|
604
|
+
* @param {Object} doko — DOKO with tritCommitment field
|
|
605
|
+
* @returns {Object} — { valid, detail?, checks? }
|
|
606
|
+
*/
|
|
607
|
+
checkTritCommitment(doko) {
|
|
608
|
+
// Get the payload (everything except signature and tritCommitment)
|
|
609
|
+
const { signature, tritCommitment, ...payloadFields } = doko;
|
|
610
|
+
|
|
611
|
+
if (!TritCommitment.isValidStructure(tritCommitment)) {
|
|
612
|
+
return {
|
|
613
|
+
valid: false,
|
|
614
|
+
detail: 'Invalid tritCommitment structure',
|
|
615
|
+
checks: [],
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Verify the 144T commitment against the DOKO payload
|
|
620
|
+
const result = TritCommitment.verify(payloadFields, tritCommitment);
|
|
621
|
+
|
|
622
|
+
if (!result.valid) {
|
|
623
|
+
log.warn('144T commitment verification FAILED', {
|
|
624
|
+
reason: result.reason,
|
|
625
|
+
detail: result.detail,
|
|
626
|
+
checks: result.checks,
|
|
627
|
+
});
|
|
628
|
+
return {
|
|
629
|
+
valid: false,
|
|
630
|
+
detail: `144T: ${result.reason} — ${result.detail || 'verification failed'}`,
|
|
631
|
+
checks: result.checks,
|
|
632
|
+
};
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
log.debug('144T commitment verified (defense-in-depth active)', {
|
|
636
|
+
checks: result.checks,
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
return { valid: true, checks: result.checks };
|
|
640
|
+
}
|
|
641
|
+
|
|
562
642
|
/**
|
|
563
643
|
* Verify domain proofs from multiple verifiers
|
|
644
|
+
* Uses SAKSHI observational verification — mathematical agreement, not voting
|
|
564
645
|
*/
|
|
565
646
|
async verifyDomainProofs(domainClaim) {
|
|
566
647
|
let validProofs = 0;
|
|
@@ -569,6 +650,9 @@ export class NamcheGateway extends EventEmitter {
|
|
|
569
650
|
return 0;
|
|
570
651
|
}
|
|
571
652
|
|
|
653
|
+
// Collect SAKSHI observations from each proof verifier
|
|
654
|
+
const observations = [];
|
|
655
|
+
|
|
572
656
|
for (const proof of domainClaim.proofs) {
|
|
573
657
|
try {
|
|
574
658
|
// Verify the verifier's signature on the beacon hash
|
|
@@ -582,13 +666,18 @@ export class NamcheGateway extends EventEmitter {
|
|
|
582
666
|
const publicKey = hexToBytes(proof.verifierPublicKey);
|
|
583
667
|
const signature = hexToBytes(proof.signature);
|
|
584
668
|
|
|
585
|
-
const proofValid =
|
|
669
|
+
const proofValid = mlDsa65Verify(signature, payloadBytes, publicKey);
|
|
670
|
+
|
|
671
|
+
// SAKSHI: Record each verifier's observation
|
|
672
|
+
const witness = new NodeWitness({
|
|
673
|
+
nodeId: proof.verifierNodeId || bytesToHex(publicKey.slice(0, 16)),
|
|
674
|
+
});
|
|
675
|
+
observations.push({
|
|
676
|
+
witness,
|
|
677
|
+
value: proofValid ? proof.beaconHash : 'INVALID_SIGNATURE',
|
|
678
|
+
});
|
|
586
679
|
|
|
587
680
|
if (proofValid) {
|
|
588
|
-
// TODO: Integrate with SAKSHI observational verification
|
|
589
|
-
// SAKSHI philosophy: Binary verification (signature valid or not),
|
|
590
|
-
// NOT tier-weighted voting (SIRDAR counts more than PATHIK)
|
|
591
|
-
// For now, we accept any valid signature
|
|
592
681
|
validProofs++;
|
|
593
682
|
}
|
|
594
683
|
} catch (error) {
|
|
@@ -597,6 +686,26 @@ export class NamcheGateway extends EventEmitter {
|
|
|
597
686
|
}
|
|
598
687
|
}
|
|
599
688
|
|
|
689
|
+
// SAKSHI: Check mathematical agreement across all proof observations
|
|
690
|
+
if (observations.length > 0) {
|
|
691
|
+
const agreement = checkMathematicalAgreement(observations);
|
|
692
|
+
if (agreement.isAgreed) {
|
|
693
|
+
log.debug('SAKSHI: Domain proofs agree mathematically', {
|
|
694
|
+
domain: domainClaim.name,
|
|
695
|
+
proofCount: validProofs,
|
|
696
|
+
confidence: agreement.confidence,
|
|
697
|
+
});
|
|
698
|
+
} else if (agreement.isDisagreed) {
|
|
699
|
+
log.warn('SAKSHI: Domain proof disagreement — flagging for recomputation', {
|
|
700
|
+
domain: domainClaim.name,
|
|
701
|
+
reason: agreement.reason,
|
|
702
|
+
action: agreement.data?.action,
|
|
703
|
+
});
|
|
704
|
+
// Disagreement means proofs don't agree — return 0 to fail quorum
|
|
705
|
+
return 0;
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
|
|
600
709
|
return validProofs;
|
|
601
710
|
}
|
|
602
711
|
|
|
@@ -610,16 +719,26 @@ export class NamcheGateway extends EventEmitter {
|
|
|
610
719
|
* @param {Object} originalDoko - The DOKO being revoked (for verification)
|
|
611
720
|
*/
|
|
612
721
|
async processRevocation(revocation, originalDoko) {
|
|
613
|
-
//
|
|
614
|
-
if (revocation.revokedBy
|
|
615
|
-
|
|
616
|
-
// TODO: Integrate SAKSHI checkRevocationAgreement() for mesh revocation
|
|
617
|
-
// SAKSHI uses mathematical agreement (do nodes agree on what happened?)
|
|
618
|
-
// NOT tier-weighted voting (SIRDAR's revocation counts 2x)
|
|
619
|
-
return { success: false, reason: 'Only DOKO owner can revoke' };
|
|
722
|
+
// Owner can always revoke directly (self-revocation)
|
|
723
|
+
if (revocation.revokedBy === originalDoko.nodeId) {
|
|
724
|
+
return this._processOwnerRevocation(revocation, originalDoko);
|
|
620
725
|
}
|
|
621
726
|
|
|
622
|
-
//
|
|
727
|
+
// Non-owner: treat as a revocation REPORT for SAKSHI consensus
|
|
728
|
+
// SAKSHI uses mathematical agreement (do nodes agree on what happened?)
|
|
729
|
+
// NOT tier-weighted voting (SIRDAR's revocation counts 2x)
|
|
730
|
+
return this.processRevocationReport({
|
|
731
|
+
reportedBy: revocation.revokedBy,
|
|
732
|
+
reason: revocation.reason,
|
|
733
|
+
timestamp: revocation.revokedAt,
|
|
734
|
+
}, revocation.dokoHash);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Process self-revocation from the DOKO owner
|
|
739
|
+
* @private
|
|
740
|
+
*/
|
|
741
|
+
async _processOwnerRevocation(revocation, originalDoko) {
|
|
623
742
|
const revocationPayload = canonicalize({
|
|
624
743
|
dokoHash: revocation.dokoHash,
|
|
625
744
|
reason: revocation.reason,
|
|
@@ -632,7 +751,7 @@ export class NamcheGateway extends EventEmitter {
|
|
|
632
751
|
const publicKey = hexToBytes(originalDoko.publicKey);
|
|
633
752
|
const signature = hexToBytes(revocation.signature);
|
|
634
753
|
|
|
635
|
-
const valid =
|
|
754
|
+
const valid = mlDsa65Verify(signature, payloadBytes, publicKey);
|
|
636
755
|
|
|
637
756
|
if (!valid) {
|
|
638
757
|
return { success: false, reason: 'Invalid revocation signature' };
|
|
@@ -653,6 +772,87 @@ export class NamcheGateway extends EventEmitter {
|
|
|
653
772
|
}
|
|
654
773
|
}
|
|
655
774
|
|
|
775
|
+
/**
|
|
776
|
+
* Process a revocation report from the mesh (SAKSHI consensus)
|
|
777
|
+
*
|
|
778
|
+
* Multiple nodes can report evidence of compromise/malice.
|
|
779
|
+
* Revocation happens when reports mathematically agree — not by weighted vote.
|
|
780
|
+
*
|
|
781
|
+
* @param {Object} report - The revocation report
|
|
782
|
+
* @param {string} report.reportedBy - NodeId of the reporting node
|
|
783
|
+
* @param {string} report.reason - Reason for revocation
|
|
784
|
+
* @param {number} [report.timestamp] - When the issue was observed
|
|
785
|
+
* @param {string} targetDokoHash - Hash of the DOKO to potentially revoke
|
|
786
|
+
* @returns {Object} Result: { success, method?, reason?, state? }
|
|
787
|
+
*/
|
|
788
|
+
processRevocationReport(report, targetDokoHash) {
|
|
789
|
+
if (!this.revocationReports.has(targetDokoHash)) {
|
|
790
|
+
this.revocationReports.set(targetDokoHash, []);
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
const reports = this.revocationReports.get(targetDokoHash);
|
|
794
|
+
const witness = new NodeWitness({ nodeId: report.reportedBy });
|
|
795
|
+
|
|
796
|
+
reports.push({
|
|
797
|
+
witness,
|
|
798
|
+
evidence: {
|
|
799
|
+
reason: report.reason,
|
|
800
|
+
targetId: targetDokoHash,
|
|
801
|
+
timestamp: report.timestamp || Date.now(),
|
|
802
|
+
},
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
// SAKSHI: Check mathematical agreement across all revocation reports
|
|
806
|
+
const minReports = this.config.minRevocationReports || 3;
|
|
807
|
+
const result = checkRevocationAgreement(reports, { minReports });
|
|
808
|
+
|
|
809
|
+
if (result.isAgreed) {
|
|
810
|
+
// Mesh agrees on revocation — execute it
|
|
811
|
+
const revocationRecord = {
|
|
812
|
+
dokoHash: targetDokoHash,
|
|
813
|
+
reason: result.data.evidence?.reason || report.reason,
|
|
814
|
+
revokedAt: result.data.timestamp || Date.now(),
|
|
815
|
+
revokedBy: 'MESH_CONSENSUS',
|
|
816
|
+
reportCount: result.data.reportCount,
|
|
817
|
+
method: 'SAKSHI_AGREEMENT',
|
|
818
|
+
};
|
|
819
|
+
|
|
820
|
+
this.revocationLog.add(targetDokoHash, revocationRecord);
|
|
821
|
+
this.dokoCache.delete(targetDokoHash);
|
|
822
|
+
this.revocationReports.delete(targetDokoHash);
|
|
823
|
+
this.stats.revocationsProcessed++;
|
|
824
|
+
|
|
825
|
+
log.info('SAKSHI: Mesh revocation consensus reached', {
|
|
826
|
+
dokoHash: targetDokoHash,
|
|
827
|
+
reportCount: result.data.reportCount,
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
this.emit('revoked', {
|
|
831
|
+
dokoHash: targetDokoHash,
|
|
832
|
+
revocation: revocationRecord,
|
|
833
|
+
method: 'SAKSHI_AGREEMENT',
|
|
834
|
+
});
|
|
835
|
+
|
|
836
|
+
return { success: true, method: 'SAKSHI_AGREEMENT' };
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (result.isDisagreed) {
|
|
840
|
+
log.debug('SAKSHI: Revocation reports disagree', {
|
|
841
|
+
dokoHash: targetDokoHash,
|
|
842
|
+
reason: result.reason,
|
|
843
|
+
});
|
|
844
|
+
return { success: false, reason: 'Reports disagree on evidence', details: result.reason };
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
// PENDING — need more reports
|
|
848
|
+
log.debug('SAKSHI: Revocation report recorded, awaiting consensus', {
|
|
849
|
+
dokoHash: targetDokoHash,
|
|
850
|
+
currentReports: reports.length,
|
|
851
|
+
minRequired: minReports,
|
|
852
|
+
});
|
|
853
|
+
return { success: false, reason: 'Pending — need more reports', state: 'PENDING' };
|
|
854
|
+
}
|
|
855
|
+
|
|
656
856
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
657
857
|
// CACHE & LOOKUP
|
|
658
858
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -695,13 +895,13 @@ export class NamcheGateway extends EventEmitter {
|
|
|
695
895
|
*/
|
|
696
896
|
lookupByDomain(domain) {
|
|
697
897
|
const normalizedDomain = domain.toLowerCase().replace(/^www\./, '');
|
|
698
|
-
|
|
898
|
+
|
|
699
899
|
for (const [hash, cached] of this.dokoCache.cache.entries()) {
|
|
700
900
|
// Check TTL first
|
|
701
901
|
if (Date.now() - cached.verifiedAt >= this.config.dokoCacheTTL) {
|
|
702
902
|
continue;
|
|
703
903
|
}
|
|
704
|
-
|
|
904
|
+
|
|
705
905
|
// Check if DOKO has domain claims
|
|
706
906
|
const doko = cached.doko;
|
|
707
907
|
if (doko.claims?.domains) {
|
|
@@ -726,6 +926,35 @@ export class NamcheGateway extends EventEmitter {
|
|
|
726
926
|
revocationsCount: this.revocationLog.size,
|
|
727
927
|
};
|
|
728
928
|
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* Create a DOKO with 144T commitment (dual-layer security).
|
|
932
|
+
*
|
|
933
|
+
* This is the recommended way to create DOKOs for full defense-in-depth:
|
|
934
|
+
* - NIST layer: ML-DSA-65 signature
|
|
935
|
+
* - 144T layer: YPC-27 + polynomial binding commitment
|
|
936
|
+
*
|
|
937
|
+
* @param {Object} dokoFields — DOKO fields (type, nodeId, publicKey, etc.)
|
|
938
|
+
* @param {string} secretKey — Sender's ML-DSA-65 secret key (hex)
|
|
939
|
+
* @param {TritAddress} senderAddress — Sender's 144T mesh address
|
|
940
|
+
* @returns {Object} — Complete DOKO with signature and tritCommitment
|
|
941
|
+
*/
|
|
942
|
+
static createDokoWithCommitment(dokoFields, secretKey, senderAddress) {
|
|
943
|
+
// Canonicalize for deterministic signing
|
|
944
|
+
const payload = canonicalize(dokoFields);
|
|
945
|
+
|
|
946
|
+
// Layer 1: NIST signature (ML-DSA-65)
|
|
947
|
+
const signature = signMessage(payload, secretKey);
|
|
948
|
+
|
|
949
|
+
// Layer 2: 144T commitment (YPC-27 + polynomial binding)
|
|
950
|
+
const tritCommitment = TritCommitment.create(dokoFields, senderAddress);
|
|
951
|
+
|
|
952
|
+
return {
|
|
953
|
+
...dokoFields,
|
|
954
|
+
signature,
|
|
955
|
+
tritCommitment,
|
|
956
|
+
};
|
|
957
|
+
}
|
|
729
958
|
}
|
|
730
959
|
|
|
731
960
|
export default NamcheGateway;
|