soulprint-network 0.3.3 → 0.3.5
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/code-hash.json +25 -0
- package/dist/code-integrity.d.ts +44 -0
- package/dist/code-integrity.js +108 -0
- package/dist/validator.js +25 -0
- package/package.json +6 -5
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"codeHash": "559c4b6036fc59ae758016efba130d9147d8a89d5f43aff68a0a00e788aec761",
|
|
3
|
+
"codeHashHex": "0x559c4b6036fc59ae758016efba130d9147d8a89d5f43aff68a0a00e788aec761",
|
|
4
|
+
"computedAt": "2026-02-24T21:00:59.352Z",
|
|
5
|
+
"fileCount": 17,
|
|
6
|
+
"files": [
|
|
7
|
+
"blockchain/blockchain-anchor.ts",
|
|
8
|
+
"blockchain/blockchain-client.ts",
|
|
9
|
+
"code-integrity.ts",
|
|
10
|
+
"consensus/attestation-consensus.ts",
|
|
11
|
+
"consensus/index.ts",
|
|
12
|
+
"consensus/nullifier-consensus.ts",
|
|
13
|
+
"consensus/state-sync.ts",
|
|
14
|
+
"credentials/email.ts",
|
|
15
|
+
"credentials/github.ts",
|
|
16
|
+
"credentials/index.ts",
|
|
17
|
+
"credentials/phone.ts",
|
|
18
|
+
"crypto/gossip-cipher.ts",
|
|
19
|
+
"crypto/peer-router.ts",
|
|
20
|
+
"index.ts",
|
|
21
|
+
"p2p.ts",
|
|
22
|
+
"server.ts",
|
|
23
|
+
"validator.ts"
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* code-integrity.ts — Fix 2: Runtime code integrity verification
|
|
3
|
+
*
|
|
4
|
+
* Carga el hash computado en build-time y lo expone para:
|
|
5
|
+
* 1. Logging al arranque del nodo
|
|
6
|
+
* 2. GET /health (peers pueden verificar)
|
|
7
|
+
* 3. Comparación con hashes aprobados on-chain por governance
|
|
8
|
+
*
|
|
9
|
+
* GARANTÍA:
|
|
10
|
+
* - Si alguien modifica src/ y recompila → el hash cambia
|
|
11
|
+
* - Los peers pueden detectar que un nodo corre código diferente
|
|
12
|
+
* - El GovernanceModule puede registrar hashes aprobados on-chain
|
|
13
|
+
* - Si el hash no coincide con el aprobado → el nodo queda marcado como no confiable
|
|
14
|
+
*
|
|
15
|
+
* LIMITACIÓN (sin TEE):
|
|
16
|
+
* - Un atacante con root puede modificar dist/ directamente o falsificar el hash
|
|
17
|
+
* - La protección completa requiere TEE (Intel SGX / AMD SEV) — fase futura
|
|
18
|
+
*/
|
|
19
|
+
export interface CodeIntegrityInfo {
|
|
20
|
+
codeHash: string;
|
|
21
|
+
codeHashHex: string;
|
|
22
|
+
computedAt: string;
|
|
23
|
+
fileCount: number;
|
|
24
|
+
available: boolean;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Carga el code-hash.json generado en build time.
|
|
28
|
+
*/
|
|
29
|
+
export declare function getCodeIntegrity(): CodeIntegrityInfo;
|
|
30
|
+
/**
|
|
31
|
+
* Verifica que el code hash del nodo actual coincide con uno aprobado.
|
|
32
|
+
* @param approvedHashes Lista de hashes aprobados por governance
|
|
33
|
+
* @returns true si el hash actual está en la lista de aprobados
|
|
34
|
+
*/
|
|
35
|
+
export declare function isCodeApproved(approvedHashes: (string | null | undefined)[]): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* Computa un hash rápido del propio binario en tiempo real (fallback).
|
|
38
|
+
* Menos preciso que el hash del código fuente pero no requiere build step.
|
|
39
|
+
*/
|
|
40
|
+
export declare function computeRuntimeHash(): string;
|
|
41
|
+
/**
|
|
42
|
+
* Log de integridad al arranque — llamar desde startServer().
|
|
43
|
+
*/
|
|
44
|
+
export declare function logCodeIntegrity(): void;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* code-integrity.ts — Fix 2: Runtime code integrity verification
|
|
3
|
+
*
|
|
4
|
+
* Carga el hash computado en build-time y lo expone para:
|
|
5
|
+
* 1. Logging al arranque del nodo
|
|
6
|
+
* 2. GET /health (peers pueden verificar)
|
|
7
|
+
* 3. Comparación con hashes aprobados on-chain por governance
|
|
8
|
+
*
|
|
9
|
+
* GARANTÍA:
|
|
10
|
+
* - Si alguien modifica src/ y recompila → el hash cambia
|
|
11
|
+
* - Los peers pueden detectar que un nodo corre código diferente
|
|
12
|
+
* - El GovernanceModule puede registrar hashes aprobados on-chain
|
|
13
|
+
* - Si el hash no coincide con el aprobado → el nodo queda marcado como no confiable
|
|
14
|
+
*
|
|
15
|
+
* LIMITACIÓN (sin TEE):
|
|
16
|
+
* - Un atacante con root puede modificar dist/ directamente o falsificar el hash
|
|
17
|
+
* - La protección completa requiere TEE (Intel SGX / AMD SEV) — fase futura
|
|
18
|
+
*/
|
|
19
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
20
|
+
import { join, dirname } from "node:path";
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
import { createHash } from "node:crypto";
|
|
23
|
+
const __dir = dirname(fileURLToPath(import.meta.url));
|
|
24
|
+
let _cached = null;
|
|
25
|
+
/**
|
|
26
|
+
* Carga el code-hash.json generado en build time.
|
|
27
|
+
*/
|
|
28
|
+
export function getCodeIntegrity() {
|
|
29
|
+
if (_cached)
|
|
30
|
+
return _cached;
|
|
31
|
+
const hashFile = join(__dir, "code-hash.json");
|
|
32
|
+
if (!existsSync(hashFile)) {
|
|
33
|
+
_cached = {
|
|
34
|
+
codeHash: "unavailable",
|
|
35
|
+
codeHashHex: "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
36
|
+
computedAt: new Date().toISOString(),
|
|
37
|
+
fileCount: 0,
|
|
38
|
+
available: false,
|
|
39
|
+
};
|
|
40
|
+
return _cached;
|
|
41
|
+
}
|
|
42
|
+
try {
|
|
43
|
+
const raw = JSON.parse(readFileSync(hashFile, "utf8"));
|
|
44
|
+
_cached = {
|
|
45
|
+
codeHash: raw.codeHash ?? "unknown",
|
|
46
|
+
codeHashHex: raw.codeHashHex ?? ("0x" + raw.codeHash),
|
|
47
|
+
computedAt: raw.computedAt ?? "unknown",
|
|
48
|
+
fileCount: raw.fileCount ?? 0,
|
|
49
|
+
available: true,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
_cached = {
|
|
54
|
+
codeHash: "parse-error",
|
|
55
|
+
codeHashHex: "0x0000000000000000000000000000000000000000000000000000000000000001",
|
|
56
|
+
computedAt: new Date().toISOString(),
|
|
57
|
+
fileCount: 0,
|
|
58
|
+
available: false,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
return _cached;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Verifica que el code hash del nodo actual coincide con uno aprobado.
|
|
65
|
+
* @param approvedHashes Lista de hashes aprobados por governance
|
|
66
|
+
* @returns true si el hash actual está en la lista de aprobados
|
|
67
|
+
*/
|
|
68
|
+
export function isCodeApproved(approvedHashes) {
|
|
69
|
+
const info = getCodeIntegrity();
|
|
70
|
+
if (!info.available)
|
|
71
|
+
return false;
|
|
72
|
+
const h = info.codeHash.toLowerCase().replace("0x", "");
|
|
73
|
+
return approvedHashes.some(a => {
|
|
74
|
+
if (a == null || typeof a !== "string")
|
|
75
|
+
return false;
|
|
76
|
+
return a.toLowerCase().replace("0x", "") === h;
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Computa un hash rápido del propio binario en tiempo real (fallback).
|
|
81
|
+
* Menos preciso que el hash del código fuente pero no requiere build step.
|
|
82
|
+
*/
|
|
83
|
+
export function computeRuntimeHash() {
|
|
84
|
+
try {
|
|
85
|
+
const selfPath = join(__dir, "validator.js");
|
|
86
|
+
if (!existsSync(selfPath))
|
|
87
|
+
return "no-binary";
|
|
88
|
+
const content = readFileSync(selfPath);
|
|
89
|
+
return createHash("sha256").update(content).digest("hex");
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return "hash-error";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Log de integridad al arranque — llamar desde startServer().
|
|
97
|
+
*/
|
|
98
|
+
export function logCodeIntegrity() {
|
|
99
|
+
const info = getCodeIntegrity();
|
|
100
|
+
if (info.available) {
|
|
101
|
+
console.log(`[integrity] ✅ Code hash: ${info.codeHash.slice(0, 16)}... (${info.fileCount} files)`);
|
|
102
|
+
console.log(`[integrity] Built at: ${info.computedAt}`);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
console.warn(`[integrity] ⚠️ Code hash unavailable — run 'pnpm build' to compute`);
|
|
106
|
+
console.warn(`[integrity] Without a code hash, peers cannot verify this node's integrity`);
|
|
107
|
+
}
|
|
108
|
+
}
|
package/dist/validator.js
CHANGED
|
@@ -10,6 +10,7 @@ import { selectGossipPeers, routingStats } from "./crypto/peer-router.js";
|
|
|
10
10
|
import { NullifierConsensus, AttestationConsensus, StateSyncManager } from "./consensus/index.js";
|
|
11
11
|
import { BlockchainAnchor } from "./blockchain/blockchain-anchor.js";
|
|
12
12
|
import { SoulprintBlockchainClient, loadBlockchainConfig, } from "./blockchain/blockchain-client.js";
|
|
13
|
+
import { getCodeIntegrity, logCodeIntegrity, computeRuntimeHash } from "./code-integrity.js";
|
|
13
14
|
import { publishAttestationP2P, onAttestationReceived, getP2PStats, } from "./p2p.js";
|
|
14
15
|
// ── Config ────────────────────────────────────────────────────────────────────
|
|
15
16
|
const PORT = parseInt(process.env.SOULPRINT_PORT ?? String(PROTOCOL.DEFAULT_HTTP_PORT));
|
|
@@ -748,6 +749,28 @@ export function startValidatorNode(port = PORT) {
|
|
|
748
749
|
return handleGetReputation(res, decodeURIComponent(cleanUrl.replace("/reputation/", "")));
|
|
749
750
|
if (cleanUrl.startsWith("/nullifier/") && req.method === "GET")
|
|
750
751
|
return handleNullifierCheck(res, decodeURIComponent(cleanUrl.replace("/nullifier/", "")));
|
|
752
|
+
// ── Code integrity + health ───────────────────────────────────────────────
|
|
753
|
+
if (cleanUrl === "/health" && req.method === "GET") {
|
|
754
|
+
const integrity = getCodeIntegrity();
|
|
755
|
+
const govHash = await client?.getCurrentApprovedHash() ?? null;
|
|
756
|
+
return json(res, 200, {
|
|
757
|
+
status: "ok",
|
|
758
|
+
version: VERSION,
|
|
759
|
+
protocolHash: PROTOCOL_HASH,
|
|
760
|
+
codeHash: integrity.codeHash,
|
|
761
|
+
codeHashHex: integrity.codeHashHex,
|
|
762
|
+
codeHashAvailable: integrity.available,
|
|
763
|
+
codeBuiltAt: integrity.computedAt,
|
|
764
|
+
codeFileCount: integrity.fileCount,
|
|
765
|
+
runtimeHash: computeRuntimeHash(),
|
|
766
|
+
governanceApprovedHash: govHash,
|
|
767
|
+
blockchainConnected: !!client?.isConnected,
|
|
768
|
+
nodeCompatible: !govHash ||
|
|
769
|
+
govHash.toLowerCase() === ("0x" + PROTOCOL_HASH).toLowerCase(),
|
|
770
|
+
uptime: process.uptime(),
|
|
771
|
+
ts: Date.now(),
|
|
772
|
+
});
|
|
773
|
+
}
|
|
751
774
|
// ── Blockchain anchor status ──────────────────────────────────────────────
|
|
752
775
|
if (cleanUrl === "/anchor/stats" && req.method === "GET") {
|
|
753
776
|
return json(res, 200, anchor.getStats());
|
|
@@ -890,6 +913,7 @@ export function startValidatorNode(port = PORT) {
|
|
|
890
913
|
json(res, 404, { error: "Not found" });
|
|
891
914
|
});
|
|
892
915
|
server.listen(port, () => {
|
|
916
|
+
logCodeIntegrity();
|
|
893
917
|
console.log(`\n🌐 Soulprint Validator Node v${VERSION}`);
|
|
894
918
|
console.log(` Node DID: ${nodeKeypair.did}`);
|
|
895
919
|
console.log(` Listening: http://0.0.0.0:${port}`);
|
|
@@ -900,6 +924,7 @@ export function startValidatorNode(port = PORT) {
|
|
|
900
924
|
console.log(` Known peers: ${peers.length}`);
|
|
901
925
|
console.log(`\n Core endpoints:`);
|
|
902
926
|
console.log(` POST /verify verify ZK proof + co-sign`);
|
|
927
|
+
console.log(` GET /health code integrity + governance status`);
|
|
903
928
|
console.log(` GET /info node info`);
|
|
904
929
|
console.log(` GET /protocol protocol constants (immutable)`);
|
|
905
930
|
console.log(` GET /nullifier/:n anti-sybil check`);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "soulprint-network",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "Soulprint validator node
|
|
3
|
+
"version": "0.3.5",
|
|
4
|
+
"description": "Soulprint validator node \u2014 HTTP server that verifies ZK proofs, co-signs SPTs, anti-Sybil registry",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"bin": {
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
"README.md"
|
|
13
13
|
],
|
|
14
14
|
"scripts": {
|
|
15
|
-
"build": "tsc",
|
|
16
|
-
"start": "node dist/server.js"
|
|
15
|
+
"build": "tsc && node scripts/compute-code-hash.mjs",
|
|
16
|
+
"start": "node dist/server.js",
|
|
17
|
+
"build:hash": "node scripts/compute-code-hash.mjs"
|
|
17
18
|
},
|
|
18
19
|
"publishConfig": {
|
|
19
20
|
"access": "public"
|
|
@@ -67,4 +68,4 @@
|
|
|
67
68
|
"types": "./dist/index.d.ts"
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
|
-
}
|
|
71
|
+
}
|