soulprint-network 0.5.1 → 0.6.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/dist/code-hash.json +3 -3
- package/dist/index.d.ts +1 -3
- package/dist/index.js +1 -2
- package/dist/p2p.d.ts +25 -30
- package/dist/p2p.js +20 -139
- package/dist/server.js +61 -114
- package/dist/validator.d.ts +0 -8
- package/dist/validator.js +29 -91
- package/package.json +4 -17
package/dist/code-hash.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"codeHash": "
|
|
3
|
-
"codeHashHex": "
|
|
4
|
-
"computedAt": "2026-03-01T03:
|
|
2
|
+
"codeHash": "3e63098b6a2421a17bad6792cbc231982327b32c2e77d0ff2be0eda630690f8c",
|
|
3
|
+
"codeHashHex": "0x3e63098b6a2421a17bad6792cbc231982327b32c2e77d0ff2be0eda630690f8c",
|
|
4
|
+
"computedAt": "2026-03-01T03:57:58.085Z",
|
|
5
5
|
"fileCount": 25,
|
|
6
6
|
"files": [
|
|
7
7
|
"blockchain/NullifierRegistryClient.ts",
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
export { startValidatorNode,
|
|
1
|
+
export { startValidatorNode, submitToNode, attestBot, getBotReputation, getNodeInfo, BOOTSTRAP_NODES, } from "./validator.js";
|
|
2
2
|
export type { NodeVerifyResult } from "./validator.js";
|
|
3
|
-
export { createSoulprintP2PNode, publishAttestationP2P, onAttestationReceived, getP2PStats, stopP2PNode, TOPIC_ATTESTATIONS, TOPIC_NULLIFIERS, MAINNET_BOOTSTRAP, } from "./p2p.js";
|
|
4
|
-
export type { P2PConfig, P2PStats, SoulprintP2PNode } from "./p2p.js";
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1 @@
|
|
|
1
|
-
export { startValidatorNode,
|
|
2
|
-
export { createSoulprintP2PNode, publishAttestationP2P, onAttestationReceived, getP2PStats, stopP2PNode, TOPIC_ATTESTATIONS, TOPIC_NULLIFIERS, MAINNET_BOOTSTRAP, } from "./p2p.js";
|
|
1
|
+
export { startValidatorNode, submitToNode, attestBot, getBotReputation, getNodeInfo, BOOTSTRAP_NODES, } from "./validator.js";
|
package/dist/p2p.d.ts
CHANGED
|
@@ -1,38 +1,33 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Noise encryption
|
|
7
|
-
* - Yamux multiplexing
|
|
8
|
-
* - Kademlia DHT (peer discovery internet)
|
|
9
|
-
* - GossipSub (attestation pub/sub)
|
|
10
|
-
* - mDNS (descubrimiento en LAN)
|
|
11
|
-
* - Bootstrap nodes (entry points a la red)
|
|
2
|
+
* p2p.ts — stub (v0.6.0)
|
|
3
|
+
* libp2p removed. The blockchain IS the network.
|
|
4
|
+
* This file exists only for backward compatibility with any external imports.
|
|
5
|
+
* @deprecated Use PeerRegistryClient for peer discovery.
|
|
12
6
|
*/
|
|
13
|
-
import type { Libp2p } from "libp2p";
|
|
14
|
-
import type { BotAttestation } from "soulprint-core";
|
|
15
7
|
export declare const TOPIC_ATTESTATIONS = "soulprint:attestations:v1";
|
|
16
8
|
export declare const TOPIC_NULLIFIERS = "soulprint:nullifiers:v1";
|
|
17
9
|
export declare const MAINNET_BOOTSTRAP: string[];
|
|
18
|
-
export
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
localOnly: boolean;
|
|
25
|
-
}
|
|
26
|
-
export interface P2PStats {
|
|
10
|
+
export type P2PConfig = {
|
|
11
|
+
port?: number;
|
|
12
|
+
bootstraps?: string[];
|
|
13
|
+
localOnly?: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type P2PStats = {
|
|
27
16
|
peerId: string;
|
|
28
17
|
peers: number;
|
|
29
|
-
multiaddrs: string[];
|
|
30
18
|
pubsubPeers: number;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
export
|
|
34
|
-
|
|
35
|
-
export declare function
|
|
36
|
-
|
|
37
|
-
export declare function
|
|
38
|
-
|
|
19
|
+
multiaddrs: string[];
|
|
20
|
+
};
|
|
21
|
+
export type SoulprintP2PNode = never;
|
|
22
|
+
/** @deprecated No-op stub */
|
|
23
|
+
export declare function createSoulprintP2PNode(_cfg?: P2PConfig): Promise<never>;
|
|
24
|
+
/** @deprecated No-op stub */
|
|
25
|
+
export declare function publishAttestationP2P(_node: any, _att: any): Promise<number>;
|
|
26
|
+
/** @deprecated No-op stub */
|
|
27
|
+
export declare function onAttestationReceived(_node: any, _cb: any): void;
|
|
28
|
+
/** @deprecated No-op stub */
|
|
29
|
+
export declare function getP2PStats(_node: any): P2PStats;
|
|
30
|
+
/** @deprecated No-op stub */
|
|
31
|
+
export declare function stopP2PNode(_node: any): Promise<void>;
|
|
32
|
+
/** @deprecated No-op stub */
|
|
33
|
+
export declare function dialP2PPeer(_node: any, _ma: string): Promise<boolean>;
|
package/dist/p2p.js
CHANGED
|
@@ -1,148 +1,29 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* - Noise encryption
|
|
7
|
-
* - Yamux multiplexing
|
|
8
|
-
* - Kademlia DHT (peer discovery internet)
|
|
9
|
-
* - GossipSub (attestation pub/sub)
|
|
10
|
-
* - mDNS (descubrimiento en LAN)
|
|
11
|
-
* - Bootstrap nodes (entry points a la red)
|
|
2
|
+
* p2p.ts — stub (v0.6.0)
|
|
3
|
+
* libp2p removed. The blockchain IS the network.
|
|
4
|
+
* This file exists only for backward compatibility with any external imports.
|
|
5
|
+
* @deprecated Use PeerRegistryClient for peer discovery.
|
|
12
6
|
*/
|
|
13
|
-
import { createLibp2p } from "libp2p";
|
|
14
|
-
import { tcp } from "@libp2p/tcp";
|
|
15
|
-
import { noise } from "@chainsafe/libp2p-noise";
|
|
16
|
-
import { yamux } from "@chainsafe/libp2p-yamux";
|
|
17
|
-
import { kadDHT } from "@libp2p/kad-dht";
|
|
18
|
-
import { gossipsub } from "@chainsafe/libp2p-gossipsub";
|
|
19
|
-
import { mdns } from "@libp2p/mdns";
|
|
20
|
-
import { bootstrap } from "@libp2p/bootstrap";
|
|
21
|
-
import { identify } from "@libp2p/identify";
|
|
22
|
-
import { ping } from "@libp2p/ping";
|
|
23
|
-
import { fromString, toString } from "uint8arrays";
|
|
24
|
-
// ─── Topics ──────────────────────────────────────────────────────────────────
|
|
25
7
|
export const TOPIC_ATTESTATIONS = "soulprint:attestations:v1";
|
|
26
8
|
export const TOPIC_NULLIFIERS = "soulprint:nullifiers:v1";
|
|
27
|
-
// ─── Bootstrap nodes públicos (mainnet) ──────────────────────────────────────
|
|
28
|
-
// Vacíos hasta que desplegamos nodos públicos.
|
|
29
|
-
// Se pueden pasar vía SOULPRINT_BOOTSTRAP=multiaddr1,multiaddr2
|
|
30
9
|
export const MAINNET_BOOTSTRAP = [];
|
|
31
|
-
|
|
32
|
-
export async function createSoulprintP2PNode(
|
|
33
|
-
|
|
34
|
-
const peerDiscovery = [mdns()];
|
|
35
|
-
if (config.bootstraps.length > 0) {
|
|
36
|
-
peerDiscovery.push(bootstrap({ list: config.bootstraps }));
|
|
37
|
-
}
|
|
38
|
-
const node = await createLibp2p({
|
|
39
|
-
addresses: {
|
|
40
|
-
listen: [`/ip4/0.0.0.0/tcp/${config.port}`],
|
|
41
|
-
},
|
|
42
|
-
transports: [tcp()],
|
|
43
|
-
connectionEncrypters: [noise()],
|
|
44
|
-
streamMuxers: [yamux()],
|
|
45
|
-
peerDiscovery,
|
|
46
|
-
services: {
|
|
47
|
-
// Kademlia DHT — descubrimiento de peers en internet
|
|
48
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
49
|
-
dht: kadDHT({ clientMode: false }),
|
|
50
|
-
// GossipSub — broadcast de attestations
|
|
51
|
-
pubsub: gossipsub({
|
|
52
|
-
allowPublishToZeroTopicPeers: true, // publica aunque no haya peers aún
|
|
53
|
-
emitSelf: false,
|
|
54
|
-
// Thresholds permisivos para redes pequeñas
|
|
55
|
-
scoreThresholds: {
|
|
56
|
-
gossipThreshold: -Infinity,
|
|
57
|
-
publishThreshold: -Infinity,
|
|
58
|
-
graylistThreshold: -Infinity,
|
|
59
|
-
},
|
|
60
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
-
}),
|
|
62
|
-
// Identify — intercambio de metadatos entre peers
|
|
63
|
-
identify: identify(),
|
|
64
|
-
// Ping — requerido por KadDHT para health checks
|
|
65
|
-
ping: ping(),
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
await node.start();
|
|
69
|
-
// Suscribirse a los topics de Soulprint
|
|
70
|
-
node.services.pubsub.subscribe(TOPIC_ATTESTATIONS);
|
|
71
|
-
node.services.pubsub.subscribe(TOPIC_NULLIFIERS);
|
|
72
|
-
return node;
|
|
10
|
+
/** @deprecated No-op stub */
|
|
11
|
+
export async function createSoulprintP2PNode(_cfg) {
|
|
12
|
+
throw new Error("libp2p removed in v0.6.0 — use blockchain peer discovery");
|
|
73
13
|
}
|
|
74
|
-
|
|
75
|
-
export async function publishAttestationP2P(
|
|
76
|
-
|
|
77
|
-
const data = fromString(JSON.stringify(att), "utf8");
|
|
78
|
-
const result = await node.services.pubsub.publish(TOPIC_ATTESTATIONS, data);
|
|
79
|
-
return result?.recipients?.length ?? 0;
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
return 0; // sin peers aún — no es error
|
|
83
|
-
}
|
|
14
|
+
/** @deprecated No-op stub */
|
|
15
|
+
export async function publishAttestationP2P(_node, _att) {
|
|
16
|
+
return 0;
|
|
84
17
|
}
|
|
85
|
-
|
|
86
|
-
export function onAttestationReceived(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
const att = JSON.parse(toString(evt.detail.data, "utf8"));
|
|
92
|
-
const fromPeer = evt.detail?.from?.toString() ?? "unknown";
|
|
93
|
-
handler(att, fromPeer);
|
|
94
|
-
}
|
|
95
|
-
catch {
|
|
96
|
-
// mensaje malformado — ignorar
|
|
97
|
-
}
|
|
98
|
-
});
|
|
18
|
+
/** @deprecated No-op stub */
|
|
19
|
+
export function onAttestationReceived(_node, _cb) { }
|
|
20
|
+
/** @deprecated No-op stub */
|
|
21
|
+
export function getP2PStats(_node) {
|
|
22
|
+
return { peerId: "", peers: 0, pubsubPeers: 0, multiaddrs: [] };
|
|
99
23
|
}
|
|
100
|
-
|
|
101
|
-
export function
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
peers: node.getPeers().length,
|
|
106
|
-
multiaddrs: node.getMultiaddrs().map((m) => m.toString()),
|
|
107
|
-
pubsubPeers: pubsub.getPeers?.()?.length ?? 0,
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
// ─── Graceful shutdown ────────────────────────────────────────────────────────
|
|
111
|
-
export async function stopP2PNode(node) {
|
|
112
|
-
try {
|
|
113
|
-
await node.stop();
|
|
114
|
-
}
|
|
115
|
-
catch { /* ignorar */ }
|
|
116
|
-
}
|
|
117
|
-
// ── dial a peer by multiaddr string (best-effort — HTTP gossip is the primary mechanism) ──
|
|
118
|
-
// Note: in WSL2/NAT environments mDNS multicast doesn't work.
|
|
119
|
-
// This function attempts libp2p TCP dial using the peer's advertised multiaddrs.
|
|
120
|
-
// Falls back gracefully — HTTP gossip layer (known_peers) handles message propagation.
|
|
121
|
-
export async function dialP2PPeer(node, maddrStr, timeoutMs = 5_000) {
|
|
122
|
-
try {
|
|
123
|
-
// Extract PeerId from the multiaddr string and add multiaddr to peer store
|
|
124
|
-
// so libp2p can use its own internal Multiaddr class (avoids version mismatch)
|
|
125
|
-
const peerIdStr = maddrStr.split("/p2p/")[1];
|
|
126
|
-
if (!peerIdStr)
|
|
127
|
-
return false;
|
|
128
|
-
// Use peer store to register the address, then dial by PeerId
|
|
129
|
-
// This lets libp2p use its own Multiaddr parsing internally
|
|
130
|
-
const { peerIdFromString } = await import("@libp2p/peer-id");
|
|
131
|
-
const peerId = peerIdFromString(peerIdStr);
|
|
132
|
-
// Register the address in the peer store
|
|
133
|
-
await node.peerStore.merge(peerId, {
|
|
134
|
-
multiaddrs: [maddrStr],
|
|
135
|
-
}).catch(() => {
|
|
136
|
-
// peerStore.merge signature varies by libp2p version — try patch
|
|
137
|
-
return node.peerStore.patch(peerId, { multiaddrs: [maddrStr] }).catch(() => { });
|
|
138
|
-
});
|
|
139
|
-
await Promise.race([
|
|
140
|
-
node.dial(peerId),
|
|
141
|
-
new Promise((_, rej) => setTimeout(() => rej(new Error("dial timeout")), timeoutMs)),
|
|
142
|
-
]);
|
|
143
|
-
return true;
|
|
144
|
-
}
|
|
145
|
-
catch {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
24
|
+
/** @deprecated No-op stub */
|
|
25
|
+
export async function stopP2PNode(_node) { }
|
|
26
|
+
/** @deprecated No-op stub */
|
|
27
|
+
export async function dialP2PPeer(_node, _ma) {
|
|
28
|
+
return false;
|
|
148
29
|
}
|
package/dist/server.js
CHANGED
|
@@ -1,129 +1,77 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* Soulprint Validator Node — entrypoint
|
|
3
|
+
* Soulprint Validator Node — entrypoint v0.6.0
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* 2. libp2p P2P node (port 6888) — Kademlia DHT + GossipSub + mDNS
|
|
5
|
+
* Pure blockchain architecture — no libp2p.
|
|
6
|
+
* The blockchain IS the network.
|
|
8
7
|
*/
|
|
9
|
-
import { startValidatorNode,
|
|
8
|
+
import { startValidatorNode, setPeerRegistryClient, setNullifierRegistry, setReputationRegistry, getNodeState, setLastSyncTs } from "./validator.js";
|
|
10
9
|
import { computeHash, saveState } from "./state/StateStore.js";
|
|
11
|
-
import { createSoulprintP2PNode, MAINNET_BOOTSTRAP, stopP2PNode } from "./p2p.js";
|
|
12
10
|
import { PeerRegistryClient } from "./blockchain/PeerRegistryClient.js";
|
|
11
|
+
import { NullifierRegistryClient } from "./blockchain/NullifierRegistryClient.js";
|
|
12
|
+
import { ReputationRegistryClient } from "./blockchain/ReputationRegistryClient.js";
|
|
13
13
|
// ─── Config ──────────────────────────────────────────────────────────────────
|
|
14
|
-
const HTTP_PORT = parseInt(process.env.SOULPRINT_PORT ?? "4888");
|
|
15
|
-
const
|
|
14
|
+
const HTTP_PORT = parseInt(process.env.PORT ?? process.env.SOULPRINT_PORT ?? "4888");
|
|
15
|
+
const adminPrivateKey = process.env.ADMIN_PRIVATE_KEY;
|
|
16
|
+
const adminToken = process.env.ADMIN_TOKEN;
|
|
16
17
|
globalThis._startTime = Date.now();
|
|
17
|
-
//
|
|
18
|
-
const bootstrapEnv = (process.env.SOULPRINT_BOOTSTRAP ?? "")
|
|
19
|
-
.split(",")
|
|
20
|
-
.map(s => s.trim())
|
|
21
|
-
.filter(Boolean);
|
|
22
|
-
const bootstraps = bootstrapEnv.length > 0 ? bootstrapEnv : MAINNET_BOOTSTRAP;
|
|
23
|
-
const localOnly = bootstraps.length === 0;
|
|
24
|
-
// ─── Arranque ────────────────────────────────────────────────────────────────
|
|
18
|
+
// ─── Start HTTP server ────────────────────────────────────────────────────────
|
|
25
19
|
const httpServer = startValidatorNode(HTTP_PORT);
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
console.log(` Modo: ${localOnly ? "local (mDNS only)" : `red principal + ${bootstraps.length} bootstrap(s)`}`);
|
|
31
|
-
p2pNode = await createSoulprintP2PNode({ port: P2P_PORT, bootstraps, localOnly });
|
|
32
|
-
setP2PNode(p2pNode);
|
|
33
|
-
console.log(`✅ P2P activo`);
|
|
34
|
-
console.log(` Peer ID: ${p2pNode.peerId.toString()}`);
|
|
35
|
-
console.log(` Multiaddrs: ${p2pNode.getMultiaddrs().map((m) => m.toString()).join(", ") || "(pendiente)"}`);
|
|
36
|
-
console.log(`\n Gossip: HTTP fallback + GossipSub P2P`);
|
|
37
|
-
console.log(` Discovery: mDNS${bootstraps.length > 0 ? " + DHT + Bootstrap" : " (LAN only)"}\n`);
|
|
38
|
-
if (localOnly) {
|
|
39
|
-
console.log(` 💡 Para conectarte a la red principal, configura SOULPRINT_BOOTSTRAP`);
|
|
40
|
-
console.log(` con multiaddrs de nodos conocidos y reinicia.\n`);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
catch (err) {
|
|
44
|
-
console.warn(`⚠️ P2P no disponible — solo HTTP gossip activo`);
|
|
45
|
-
console.warn(` Error: ${err?.message ?? String(err)}\n`);
|
|
46
|
-
}
|
|
47
|
-
// ─── On-chain PeerRegistry (auto-registro + bootstrap desde chain) ────────────
|
|
48
|
-
const adminPrivateKey = process.env.ADMIN_PRIVATE_KEY;
|
|
49
|
-
const peerRegistry = new PeerRegistryClient({
|
|
50
|
-
privateKey: adminPrivateKey,
|
|
51
|
-
});
|
|
20
|
+
// ─── Init blockchain clients ──────────────────────────────────────────────────
|
|
21
|
+
const peerRegistry = new PeerRegistryClient({ privateKey: adminPrivateKey });
|
|
22
|
+
const nullifierRegistry = new NullifierRegistryClient({ privateKey: adminPrivateKey });
|
|
23
|
+
const reputationRegistry = new ReputationRegistryClient({ privateKey: adminPrivateKey });
|
|
52
24
|
setPeerRegistryClient(peerRegistry);
|
|
53
|
-
|
|
25
|
+
setNullifierRegistry(nullifierRegistry);
|
|
26
|
+
setReputationRegistry(reputationRegistry);
|
|
27
|
+
// ─── Bootstrap: read on-chain peers, register self ────────────────────────────
|
|
54
28
|
setTimeout(async () => {
|
|
55
29
|
try {
|
|
56
|
-
// 1. Leer peers on-chain y hacer dial P2P a sus multiaddrs
|
|
57
30
|
const chainPeers = await peerRegistry.getAllPeers().catch(() => []);
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
body: JSON.stringify({ url: httpUrl, protocol_hash: PROTOCOL_HASH }),
|
|
74
|
-
signal: AbortSignal.timeout(5_000),
|
|
75
|
-
}).catch(() => null);
|
|
76
|
-
}
|
|
77
|
-
console.log(`[peer-registry] ✅ Peer P2P registrado: ${peer.multiaddr.slice(0, 40)}`);
|
|
78
|
-
}
|
|
79
|
-
else if (peer.multiaddr.startsWith("http")) {
|
|
80
|
-
// HTTP peer — registrar vía /peers/register
|
|
81
|
-
const PROTOCOL_HASH = "dfe1ccca1270ec86f93308dc4b981bab1d6bd74bdcc334059f4380b407ca07ca";
|
|
82
|
-
await fetch(`http://localhost:${HTTP_PORT}/peers/register`, {
|
|
83
|
-
method: "POST",
|
|
84
|
-
headers: { "Content-Type": "application/json" },
|
|
85
|
-
body: JSON.stringify({ url: peer.multiaddr, protocol_hash: PROTOCOL_HASH }),
|
|
86
|
-
signal: AbortSignal.timeout(5_000),
|
|
87
|
-
}).catch(() => null);
|
|
88
|
-
console.log(`[peer-registry] 🌐 Peer HTTP registrado: ${peer.multiaddr}`);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
catch (e) {
|
|
92
|
-
console.warn(`[peer-registry] ⚠️ No se pudo conectar a ${peer.multiaddr}: ${e.message}`);
|
|
31
|
+
console.log(`[peer-registry] ${chainPeers.length} peer(s) on-chain`);
|
|
32
|
+
// Register HTTP peers
|
|
33
|
+
for (const peer of chainPeers) {
|
|
34
|
+
try {
|
|
35
|
+
if (!peer.multiaddr)
|
|
36
|
+
continue;
|
|
37
|
+
if (peer.multiaddr.startsWith("http")) {
|
|
38
|
+
const PROTOCOL_HASH = "dfe1ccca1270ec86f93308dc4b981bab1d6bd74bdcc334059f4380b407ca07ca";
|
|
39
|
+
await fetch(`http://localhost:${HTTP_PORT}/peers/register`, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: { "Content-Type": "application/json" },
|
|
42
|
+
body: JSON.stringify({ url: peer.multiaddr, protocol_hash: PROTOCOL_HASH }),
|
|
43
|
+
signal: AbortSignal.timeout(5_000),
|
|
44
|
+
}).catch(() => null);
|
|
45
|
+
console.log(`[peer-registry] 🌐 Peer HTTP registrado: ${peer.multiaddr}`);
|
|
93
46
|
}
|
|
94
47
|
}
|
|
48
|
+
catch (e) {
|
|
49
|
+
console.warn(`[peer-registry] ⚠️ No se pudo conectar a ${peer.multiaddr}: ${e.message}`);
|
|
50
|
+
}
|
|
95
51
|
}
|
|
96
|
-
|
|
97
|
-
console.log("[peer-registry] ℹ️ No hay peers registrados on-chain aún — primer nodo de la red");
|
|
98
|
-
}
|
|
99
|
-
// 2. Registrar self on-chain
|
|
52
|
+
// Register self on-chain
|
|
100
53
|
if (!adminPrivateKey) {
|
|
101
54
|
console.warn("[peer-registry] ⚠️ ADMIN_PRIVATE_KEY not set — skipping on-chain registration");
|
|
102
55
|
return;
|
|
103
56
|
}
|
|
104
|
-
let multiaddr = `http://localhost:${HTTP_PORT}`;
|
|
105
|
-
if (p2pNode) {
|
|
106
|
-
const addrs = p2pNode.getMultiaddrs().map((m) => m.toString());
|
|
107
|
-
const publicAddr = addrs.find(a => !a.includes("127.0.0.1") && !a.includes("/ip4/0.0.0.0"));
|
|
108
|
-
multiaddr = publicAddr ?? addrs[0] ?? multiaddr;
|
|
109
|
-
}
|
|
110
57
|
const nodeDid = globalThis._nodeDid ?? `did:soulprint:node:${Date.now()}`;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
58
|
+
await peerRegistry.registerSelf({
|
|
59
|
+
peerDid: nodeDid,
|
|
60
|
+
peerId: "",
|
|
61
|
+
multiaddr: `http://localhost:${HTTP_PORT}`,
|
|
62
|
+
score: 0,
|
|
63
|
+
});
|
|
64
|
+
console.log(`[peer-registry] ✅ Registered on-chain: ${nodeDid.slice(0, 30)}…`);
|
|
114
65
|
}
|
|
115
66
|
catch (e) {
|
|
116
|
-
console.warn(`[peer-registry] ⚠️
|
|
67
|
+
console.warn(`[peer-registry] ⚠️ Bootstrap error: ${e.message}`);
|
|
117
68
|
}
|
|
118
69
|
}, 3_000);
|
|
119
|
-
// ─── HTTP Bootstrap Peers
|
|
120
|
-
// SOULPRINT_BOOTSTRAP_HTTP=http://node1:4888,http://node2:4888
|
|
121
|
-
// Registra peers HTTP automáticamente al arrancar (útil en WSL2 / Docker / cloud)
|
|
70
|
+
// ─── HTTP Bootstrap Peers ─────────────────────────────────────────────────────
|
|
122
71
|
const httpBootstraps = (process.env.SOULPRINT_BOOTSTRAP_HTTP ?? "")
|
|
123
72
|
.split(",").map(s => s.trim()).filter(s => s.startsWith("http"));
|
|
124
73
|
if (httpBootstraps.length > 0) {
|
|
125
74
|
console.log(`🔗 Bootstrap HTTP: ${httpBootstraps.length} peer(s) configurados`);
|
|
126
|
-
// Esperar 2s a que el HTTP server esté listo antes de registrar
|
|
127
75
|
setTimeout(async () => {
|
|
128
76
|
const PROTOCOL_HASH = process.env.SOULPRINT_PROTOCOL_HASH
|
|
129
77
|
?? "dfe1ccca1270ec86f93308dc4b981bab1d6bd74bdcc334059f4380b407ca07ca";
|
|
@@ -149,9 +97,19 @@ if (httpBootstraps.length > 0) {
|
|
|
149
97
|
}
|
|
150
98
|
}, 2_000);
|
|
151
99
|
}
|
|
152
|
-
// ───
|
|
153
|
-
|
|
154
|
-
|
|
100
|
+
// ─── HTTP peer sync every 5 min (simple HTTP gossip — no libp2p) ─────────────
|
|
101
|
+
setInterval(async () => {
|
|
102
|
+
try {
|
|
103
|
+
const peers = await peerRegistry.getAllPeers().catch(() => []);
|
|
104
|
+
for (const peer of peers) {
|
|
105
|
+
if (peer.multiaddr?.startsWith("http")) {
|
|
106
|
+
await fetch(`${peer.multiaddr}/state/hash`, { signal: AbortSignal.timeout(3_000) }).catch(() => null);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
catch { }
|
|
111
|
+
}, 5 * 60 * 1000);
|
|
112
|
+
// ─── Anti-entropy sync loop ───────────────────────────────────────────────────
|
|
155
113
|
setInterval(async () => {
|
|
156
114
|
const { nullifiers, repStore, peers: knownPeers } = getNodeState();
|
|
157
115
|
if (knownPeers.length === 0)
|
|
@@ -163,17 +121,11 @@ setInterval(async () => {
|
|
|
163
121
|
if (!hashRes.ok)
|
|
164
122
|
continue;
|
|
165
123
|
const hashData = await hashRes.json();
|
|
166
|
-
|
|
167
|
-
if (peerHash === localHash) {
|
|
168
|
-
console.log(`[sync] peer ${peerUrl}: hash match ✅`);
|
|
124
|
+
if (hashData.hash === localHash)
|
|
169
125
|
continue;
|
|
170
|
-
}
|
|
171
|
-
// Hashes differ — fetch full state and merge
|
|
172
126
|
const exportRes = await fetch(`${peerUrl}/state/export`, { signal: AbortSignal.timeout(10_000) });
|
|
173
|
-
if (!exportRes.ok)
|
|
174
|
-
console.warn(`[sync] peer ${peerUrl}: export failed (${exportRes.status})`);
|
|
127
|
+
if (!exportRes.ok)
|
|
175
128
|
continue;
|
|
176
|
-
}
|
|
177
129
|
const peerState = await exportRes.json();
|
|
178
130
|
const mergeRes = await fetch(`http://localhost:${HTTP_PORT}/state/merge`, {
|
|
179
131
|
method: "POST",
|
|
@@ -182,8 +134,7 @@ setInterval(async () => {
|
|
|
182
134
|
signal: AbortSignal.timeout(5_000),
|
|
183
135
|
});
|
|
184
136
|
const merged = await mergeRes.json();
|
|
185
|
-
console.log(`[sync] peer ${peerUrl}:
|
|
186
|
-
// Persist updated state
|
|
137
|
+
console.log(`[sync] peer ${peerUrl}: merged ${merged.new_nullifiers ?? 0} nullifiers, ${merged.new_attestations ?? 0} attestations`);
|
|
187
138
|
const { nullifiers: n2, repStore: r2, peers: p2 } = getNodeState();
|
|
188
139
|
const ts = Date.now();
|
|
189
140
|
saveState({
|
|
@@ -204,10 +155,6 @@ setInterval(async () => {
|
|
|
204
155
|
// ─── Graceful shutdown ────────────────────────────────────────────────────────
|
|
205
156
|
async function shutdown(signal) {
|
|
206
157
|
console.log(`\n${signal} recibido — cerrando...`);
|
|
207
|
-
if (p2pNode) {
|
|
208
|
-
await stopP2PNode(p2pNode);
|
|
209
|
-
console.log(" ✓ Nodo P2P cerrado");
|
|
210
|
-
}
|
|
211
158
|
httpServer.close(() => {
|
|
212
159
|
console.log(" ✓ HTTP server cerrado");
|
|
213
160
|
process.exit(0);
|
package/dist/validator.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { IncomingMessage, ServerResponse } from "node:http";
|
|
2
2
|
import { BotAttestation, BotReputation } from "soulprint-core";
|
|
3
|
-
import { type SoulprintP2PNode } from "./p2p.js";
|
|
4
3
|
import { PeerRegistryClient } from "./blockchain/PeerRegistryClient.js";
|
|
5
4
|
import { NullifierRegistryClient } from "./blockchain/NullifierRegistryClient.js";
|
|
6
5
|
import { ReputationRegistryClient } from "./blockchain/ReputationRegistryClient.js";
|
|
@@ -20,13 +19,6 @@ export declare function setNullifierRegistry(client: NullifierRegistryClient): v
|
|
|
20
19
|
export declare function setReputationRegistry(client: ReputationRegistryClient): void;
|
|
21
20
|
export declare function getNullifierRegistry(): NullifierRegistryClient | null;
|
|
22
21
|
export declare function getReputationRegistry(): ReputationRegistryClient | null;
|
|
23
|
-
/**
|
|
24
|
-
* Inyecta el nodo libp2p al validador.
|
|
25
|
-
* Cuando se llama:
|
|
26
|
-
* 1. Se registra el handler de attestations entrantes por GossipSub
|
|
27
|
-
* 2. Desde ese momento, gossipAttestation() también publica por P2P
|
|
28
|
-
*/
|
|
29
|
-
export declare function setP2PNode(node: SoulprintP2PNode): void;
|
|
30
22
|
/**
|
|
31
23
|
* Per-DID reputation: score (0-20) + attestation history.
|
|
32
24
|
* Persisted to disk - survives node restarts.
|
package/dist/validator.js
CHANGED
|
@@ -16,7 +16,6 @@ import { SoulprintBlockchainClient, loadBlockchainConfig, } from "./blockchain/b
|
|
|
16
16
|
import { thresholdsClient, PROTOCOL_THRESHOLDS_ADDRESS, PROTOCOL_THRESHOLDS_CHAIN, } from "./blockchain/protocol-thresholds-client.js";
|
|
17
17
|
import { getCodeIntegrity, logCodeIntegrity, computeRuntimeHash } from "./code-integrity.js";
|
|
18
18
|
import { getMCPEntry, getVerifiedMCPEntries, getAllMCPEntries, getRegistryInfo, verifyMCPOnChain, revokeMCPOnChain, registerMCPOnChain, } from "./mcp-registry-client.js";
|
|
19
|
-
import { publishAttestationP2P, onAttestationReceived, getP2PStats, dialP2PPeer, } from "./p2p.js";
|
|
20
19
|
// ── Config ────────────────────────────────────────────────────────────────────
|
|
21
20
|
const PORT = parseInt(process.env.SOULPRINT_PORT ?? String(PROTOCOL.DEFAULT_HTTP_PORT));
|
|
22
21
|
const NODE_DIR = join(homedir(), ".soulprint", "node");
|
|
@@ -25,7 +24,7 @@ const NULLIFIER_DB = join(NODE_DIR, "nullifiers.json");
|
|
|
25
24
|
const REPUTE_DB = join(NODE_DIR, "reputation.json");
|
|
26
25
|
const PEERS_DB = join(NODE_DIR, "peers.json");
|
|
27
26
|
const AUDIT_DB = join(NODE_DIR, "audit.json");
|
|
28
|
-
const VERSION = "0.
|
|
27
|
+
const VERSION = "0.6.0";
|
|
29
28
|
const MAX_BODY_BYTES = 64 * 1024;
|
|
30
29
|
// ── Protocol constants (inamovibles - no cambiar directamente aquí) ───────────
|
|
31
30
|
const RATE_LIMIT_MS = PROTOCOL.RATE_LIMIT_WINDOW_MS;
|
|
@@ -70,8 +69,6 @@ async function refreshThresholds() {
|
|
|
70
69
|
}
|
|
71
70
|
catch { /* usa los valores actuales */ }
|
|
72
71
|
}
|
|
73
|
-
// ── P2P Node (Phase 5) ────────────────────────────────────────────────────────
|
|
74
|
-
let p2pNode = null;
|
|
75
72
|
// ── PeerRegistry Client ───────────────────────────────────────────────────────
|
|
76
73
|
let peerRegistryClient = null;
|
|
77
74
|
export function setPeerRegistryClient(client) {
|
|
@@ -94,27 +91,6 @@ export function getNullifierRegistry() {
|
|
|
94
91
|
export function getReputationRegistry() {
|
|
95
92
|
return reputationRegistry;
|
|
96
93
|
}
|
|
97
|
-
/**
|
|
98
|
-
* Inyecta el nodo libp2p al validador.
|
|
99
|
-
* Cuando se llama:
|
|
100
|
-
* 1. Se registra el handler de attestations entrantes por GossipSub
|
|
101
|
-
* 2. Desde ese momento, gossipAttestation() también publica por P2P
|
|
102
|
-
*/
|
|
103
|
-
export function setP2PNode(node) {
|
|
104
|
-
p2pNode = node;
|
|
105
|
-
// Recibir attestations de otros nodos via GossipSub
|
|
106
|
-
onAttestationReceived(node, (att, fromPeer) => {
|
|
107
|
-
// Validar firma antes de aplicar
|
|
108
|
-
if (!verifyAttestation(att)) {
|
|
109
|
-
console.warn(`[p2p] Attestation inválida de peer ${fromPeer.slice(0, 16)}... - descartada`);
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
// Anti-replay ya está dentro de applyAttestation()
|
|
113
|
-
applyAttestation(att);
|
|
114
|
-
console.log(`[p2p] Attestation recibida de peer ${fromPeer.slice(0, 16)}... → ${att.target_did.slice(0, 20)}... (${att.value > 0 ? "+" : ""}${att.value})`);
|
|
115
|
-
});
|
|
116
|
-
console.log(`[p2p] P2P integrado → ${node.peerId.toString().slice(0, 16)}...`);
|
|
117
|
-
}
|
|
118
94
|
// ── Rate limiter ──────────────────────────────────────────────────────────────
|
|
119
95
|
const rateLimits = new Map();
|
|
120
96
|
// ── DPoP Nonce Store — anti-replay para request signing ──────────────────────
|
|
@@ -248,25 +224,10 @@ function loadPeers() {
|
|
|
248
224
|
}
|
|
249
225
|
function savePeers() { writeFileSync(PEERS_DB, JSON.stringify(peers, null, 2)); }
|
|
250
226
|
/**
|
|
251
|
-
* Gossip: propaga la attestation a la red.
|
|
252
|
-
*
|
|
253
|
-
* Estrategia:
|
|
254
|
-
* 1. P2P GossipSub (Phase 5) - si el nodo libp2p está activo
|
|
255
|
-
* 2. HTTP fire-and-forget (Phase 3) - fallback para nodos legacy sin libp2p
|
|
256
|
-
*
|
|
257
|
-
* Ambos canales son fire-and-forget: no bloquean la respuesta al cliente.
|
|
227
|
+
* Gossip: propaga la attestation a la red via HTTP fire-and-forget.
|
|
258
228
|
*/
|
|
259
229
|
async function gossipAttestation(att, excludeUrl) {
|
|
260
|
-
//
|
|
261
|
-
if (p2pNode) {
|
|
262
|
-
const recipients = await publishAttestationP2P(p2pNode, att);
|
|
263
|
-
if (recipients > 0) {
|
|
264
|
-
console.log(`[p2p] Attestation publicada → ${recipients} peer(s) via GossipSub`);
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
// ── Canal 2: HTTP gossip con cifrado AES-256-GCM + XOR routing ────────────
|
|
268
|
-
// Selección de peers: XOR routing hacia el DID objetivo → O(log n)
|
|
269
|
-
// Con ≤10 peers: broadcast total. Con más: solo K=6 más cercanos.
|
|
230
|
+
// HTTP gossip con cifrado AES-256-GCM + XOR routing
|
|
270
231
|
const targets = selectGossipPeers(peers, att.target_did, excludeUrl);
|
|
271
232
|
if (targets.length < peers.length - (excludeUrl ? 1 : 0)) {
|
|
272
233
|
console.log(routingStats(peers.length, targets.length, att.target_did));
|
|
@@ -340,26 +301,32 @@ function getIP(req) {
|
|
|
340
301
|
}
|
|
341
302
|
// ── GET /info ─────────────────────────────────────────────────────────────────
|
|
342
303
|
function handleInfo(res, nodeKeypair) {
|
|
343
|
-
const
|
|
304
|
+
const addresses = (() => {
|
|
305
|
+
try {
|
|
306
|
+
const { default: addrs } = require("./blockchain/addresses.json");
|
|
307
|
+
return addrs;
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
return {};
|
|
311
|
+
}
|
|
312
|
+
})();
|
|
344
313
|
json(res, 200, {
|
|
345
314
|
node_did: nodeKeypair.did,
|
|
346
315
|
version: VERSION,
|
|
347
316
|
protocol: PROTOCOL.VERSION,
|
|
348
|
-
protocol_hash: PROTOCOL_HASH,
|
|
317
|
+
protocol_hash: PROTOCOL_HASH,
|
|
318
|
+
network: "base-sepolia",
|
|
319
|
+
contracts: {
|
|
320
|
+
PeerRegistry: "0x452fb66159dFCfC13f2fD9627aA4c56886BfB15b",
|
|
321
|
+
NullifierRegistry: addresses?.NullifierRegistry ?? "",
|
|
322
|
+
ReputationRegistry: addresses?.ReputationRegistry ?? "",
|
|
323
|
+
},
|
|
349
324
|
total_verified: Object.keys(nullifiers).length,
|
|
350
325
|
total_reputation: Object.keys(repStore).length,
|
|
351
326
|
known_peers: peers.length,
|
|
352
327
|
supported_countries: ["CO"],
|
|
353
|
-
capabilities: ["zk-verify", "anti-sybil", "co-sign", "bot-reputation", "
|
|
328
|
+
capabilities: ["zk-verify", "anti-sybil", "co-sign", "bot-reputation", "registraduria-validation", "on-chain-state"],
|
|
354
329
|
rate_limit: `${PROTOCOL.RATE_LIMIT_MAX} req/min per IP`,
|
|
355
|
-
// P2P stats (Phase 5)
|
|
356
|
-
p2p: p2pStats ? {
|
|
357
|
-
enabled: true,
|
|
358
|
-
peer_id: p2pStats.peerId,
|
|
359
|
-
peers: p2pStats.peers,
|
|
360
|
-
pubsub_peers: p2pStats.pubsubPeers,
|
|
361
|
-
multiaddrs: p2pStats.multiaddrs,
|
|
362
|
-
} : { enabled: false },
|
|
363
330
|
});
|
|
364
331
|
}
|
|
365
332
|
// ── GET /protocol ──────────────────────────────────────────────────────────────
|
|
@@ -640,32 +607,6 @@ async function handlePeerRegister(req, res) {
|
|
|
640
607
|
}
|
|
641
608
|
peers.push(url);
|
|
642
609
|
savePeers();
|
|
643
|
-
// ── Auto-dial libp2p layer ──────────────────────────────────────────────────
|
|
644
|
-
// WSL2 / NAT: mDNS no funciona → al registrar un peer HTTP, intentamos
|
|
645
|
-
// conectar también vía libp2p usando sus multiaddrs del /info endpoint.
|
|
646
|
-
if (p2pNode) {
|
|
647
|
-
setImmediate(async () => {
|
|
648
|
-
try {
|
|
649
|
-
const infoRes = await fetch(`${url}/info`, { signal: AbortSignal.timeout(3_000) });
|
|
650
|
-
if (infoRes.ok) {
|
|
651
|
-
const info = await infoRes.json();
|
|
652
|
-
const addrs = info?.p2p?.multiaddrs ?? [];
|
|
653
|
-
let dialed = false;
|
|
654
|
-
for (const ma of addrs) {
|
|
655
|
-
const ok = await dialP2PPeer(p2pNode, ma);
|
|
656
|
-
if (ok) {
|
|
657
|
-
console.log(`[peer] 🔗 P2P dial OK: ${ma}`);
|
|
658
|
-
dialed = true;
|
|
659
|
-
break;
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
if (!dialed)
|
|
663
|
-
console.log(`[peer] ℹ️ P2P dial failed for ${url} (mDNS fallback)`);
|
|
664
|
-
}
|
|
665
|
-
}
|
|
666
|
-
catch { /* non-critical — HTTP gossip is the fallback */ }
|
|
667
|
-
});
|
|
668
|
-
}
|
|
669
610
|
json(res, 200, { ok: true, peers: peers.length, protocol_hash: PROTOCOL_HASH });
|
|
670
611
|
}
|
|
671
612
|
// ── GET /peers ─────────────────────────────────────────────────────────────────
|
|
@@ -1089,9 +1030,7 @@ export function startValidatorNode(port = PORT) {
|
|
|
1089
1030
|
}
|
|
1090
1031
|
// GET /network/stats — stats públicas para la landing page
|
|
1091
1032
|
if (cleanUrl === "/network/stats" && req.method === "GET") {
|
|
1092
|
-
const p2pStats = p2pNode ? getP2PStats(p2pNode) : null;
|
|
1093
1033
|
const httpPeers = peers.length;
|
|
1094
|
-
const libp2pPeers = p2pStats?.peers ?? 0;
|
|
1095
1034
|
let registeredPeers = 0;
|
|
1096
1035
|
let nullifiersOnchain = 0;
|
|
1097
1036
|
let reputationOnchain = 0;
|
|
@@ -1116,25 +1055,24 @@ export function startValidatorNode(port = PORT) {
|
|
|
1116
1055
|
node_did: nodeKeypair.did.slice(0, 20) + "...",
|
|
1117
1056
|
version: VERSION,
|
|
1118
1057
|
protocol_hash: PROTOCOL_HASH.slice(0, 16) + "...",
|
|
1058
|
+
network: "base-sepolia",
|
|
1059
|
+
contracts: {
|
|
1060
|
+
PeerRegistry: "0x452fb66159dFCfC13f2fD9627aA4c56886BfB15b",
|
|
1061
|
+
},
|
|
1119
1062
|
// identidades y reputación (in-memory cache)
|
|
1120
1063
|
verified_identities: Object.keys(nullifiers).length,
|
|
1121
1064
|
reputation_profiles: Object.keys(repStore).length,
|
|
1122
|
-
// on-chain state (v0.
|
|
1065
|
+
// on-chain state (v0.6.0) — blockchain IS the shared state
|
|
1123
1066
|
nullifiers_onchain: nullifiersOnchain,
|
|
1124
1067
|
reputation_onchain: reputationOnchain,
|
|
1125
|
-
// peers
|
|
1068
|
+
// peers
|
|
1126
1069
|
known_peers: httpPeers,
|
|
1127
|
-
// peers — libp2p P2P
|
|
1128
|
-
p2p_peers: libp2pPeers,
|
|
1129
|
-
p2p_pubsub_peers: p2pStats?.pubsubPeers ?? 0,
|
|
1130
|
-
p2p_enabled: !!p2pNode,
|
|
1131
|
-
total_peers: Math.max(httpPeers, libp2pPeers),
|
|
1132
|
-
// on-chain registered peers (PeerRegistry)
|
|
1133
1070
|
registered_peers: registeredPeers,
|
|
1134
|
-
|
|
1071
|
+
total_peers: Math.max(httpPeers, registeredPeers),
|
|
1072
|
+
// state sync
|
|
1135
1073
|
state_hash: computeHash(Object.keys(nullifiers)).slice(0, 16) + "...",
|
|
1136
1074
|
last_sync: lastSyncTs,
|
|
1137
|
-
//
|
|
1075
|
+
// general
|
|
1138
1076
|
uptime_ms: Date.now() - (globalThis._startTime ?? Date.now()),
|
|
1139
1077
|
timestamp: Date.now(),
|
|
1140
1078
|
mcps_verified: null,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "soulprint-network",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Soulprint validator node
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Soulprint validator node — 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": {
|
|
@@ -35,25 +35,12 @@
|
|
|
35
35
|
],
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@chainsafe/libp2p-gossipsub": "14.1.2",
|
|
39
|
-
"@chainsafe/libp2p-noise": "16.1.5",
|
|
40
|
-
"@chainsafe/libp2p-yamux": "7.0.4",
|
|
41
|
-
"@libp2p/bootstrap": "11.0.47",
|
|
42
|
-
"@libp2p/identify": "3.0.39",
|
|
43
|
-
"@libp2p/kad-dht": "16.1.3",
|
|
44
|
-
"@libp2p/mdns": "11.0.47",
|
|
45
|
-
"@libp2p/peer-id": "^6.0.4",
|
|
46
|
-
"@libp2p/ping": "2.0.37",
|
|
47
|
-
"@libp2p/tcp": "10.1.19",
|
|
48
|
-
"@multiformats/multiaddr": "^13.0.1",
|
|
49
38
|
"ethers": "^6.16.0",
|
|
50
|
-
"libp2p": "2.10.0",
|
|
51
39
|
"nodemailer": "^8.0.1",
|
|
52
40
|
"otpauth": "^9.5.0",
|
|
53
41
|
"otplib": "^13.3.0",
|
|
54
42
|
"soulprint-core": "0.1.11",
|
|
55
|
-
"soulprint-zkp": "0.1.6"
|
|
56
|
-
"uint8arrays": "5.1.0"
|
|
43
|
+
"soulprint-zkp": "0.1.6"
|
|
57
44
|
},
|
|
58
45
|
"devDependencies": {
|
|
59
46
|
"@types/node": "^20.0.0",
|
|
@@ -70,4 +57,4 @@
|
|
|
70
57
|
"types": "./dist/index.d.ts"
|
|
71
58
|
}
|
|
72
59
|
}
|
|
73
|
-
}
|
|
60
|
+
}
|