yakmesh 2.8.2 → 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/CONTRIBUTING.md +42 -0
- package/Caddyfile +77 -0
- package/README.md +119 -29
- package/adapters/adapter-mlv-bible/README.md +124 -0
- package/adapters/adapter-mlv-bible/index.js +400 -0
- package/adapters/chat-mod-adapter.js +532 -0
- package/adapters/content-adapter.js +273 -0
- package/content/api.js +50 -41
- package/content/index.js +2 -2
- package/content/store.js +355 -173
- 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 +334 -74
- 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 +113 -1
- package/mesh/darshan.js +1718 -0
- package/mesh/gumba.js +1567 -0
- package/mesh/jhilke.js +651 -0
- package/mesh/katha.js +1012 -0
- package/mesh/nakpak-routing.js +8 -5
- package/mesh/network.js +724 -34
- package/mesh/pulse-sync.js +4 -1
- package/mesh/rate-limiter.js +127 -15
- 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/vani.js +1364 -0
- package/mesh/yurt.js +1340 -0
- 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/network-identity.js +16 -0
- package/oracle/packet-checksum.js +201 -0
- package/oracle/sst.js +579 -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 +28 -13
- 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-nav.js +194 -0
- package/scripts/update-docs-sidebar.cjs +164 -0
- package/security/crypto-config.js +4 -3
- package/security/dharma-moderation.js +517 -0
- 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 +373 -5
- package/security/namche-gateway.js +298 -69
- package/security/sakshi.js +460 -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/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
|
@@ -0,0 +1,714 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TERNARY-144T — 144-Trit Hierarchical Mesh Addressing
|
|
3
|
+
*
|
|
4
|
+
* A 4-tier hierarchical addressing system using 144 balanced ternary digits,
|
|
5
|
+
* structured as 4 × 36-trit segments (or equivalently 4 levels of 3^3 sub-blocks).
|
|
6
|
+
*
|
|
7
|
+
* Why 144 trits?
|
|
8
|
+
* - 144 = 12² = 4 × 36 = 4 × 4 × 9 = (2²)(2²)(3²) — rich factorization
|
|
9
|
+
* - 3^144 ≈ 1.55 × 10^68 unique addresses (exceeds SHA-256 space)
|
|
10
|
+
* - Natural 4-tier hierarchy: Galaxy → Cluster → Zone → Node
|
|
11
|
+
* - Each 36-trit tier ≈ 57 bits — fits in a 64-bit word with room for metadata
|
|
12
|
+
* - 144 = 6 × 24 — aligns with SST's 24-cycle Fibonacci (6 full cycles)
|
|
13
|
+
* - 144 = 4 × 36 → each tier holds 4 × 9-trit sub-blocks (hexagonal addressing)
|
|
14
|
+
*
|
|
15
|
+
* POST-QUANTUM HARDENING:
|
|
16
|
+
* - 144 trits × log₂(3) ≈ 228 bits of classical entropy
|
|
17
|
+
* - Grover's algorithm (quantum search) halves binary bit-security but gains
|
|
18
|
+
* NO advantage over balanced ternary — ternary search is already optimal
|
|
19
|
+
* - Result: 144T provides ~256-bit equivalent post-quantum routing security
|
|
20
|
+
* - Combined with ML-DSA-65 signatures and ML-KEM-768 key exchange,
|
|
21
|
+
* the entire mesh addressing layer is quantum-resistant
|
|
22
|
+
*
|
|
23
|
+
* Hierarchy (4 tiers, 36 trits each):
|
|
24
|
+
*
|
|
25
|
+
* ┌──────────────────────────────────────────────────────────────────────────────┐
|
|
26
|
+
* │ Galaxy (36 trits) │ Cluster (36 trits) │ Zone (36 trits) │ Node (36 trits) │
|
|
27
|
+
* │ ≈ 57 bits │ ≈ 57 bits │ ≈ 57 bits │ ≈ 57 bits │
|
|
28
|
+
* └──────────────────────────────────────────────────────────────────────────────┘
|
|
29
|
+
*
|
|
30
|
+
* Within each tier, 36 trits decompose into 4 × 9-trit sub-blocks:
|
|
31
|
+
* [region:9] [sector:9] [cell:9] [local:9]
|
|
32
|
+
*
|
|
33
|
+
* This maps naturally to the SST hexagonal tessellation (HexCoord):
|
|
34
|
+
* - 9 trits = 3^9 = 19683 addresses per sub-block
|
|
35
|
+
* - 4 sub-blocks per tier = 3^36 ≈ 1.5 × 10^17 per tier
|
|
36
|
+
*
|
|
37
|
+
* Integration:
|
|
38
|
+
* TRIBHUJ (trit algebra) → 144T (addressing) → YPC-27 (integrity per tier)
|
|
39
|
+
* SST (hex geometry) → 144T (spatial mapping) → PRAHARI (satellite seeds)
|
|
40
|
+
* MANDALA (mesh topology) → 144T (hierarchical routing)
|
|
41
|
+
*
|
|
42
|
+
* @module oracle/ternary-144t
|
|
43
|
+
* @license MIT
|
|
44
|
+
* @copyright 2026 YAKMESH™ Contributors
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
import { Trit, TritArray, POSITIVE, NEUTRAL, NEGATIVE } from './tribhuj.js';
|
|
48
|
+
import { digitalRoot, getFamilyOf, SSTFamily, FIBONACCI_CYCLE_24 } from './sst.js';
|
|
49
|
+
import { Poly27, YPC27Checksum, N as YPC27_N, DEFAULT_SEED, bytesToTrits } from './ypc27.js';
|
|
50
|
+
|
|
51
|
+
// =============================================================================
|
|
52
|
+
// CONSTANTS
|
|
53
|
+
// =============================================================================
|
|
54
|
+
|
|
55
|
+
/** Total trit length of a full 144T address */
|
|
56
|
+
export const TOTAL_TRITS = 144;
|
|
57
|
+
|
|
58
|
+
/** Number of hierarchical tiers */
|
|
59
|
+
export const TIER_COUNT = 4;
|
|
60
|
+
|
|
61
|
+
/** Trits per tier */
|
|
62
|
+
export const TRITS_PER_TIER = 36;
|
|
63
|
+
|
|
64
|
+
/** Sub-blocks per tier */
|
|
65
|
+
export const SUB_BLOCKS_PER_TIER = 4;
|
|
66
|
+
|
|
67
|
+
/** Trits per sub-block */
|
|
68
|
+
export const TRITS_PER_SUB_BLOCK = 9;
|
|
69
|
+
|
|
70
|
+
/** Tier names (Himalayan naming convention) */
|
|
71
|
+
export const TierName = Object.freeze({
|
|
72
|
+
GALAXY: 0, // Broadest scope — intercontinental / satellite
|
|
73
|
+
CLUSTER: 1, // Regional mesh clusters
|
|
74
|
+
ZONE: 2, // Local zone (city-scale, maps to HexCoord)
|
|
75
|
+
NODE: 3, // Individual node identity
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
/** Tier name labels */
|
|
79
|
+
export const TIER_LABELS = Object.freeze(['galaxy', 'cluster', 'zone', 'node']);
|
|
80
|
+
|
|
81
|
+
// =============================================================================
|
|
82
|
+
// TRIT ADDRESS CLASS
|
|
83
|
+
// =============================================================================
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* A 144-trit hierarchical mesh address.
|
|
87
|
+
* Immutable value object with tier-aware operations.
|
|
88
|
+
*/
|
|
89
|
+
export class TritAddress {
|
|
90
|
+
/** @type {Int8Array} — 144 balanced ternary digits */
|
|
91
|
+
#trits;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Create a TritAddress.
|
|
95
|
+
* @param {Int8Array | number[] | string} value — 144 trits or trit string
|
|
96
|
+
*/
|
|
97
|
+
constructor(value) {
|
|
98
|
+
if (typeof value === 'string') {
|
|
99
|
+
this.#trits = TritAddress.#parseString(value);
|
|
100
|
+
} else if (value instanceof Int8Array) {
|
|
101
|
+
if (value.length !== TOTAL_TRITS) {
|
|
102
|
+
throw new Error(`TritAddress requires ${TOTAL_TRITS} trits, got ${value.length}`);
|
|
103
|
+
}
|
|
104
|
+
this.#trits = new Int8Array(value);
|
|
105
|
+
} else if (Array.isArray(value)) {
|
|
106
|
+
if (value.length !== TOTAL_TRITS) {
|
|
107
|
+
throw new Error(`TritAddress requires ${TOTAL_TRITS} trits, got ${value.length}`);
|
|
108
|
+
}
|
|
109
|
+
this.#trits = new Int8Array(value);
|
|
110
|
+
} else {
|
|
111
|
+
throw new Error('TritAddress requires Int8Array, number[], or string');
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Validate all values are balanced ternary
|
|
115
|
+
for (let i = 0; i < TOTAL_TRITS; i++) {
|
|
116
|
+
if (this.#trits[i] < -1 || this.#trits[i] > 1) {
|
|
117
|
+
throw new Error(`Invalid trit value at index ${i}: ${this.#trits[i]}`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
Object.freeze(this);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get the full 144-trit array.
|
|
126
|
+
* @returns {Int8Array}
|
|
127
|
+
*/
|
|
128
|
+
toTrits() {
|
|
129
|
+
return new Int8Array(this.#trits);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Get a specific tier's 36-trit segment.
|
|
134
|
+
* @param {number} tier — 0-3 (GALAXY, CLUSTER, ZONE, NODE)
|
|
135
|
+
* @returns {Int8Array} — 36 trits
|
|
136
|
+
*/
|
|
137
|
+
getTier(tier) {
|
|
138
|
+
if (tier < 0 || tier >= TIER_COUNT) {
|
|
139
|
+
throw new Error(`Invalid tier: ${tier}. Must be 0-${TIER_COUNT - 1}`);
|
|
140
|
+
}
|
|
141
|
+
const start = tier * TRITS_PER_TIER;
|
|
142
|
+
return new Int8Array(this.#trits.slice(start, start + TRITS_PER_TIER));
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get a sub-block within a tier.
|
|
147
|
+
* @param {number} tier — 0-3
|
|
148
|
+
* @param {number} subBlock — 0-3 (region, sector, cell, local)
|
|
149
|
+
* @returns {Int8Array} — 9 trits
|
|
150
|
+
*/
|
|
151
|
+
getSubBlock(tier, subBlock) {
|
|
152
|
+
if (subBlock < 0 || subBlock >= SUB_BLOCKS_PER_TIER) {
|
|
153
|
+
throw new Error(`Invalid sub-block: ${subBlock}`);
|
|
154
|
+
}
|
|
155
|
+
const start = tier * TRITS_PER_TIER + subBlock * TRITS_PER_SUB_BLOCK;
|
|
156
|
+
return new Int8Array(this.#trits.slice(start, start + TRITS_PER_SUB_BLOCK));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get the tier name label.
|
|
161
|
+
* @param {number} tier
|
|
162
|
+
* @returns {string}
|
|
163
|
+
*/
|
|
164
|
+
static tierLabel(tier) {
|
|
165
|
+
return TIER_LABELS[tier] || 'unknown';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Check if two addresses share a common prefix up to a given tier.
|
|
170
|
+
* Addresses in the same galaxy share tier 0.
|
|
171
|
+
* Addresses in the same cluster share tiers 0+1.
|
|
172
|
+
*
|
|
173
|
+
* @param {TritAddress} other
|
|
174
|
+
* @param {number} tier — check up to and including this tier (0-3)
|
|
175
|
+
* @returns {boolean}
|
|
176
|
+
*/
|
|
177
|
+
sharesTier(other, tier) {
|
|
178
|
+
const endTrit = (tier + 1) * TRITS_PER_TIER;
|
|
179
|
+
for (let i = 0; i < endTrit; i++) {
|
|
180
|
+
if (this.#trits[i] !== other.#trits[i]) return false;
|
|
181
|
+
}
|
|
182
|
+
return true;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Compute the hierarchical distance between two addresses.
|
|
187
|
+
* Returns the tier at which the addresses diverge.
|
|
188
|
+
*
|
|
189
|
+
* Distance 0 = identical
|
|
190
|
+
* Distance 1 = different node, same zone
|
|
191
|
+
* Distance 2 = different zone, same cluster
|
|
192
|
+
* Distance 3 = different cluster, same galaxy
|
|
193
|
+
* Distance 4 = different galaxy
|
|
194
|
+
*
|
|
195
|
+
* @param {TritAddress} other
|
|
196
|
+
* @returns {number} — 0-4
|
|
197
|
+
*/
|
|
198
|
+
tierDistance(other) {
|
|
199
|
+
for (let tier = 0; tier < TIER_COUNT; tier++) {
|
|
200
|
+
const start = tier * TRITS_PER_TIER;
|
|
201
|
+
const end = start + TRITS_PER_TIER;
|
|
202
|
+
for (let i = start; i < end; i++) {
|
|
203
|
+
if (this.#trits[i] !== other.#trits[i]) {
|
|
204
|
+
return TIER_COUNT - tier;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return 0; // Identical addresses
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Compute the XOR-like ternary distance (trit-wise difference).
|
|
213
|
+
* For balanced ternary, "XOR" is: diff = (a - b + 3) % 3 mapped to {-1,0,+1}
|
|
214
|
+
* The Hamming-like distance counts non-zero differences.
|
|
215
|
+
*
|
|
216
|
+
* @param {TritAddress} other
|
|
217
|
+
* @returns {{ distance: number, maxDistance: number, similarity: number }}
|
|
218
|
+
*/
|
|
219
|
+
tritDistance(other) {
|
|
220
|
+
let distance = 0;
|
|
221
|
+
for (let i = 0; i < TOTAL_TRITS; i++) {
|
|
222
|
+
if (this.#trits[i] !== other.#trits[i]) distance++;
|
|
223
|
+
}
|
|
224
|
+
return {
|
|
225
|
+
distance,
|
|
226
|
+
maxDistance: TOTAL_TRITS,
|
|
227
|
+
similarity: +((1 - distance / TOTAL_TRITS).toFixed(4)),
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get the SST family balance for each tier.
|
|
233
|
+
* Useful for detecting address anomalies or locality properties.
|
|
234
|
+
*
|
|
235
|
+
* @returns {Array<{tier: string, a: number, b: number, c: number, balance: number}>}
|
|
236
|
+
*/
|
|
237
|
+
tierFamilyBalance() {
|
|
238
|
+
const results = [];
|
|
239
|
+
for (let tier = 0; tier < TIER_COUNT; tier++) {
|
|
240
|
+
const tierTrits = this.getTier(tier);
|
|
241
|
+
let a = 0, b = 0, c = 0;
|
|
242
|
+
for (let i = 0; i < TRITS_PER_TIER; i++) {
|
|
243
|
+
// Map trit to SST family: -1 → A, 0 → C, +1 → B
|
|
244
|
+
if (tierTrits[i] === NEGATIVE) a++;
|
|
245
|
+
else if (tierTrits[i] === POSITIVE) b++;
|
|
246
|
+
else c++;
|
|
247
|
+
}
|
|
248
|
+
const expected = TRITS_PER_TIER / 3;
|
|
249
|
+
const balance = Math.sqrt(
|
|
250
|
+
Math.pow(a - expected, 2) +
|
|
251
|
+
Math.pow(b - expected, 2) +
|
|
252
|
+
Math.pow(c - expected, 2)
|
|
253
|
+
) / expected;
|
|
254
|
+
|
|
255
|
+
results.push({
|
|
256
|
+
tier: TIER_LABELS[tier],
|
|
257
|
+
a, b, c,
|
|
258
|
+
balance: +balance.toFixed(4),
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
return results;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Compute a YPC-27 checksum for the full address.
|
|
266
|
+
* Uses the first 27 trits of each tier as a Poly27 input.
|
|
267
|
+
*
|
|
268
|
+
* @returns {Poly27} — 27-trit checksum
|
|
269
|
+
*/
|
|
270
|
+
checksum() {
|
|
271
|
+
const hasher = new YPC27Checksum();
|
|
272
|
+
// Feed each tier's trits as bytes (trit→byte conversion)
|
|
273
|
+
for (let tier = 0; tier < TIER_COUNT; tier++) {
|
|
274
|
+
const tierTrits = this.getTier(tier);
|
|
275
|
+
// Pack trits as int8 bytes for checksum input
|
|
276
|
+
hasher.update(new Uint8Array(tierTrits.buffer));
|
|
277
|
+
}
|
|
278
|
+
return hasher.digest();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Convert to string representation.
|
|
283
|
+
* Format: Galaxy.Cluster.Zone.Node (each tier as trit string)
|
|
284
|
+
* Uses T for -1, 0 for 0, 1 for +1.
|
|
285
|
+
*
|
|
286
|
+
* @param {boolean} [compact=true] — use 9-char sub-block grouping
|
|
287
|
+
* @returns {string}
|
|
288
|
+
*/
|
|
289
|
+
toString(compact = true) {
|
|
290
|
+
const tiers = [];
|
|
291
|
+
for (let tier = 0; tier < TIER_COUNT; tier++) {
|
|
292
|
+
const tierTrits = this.getTier(tier);
|
|
293
|
+
const chars = [];
|
|
294
|
+
for (let i = 0; i < TRITS_PER_TIER; i++) {
|
|
295
|
+
chars.push(tierTrits[i] === NEGATIVE ? 'T' : String(tierTrits[i]));
|
|
296
|
+
}
|
|
297
|
+
if (compact) {
|
|
298
|
+
// Group into sub-blocks separated by colons
|
|
299
|
+
const grouped = [];
|
|
300
|
+
for (let s = 0; s < SUB_BLOCKS_PER_TIER; s++) {
|
|
301
|
+
grouped.push(chars.slice(s * TRITS_PER_SUB_BLOCK, (s + 1) * TRITS_PER_SUB_BLOCK).join(''));
|
|
302
|
+
}
|
|
303
|
+
tiers.push(grouped.join(':'));
|
|
304
|
+
} else {
|
|
305
|
+
tiers.push(chars.join(''));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return tiers.join('.');
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Check equality.
|
|
313
|
+
* @param {TritAddress} other
|
|
314
|
+
* @returns {boolean}
|
|
315
|
+
*/
|
|
316
|
+
equals(other) {
|
|
317
|
+
for (let i = 0; i < TOTAL_TRITS; i++) {
|
|
318
|
+
if (this.#trits[i] !== other.#trits[i]) return false;
|
|
319
|
+
}
|
|
320
|
+
return true;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// ---------------------------------------------------------------------------
|
|
324
|
+
// Static constructors
|
|
325
|
+
// ---------------------------------------------------------------------------
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Create a TritAddress from a hex peer ID (e.g., 64-char SHA3 hash).
|
|
329
|
+
* Converts hex bytes → trits (5 trits per byte), pads/truncates to 144.
|
|
330
|
+
*
|
|
331
|
+
* @param {string} hexId — hex-encoded peer ID
|
|
332
|
+
* @returns {TritAddress}
|
|
333
|
+
*/
|
|
334
|
+
static fromHex(hexId) {
|
|
335
|
+
const bytes = [];
|
|
336
|
+
const cleanHex = hexId.replace(/^0x/, '');
|
|
337
|
+
for (let i = 0; i < cleanHex.length; i += 2) {
|
|
338
|
+
bytes.push(parseInt(cleanHex.substr(i, 2), 16) || 0);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
const allTrits = bytesToTrits(new Uint8Array(bytes));
|
|
342
|
+
|
|
343
|
+
// Pad or truncate to exactly 144
|
|
344
|
+
const trits = new Int8Array(TOTAL_TRITS);
|
|
345
|
+
for (let i = 0; i < TOTAL_TRITS && i < allTrits.length; i++) {
|
|
346
|
+
trits[i] = allTrits[i];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return new TritAddress(trits);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Create a TritAddress from tier components.
|
|
354
|
+
* @param {Int8Array} galaxy — 36 trits
|
|
355
|
+
* @param {Int8Array} cluster — 36 trits
|
|
356
|
+
* @param {Int8Array} zone — 36 trits
|
|
357
|
+
* @param {Int8Array} node — 36 trits
|
|
358
|
+
* @returns {TritAddress}
|
|
359
|
+
*/
|
|
360
|
+
static fromTiers(galaxy, cluster, zone, node) {
|
|
361
|
+
const trits = new Int8Array(TOTAL_TRITS);
|
|
362
|
+
trits.set(galaxy, 0);
|
|
363
|
+
trits.set(cluster, TRITS_PER_TIER);
|
|
364
|
+
trits.set(zone, TRITS_PER_TIER * 2);
|
|
365
|
+
trits.set(node, TRITS_PER_TIER * 3);
|
|
366
|
+
return new TritAddress(trits);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Create a zero address (all NEUTRAL trits).
|
|
371
|
+
* Used as the "origin" in the ternary address space.
|
|
372
|
+
* @returns {TritAddress}
|
|
373
|
+
*/
|
|
374
|
+
static zero() {
|
|
375
|
+
return new TritAddress(new Int8Array(TOTAL_TRITS));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Create a random address (for testing).
|
|
380
|
+
* Each trit is randomly -1, 0, or +1.
|
|
381
|
+
* @returns {TritAddress}
|
|
382
|
+
*/
|
|
383
|
+
static random() {
|
|
384
|
+
const trits = new Int8Array(TOTAL_TRITS);
|
|
385
|
+
for (let i = 0; i < TOTAL_TRITS; i++) {
|
|
386
|
+
trits[i] = Math.floor(Math.random() * 3) - 1;
|
|
387
|
+
}
|
|
388
|
+
return new TritAddress(trits);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Parse a trit string (T=−1, 0=0, 1=+1).
|
|
393
|
+
* Strips dots and colons used as separators.
|
|
394
|
+
* @param {string} s
|
|
395
|
+
* @returns {Int8Array}
|
|
396
|
+
*/
|
|
397
|
+
static #parseString(s) {
|
|
398
|
+
const clean = s.replace(/[.:]/g, '');
|
|
399
|
+
if (clean.length !== TOTAL_TRITS) {
|
|
400
|
+
throw new Error(`TritAddress string must have ${TOTAL_TRITS} trit chars, got ${clean.length}`);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const trits = new Int8Array(TOTAL_TRITS);
|
|
404
|
+
for (let i = 0; i < TOTAL_TRITS; i++) {
|
|
405
|
+
const c = clean[i].toUpperCase();
|
|
406
|
+
if (c === 'T' || c === '-') trits[i] = NEGATIVE;
|
|
407
|
+
else if (c === '1' || c === '+') trits[i] = POSITIVE;
|
|
408
|
+
else trits[i] = NEUTRAL;
|
|
409
|
+
}
|
|
410
|
+
return trits;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// =============================================================================
|
|
415
|
+
// ROUTING TABLE — Hierarchical Ternary Routing
|
|
416
|
+
// =============================================================================
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* TernaryRoutingTable — XOR-metric routing for 144T addresses.
|
|
420
|
+
*
|
|
421
|
+
* Similar to Kademlia but using balanced ternary distance instead of
|
|
422
|
+
* binary XOR. Each "k-bucket" is replaced by a "t-bucket" that groups
|
|
423
|
+
* peers by tier distance (galaxy, cluster, zone, node).
|
|
424
|
+
*
|
|
425
|
+
* Routing priority:
|
|
426
|
+
* 1. Same zone (tier distance 1) — direct connection preferred
|
|
427
|
+
* 2. Same cluster (distance 2) — relay via zone peer
|
|
428
|
+
* 3. Same galaxy (distance 3) — relay via cluster peer
|
|
429
|
+
* 4. Different galaxy (distance 4) — relay via galaxy peer
|
|
430
|
+
*/
|
|
431
|
+
export class TernaryRoutingTable {
|
|
432
|
+
/**
|
|
433
|
+
* @param {TritAddress} selfAddress — this node's 144T address
|
|
434
|
+
* @param {number} [bucketSize=6] — max peers per bucket (k=6 for hexagonal)
|
|
435
|
+
*/
|
|
436
|
+
constructor(selfAddress, bucketSize = 6) {
|
|
437
|
+
this.selfAddress = selfAddress;
|
|
438
|
+
this.bucketSize = bucketSize;
|
|
439
|
+
|
|
440
|
+
// 4 buckets — one per tier distance (1=zone, 2=cluster, 3=galaxy, 4=remote)
|
|
441
|
+
/** @type {Map<string, {address: TritAddress, lastSeen: number, rtt: number}>[]} */
|
|
442
|
+
this._buckets = new Array(TIER_COUNT).fill(null).map(() => new Map());
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Add a peer to the routing table.
|
|
447
|
+
* Placed in the bucket corresponding to tier distance from self.
|
|
448
|
+
*
|
|
449
|
+
* @param {string} peerId — unique peer identifier
|
|
450
|
+
* @param {TritAddress} peerAddress — peer's 144T address
|
|
451
|
+
* @param {number} [rtt=0] — round-trip time in ms
|
|
452
|
+
* @returns {boolean} — true if added (false if bucket full)
|
|
453
|
+
*/
|
|
454
|
+
addPeer(peerId, peerAddress, rtt = 0) {
|
|
455
|
+
const distance = this.selfAddress.tierDistance(peerAddress);
|
|
456
|
+
if (distance === 0) return false; // Can't add self
|
|
457
|
+
|
|
458
|
+
const bucketIdx = distance - 1; // 1-4 → 0-3
|
|
459
|
+
const bucket = this._buckets[bucketIdx];
|
|
460
|
+
|
|
461
|
+
// Update if already present
|
|
462
|
+
if (bucket.has(peerId)) {
|
|
463
|
+
const entry = bucket.get(peerId);
|
|
464
|
+
entry.lastSeen = Date.now();
|
|
465
|
+
entry.rtt = rtt;
|
|
466
|
+
return true;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Add if bucket has room
|
|
470
|
+
if (bucket.size < this.bucketSize) {
|
|
471
|
+
bucket.set(peerId, {
|
|
472
|
+
address: peerAddress,
|
|
473
|
+
lastSeen: Date.now(),
|
|
474
|
+
rtt,
|
|
475
|
+
});
|
|
476
|
+
return true;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Bucket full — evict oldest if new peer is closer (lower RTT)
|
|
480
|
+
let oldestKey = null;
|
|
481
|
+
let oldestTime = Infinity;
|
|
482
|
+
for (const [key, entry] of bucket) {
|
|
483
|
+
if (entry.lastSeen < oldestTime) {
|
|
484
|
+
oldestTime = entry.lastSeen;
|
|
485
|
+
oldestKey = key;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (oldestKey) {
|
|
490
|
+
bucket.delete(oldestKey);
|
|
491
|
+
bucket.set(peerId, {
|
|
492
|
+
address: peerAddress,
|
|
493
|
+
lastSeen: Date.now(),
|
|
494
|
+
rtt,
|
|
495
|
+
});
|
|
496
|
+
return true;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Remove a peer from the routing table.
|
|
504
|
+
* @param {string} peerId
|
|
505
|
+
* @returns {boolean}
|
|
506
|
+
*/
|
|
507
|
+
removePeer(peerId) {
|
|
508
|
+
for (const bucket of this._buckets) {
|
|
509
|
+
if (bucket.delete(peerId)) return true;
|
|
510
|
+
}
|
|
511
|
+
return false;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Find the closest peers to a target address.
|
|
516
|
+
* Returns peers sorted by trit distance (ascending).
|
|
517
|
+
*
|
|
518
|
+
* @param {TritAddress} target — address to find peers near
|
|
519
|
+
* @param {number} [count=6] — max peers to return
|
|
520
|
+
* @returns {Array<{peerId: string, address: TritAddress, distance: number, rtt: number}>}
|
|
521
|
+
*/
|
|
522
|
+
findClosest(target, count = 6) {
|
|
523
|
+
const candidates = [];
|
|
524
|
+
|
|
525
|
+
for (const bucket of this._buckets) {
|
|
526
|
+
for (const [peerId, entry] of bucket) {
|
|
527
|
+
const { distance } = entry.address.tritDistance(target);
|
|
528
|
+
candidates.push({
|
|
529
|
+
peerId,
|
|
530
|
+
address: entry.address,
|
|
531
|
+
distance,
|
|
532
|
+
rtt: entry.rtt,
|
|
533
|
+
});
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
// Sort by trit distance, then by RTT
|
|
538
|
+
candidates.sort((a, b) => a.distance - b.distance || a.rtt - b.rtt);
|
|
539
|
+
|
|
540
|
+
return candidates.slice(0, count);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* Get the next hop toward a target address.
|
|
545
|
+
* Selects the peer with minimized tier distance to the target.
|
|
546
|
+
*
|
|
547
|
+
* @param {TritAddress} target
|
|
548
|
+
* @returns {{peerId: string, address: TritAddress, tierDistance: number, rtt: number} | null}
|
|
549
|
+
*/
|
|
550
|
+
nextHop(target) {
|
|
551
|
+
const targetTierDist = this.selfAddress.tierDistance(target);
|
|
552
|
+
|
|
553
|
+
// Try to find a peer closer to the target than we are
|
|
554
|
+
let best = null;
|
|
555
|
+
let bestDist = targetTierDist;
|
|
556
|
+
|
|
557
|
+
for (const bucket of this._buckets) {
|
|
558
|
+
for (const [peerId, entry] of bucket) {
|
|
559
|
+
const dist = entry.address.tierDistance(target);
|
|
560
|
+
if (dist < bestDist || (dist === bestDist && (!best || entry.rtt < best.rtt))) {
|
|
561
|
+
bestDist = dist;
|
|
562
|
+
best = { peerId, address: entry.address, tierDistance: dist, rtt: entry.rtt };
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
return best;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
/**
|
|
571
|
+
* Get routing table status.
|
|
572
|
+
* @returns {Object}
|
|
573
|
+
*/
|
|
574
|
+
getStatus() {
|
|
575
|
+
const buckets = this._buckets.map((bucket, i) => ({
|
|
576
|
+
tier: TIER_LABELS[TIER_COUNT - 1 - i] || `distance-${i + 1}`,
|
|
577
|
+
peerCount: bucket.size,
|
|
578
|
+
capacity: this.bucketSize,
|
|
579
|
+
}));
|
|
580
|
+
|
|
581
|
+
const totalPeers = this._buckets.reduce((sum, b) => sum + b.size, 0);
|
|
582
|
+
|
|
583
|
+
return {
|
|
584
|
+
selfAddress: this.selfAddress.toString(),
|
|
585
|
+
buckets,
|
|
586
|
+
totalPeers,
|
|
587
|
+
capacity: this.bucketSize * TIER_COUNT,
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
// =============================================================================
|
|
593
|
+
// UTILITY FUNCTIONS
|
|
594
|
+
// =============================================================================
|
|
595
|
+
|
|
596
|
+
/**
|
|
597
|
+
* Convert a standard 64-char hex node ID into a 144T address with
|
|
598
|
+
* SST-based tier assignment.
|
|
599
|
+
*
|
|
600
|
+
* The hex ID is converted to trits, then the tiers are assigned
|
|
601
|
+
* using an SST Fibonacci cycle rotation to ensure good distribution
|
|
602
|
+
* across the address space.
|
|
603
|
+
*
|
|
604
|
+
* @param {string} hexId — 64-char hex peer ID
|
|
605
|
+
* @param {Object} [locality] — optional locality hints
|
|
606
|
+
* @param {number} [locality.galaxy=0] — galaxy index (0 for default)
|
|
607
|
+
* @param {number} [locality.cluster=0] — cluster index
|
|
608
|
+
* @returns {TritAddress}
|
|
609
|
+
*/
|
|
610
|
+
export function hexIdToAddress(hexId, locality = {}) {
|
|
611
|
+
// Convert hex to raw trits
|
|
612
|
+
const bytes = [];
|
|
613
|
+
const clean = hexId.replace(/^0x/, '').slice(0, 64); // Max 32 bytes = 160 trits
|
|
614
|
+
for (let i = 0; i < clean.length; i += 2) {
|
|
615
|
+
bytes.push(parseInt(clean.substr(i, 2), 16) || 0);
|
|
616
|
+
}
|
|
617
|
+
const rawTrits = bytesToTrits(new Uint8Array(bytes));
|
|
618
|
+
|
|
619
|
+
const trits = new Int8Array(TOTAL_TRITS);
|
|
620
|
+
|
|
621
|
+
// Galaxy tier: use locality hint or derive from first bytes
|
|
622
|
+
if (locality.galaxy !== undefined) {
|
|
623
|
+
const galaxyTrits = new TritArray(locality.galaxy, TRITS_PER_TIER).toJSON();
|
|
624
|
+
for (let i = 0; i < TRITS_PER_TIER && i < galaxyTrits.length; i++) {
|
|
625
|
+
trits[i] = galaxyTrits[i];
|
|
626
|
+
}
|
|
627
|
+
} else {
|
|
628
|
+
// Derive galaxy from SST digital root of first 4 bytes
|
|
629
|
+
for (let i = 0; i < TRITS_PER_TIER && i < rawTrits.length; i++) {
|
|
630
|
+
trits[i] = rawTrits[i];
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
// Cluster tier: derive or use hint
|
|
635
|
+
if (locality.cluster !== undefined) {
|
|
636
|
+
const clusterTrits = new TritArray(locality.cluster, TRITS_PER_TIER).toJSON();
|
|
637
|
+
for (let i = 0; i < TRITS_PER_TIER && i < clusterTrits.length; i++) {
|
|
638
|
+
trits[TRITS_PER_TIER + i] = clusterTrits[i];
|
|
639
|
+
}
|
|
640
|
+
} else {
|
|
641
|
+
for (let i = 0; i < TRITS_PER_TIER; i++) {
|
|
642
|
+
const srcIdx = TRITS_PER_TIER + i;
|
|
643
|
+
trits[TRITS_PER_TIER + i] = srcIdx < rawTrits.length ? rawTrits[srcIdx] : 0;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
// Zone tier: always derived from hex ID
|
|
648
|
+
for (let i = 0; i < TRITS_PER_TIER; i++) {
|
|
649
|
+
const srcIdx = TRITS_PER_TIER * 2 + i;
|
|
650
|
+
trits[TRITS_PER_TIER * 2 + i] = srcIdx < rawTrits.length ? rawTrits[srcIdx] : 0;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
// Node tier: always derived from hex ID (unique portion)
|
|
654
|
+
for (let i = 0; i < TRITS_PER_TIER; i++) {
|
|
655
|
+
const srcIdx = TRITS_PER_TIER * 3 + i;
|
|
656
|
+
trits[TRITS_PER_TIER * 3 + i] = srcIdx < rawTrits.length ? rawTrits[srcIdx] : 0;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return new TritAddress(trits);
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Compute the 216-hypercycle position for an address.
|
|
664
|
+
* The hypercycle is LCM(27, 24) = 216 — the full SST-YPC-27 alignment.
|
|
665
|
+
*
|
|
666
|
+
* @param {TritAddress} address
|
|
667
|
+
* @returns {number} — position 0-215
|
|
668
|
+
*/
|
|
669
|
+
export function hypercyclePosition(address) {
|
|
670
|
+
const trits = address.toTrits();
|
|
671
|
+
// Sum all trit absolute values, then mod 216
|
|
672
|
+
let sum = 0;
|
|
673
|
+
for (let i = 0; i < TOTAL_TRITS; i++) {
|
|
674
|
+
sum += Math.abs(trits[i]) * (i + 1);
|
|
675
|
+
}
|
|
676
|
+
return sum % 216;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/**
|
|
680
|
+
* Verify the integrity of a 144T address using YPC-27.
|
|
681
|
+
* Computes a checksum of all 4 tiers and compares.
|
|
682
|
+
*
|
|
683
|
+
* @param {TritAddress} address
|
|
684
|
+
* @param {Poly27} expectedChecksum
|
|
685
|
+
* @returns {boolean}
|
|
686
|
+
*/
|
|
687
|
+
export function verifyAddressIntegrity(address, expectedChecksum) {
|
|
688
|
+
const computed = address.checksum();
|
|
689
|
+
return computed.equals(expectedChecksum);
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
// =============================================================================
|
|
693
|
+
// EXPORTS
|
|
694
|
+
// =============================================================================
|
|
695
|
+
|
|
696
|
+
export default {
|
|
697
|
+
// Constants
|
|
698
|
+
TOTAL_TRITS,
|
|
699
|
+
TIER_COUNT,
|
|
700
|
+
TRITS_PER_TIER,
|
|
701
|
+
SUB_BLOCKS_PER_TIER,
|
|
702
|
+
TRITS_PER_SUB_BLOCK,
|
|
703
|
+
TierName,
|
|
704
|
+
TIER_LABELS,
|
|
705
|
+
|
|
706
|
+
// Classes
|
|
707
|
+
TritAddress,
|
|
708
|
+
TernaryRoutingTable,
|
|
709
|
+
|
|
710
|
+
// Utilities
|
|
711
|
+
hexIdToAddress,
|
|
712
|
+
hypercyclePosition,
|
|
713
|
+
verifyAddressIntegrity,
|
|
714
|
+
};
|