@gethashd/bytecave-browser 1.0.1 → 1.0.3
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/{chunk-OJEETLZQ.js → chunk-O56JMOMV.js} +84 -27
- package/dist/client.d.ts +2 -1
- package/dist/discovery.d.ts +20 -3
- package/dist/index.cjs +85 -27
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/react/index.js +1 -1
- package/package.json +1 -1
- package/src/client.ts +80 -43
- package/src/discovery.ts +35 -3
- package/src/index.ts +1 -1
- package/src/types.ts +1 -1
|
@@ -6,6 +6,29 @@ import {
|
|
|
6
6
|
|
|
7
7
|
// src/discovery.ts
|
|
8
8
|
import { ethers } from "ethers";
|
|
9
|
+
var RelayDiscovery = class {
|
|
10
|
+
constructor(relayHttpUrl) {
|
|
11
|
+
this.relayHttpUrl = relayHttpUrl;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Get currently connected storage nodes from relay
|
|
15
|
+
* Returns peer IDs and relay circuit multiaddrs
|
|
16
|
+
*/
|
|
17
|
+
async getConnectedPeers() {
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetch(`${this.relayHttpUrl}/peers`);
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error(`Relay returned ${response.status}`);
|
|
22
|
+
}
|
|
23
|
+
const peers = await response.json();
|
|
24
|
+
console.log("[RelayDiscovery] Found connected peers:", peers.length);
|
|
25
|
+
return peers;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
console.warn("[RelayDiscovery] Failed to fetch peers from relay:", error);
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
};
|
|
9
32
|
var VAULT_REGISTRY_ABI = [
|
|
10
33
|
"function getActiveNodes() external view returns (bytes32[] memory)",
|
|
11
34
|
"function getNode(bytes32 nodeId) external view returns (tuple(address owner, bytes publicKey, string url, bytes32 metadataHash, uint256 registeredAt, bool active))",
|
|
@@ -5956,8 +5979,13 @@ var ByteCaveClient = class {
|
|
|
5956
5979
|
connectionTimeout: 3e4,
|
|
5957
5980
|
...config
|
|
5958
5981
|
};
|
|
5982
|
+
if (config.relayPeers && config.relayPeers.length > 0) {
|
|
5983
|
+
const relayHttpUrl = config.relayPeers[0];
|
|
5984
|
+
this.relayDiscovery = new RelayDiscovery(relayHttpUrl);
|
|
5985
|
+
console.log("[ByteCave] Using relay HTTP URL for peer discovery:", relayHttpUrl);
|
|
5986
|
+
}
|
|
5959
5987
|
if (config.vaultNodeRegistryAddress && config.rpcUrl) {
|
|
5960
|
-
this.
|
|
5988
|
+
this.contractDiscovery = new ContractDiscovery(config.vaultNodeRegistryAddress, config.rpcUrl);
|
|
5961
5989
|
}
|
|
5962
5990
|
}
|
|
5963
5991
|
/**
|
|
@@ -5976,12 +6004,8 @@ var ByteCaveClient = class {
|
|
|
5976
6004
|
console.log("[ByteCave] Using direct node addresses:", this.config.directNodeAddrs);
|
|
5977
6005
|
bootstrapPeers.push(...this.config.directNodeAddrs);
|
|
5978
6006
|
}
|
|
5979
|
-
if (this.config.relayPeers && this.config.relayPeers.length > 0) {
|
|
5980
|
-
console.log("[ByteCave] Using relay peers as fallback:", this.config.relayPeers);
|
|
5981
|
-
bootstrapPeers.push(...this.config.relayPeers);
|
|
5982
|
-
}
|
|
5983
6007
|
if (bootstrapPeers.length === 0) {
|
|
5984
|
-
console.warn("[ByteCave] No
|
|
6008
|
+
console.warn("[ByteCave] No direct node addresses configured - will rely on relay peer discovery");
|
|
5985
6009
|
}
|
|
5986
6010
|
console.log("[ByteCave] Bootstrap peers:", bootstrapPeers);
|
|
5987
6011
|
this.node = await createLibp2p({
|
|
@@ -6029,7 +6053,50 @@ var ByteCaveClient = class {
|
|
|
6029
6053
|
const connectedPeers = this.node.getPeers();
|
|
6030
6054
|
console.log("[ByteCave] Connected peers after relay dial:", connectedPeers.length, connectedPeers.map((p) => p.toString()));
|
|
6031
6055
|
p2pProtocolClient.setNode(this.node);
|
|
6032
|
-
|
|
6056
|
+
if (this.relayDiscovery) {
|
|
6057
|
+
console.log("[ByteCave] Querying relay HTTP endpoint for instant peer list...");
|
|
6058
|
+
try {
|
|
6059
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
6060
|
+
if (relayPeers.length > 0) {
|
|
6061
|
+
console.log("[ByteCave] Got", relayPeers.length, "peers from relay HTTP endpoint");
|
|
6062
|
+
for (const peer of relayPeers) {
|
|
6063
|
+
try {
|
|
6064
|
+
console.log("[ByteCave] Dialing peer:", peer.peerId.slice(0, 12) + "...");
|
|
6065
|
+
let connected = false;
|
|
6066
|
+
for (const addr of peer.multiaddrs) {
|
|
6067
|
+
try {
|
|
6068
|
+
const ma = multiaddr(addr);
|
|
6069
|
+
await this.node.dial(ma);
|
|
6070
|
+
connected = true;
|
|
6071
|
+
console.log("[ByteCave] \u2713 Connected via relay circuit");
|
|
6072
|
+
break;
|
|
6073
|
+
} catch (dialErr) {
|
|
6074
|
+
console.warn("[ByteCave] Failed to dial:", dialErr.message);
|
|
6075
|
+
}
|
|
6076
|
+
}
|
|
6077
|
+
if (connected) {
|
|
6078
|
+
const health = await p2pProtocolClient.getHealthFromPeer(peer.peerId);
|
|
6079
|
+
if (health) {
|
|
6080
|
+
this.knownPeers.set(peer.peerId, {
|
|
6081
|
+
peerId: peer.peerId,
|
|
6082
|
+
publicKey: health.publicKey || "",
|
|
6083
|
+
contentTypes: health.contentTypes || "all",
|
|
6084
|
+
connected: true,
|
|
6085
|
+
nodeId: health.nodeId
|
|
6086
|
+
});
|
|
6087
|
+
console.log("[ByteCave] \u2713 Discovered peer via HTTP relay:", health.nodeId || peer.peerId.slice(0, 12));
|
|
6088
|
+
}
|
|
6089
|
+
}
|
|
6090
|
+
} catch (err) {
|
|
6091
|
+
console.warn("[ByteCave] Failed to process peer from HTTP relay:", err.message);
|
|
6092
|
+
}
|
|
6093
|
+
}
|
|
6094
|
+
}
|
|
6095
|
+
} catch (err) {
|
|
6096
|
+
console.warn("[ByteCave] HTTP relay discovery failed, falling back to P2P directory:", err.message);
|
|
6097
|
+
}
|
|
6098
|
+
}
|
|
6099
|
+
console.log("[ByteCave] Querying relay for peer directory via P2P...");
|
|
6033
6100
|
for (const relayAddr of bootstrapPeers) {
|
|
6034
6101
|
try {
|
|
6035
6102
|
const parts = relayAddr.split("/p2p/");
|
|
@@ -6112,20 +6179,13 @@ var ByteCaveClient = class {
|
|
|
6112
6179
|
console.warn("[ByteCave] Cannot refresh - node not initialized");
|
|
6113
6180
|
return;
|
|
6114
6181
|
}
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
...this.config.relayPeers || []
|
|
6118
|
-
];
|
|
6119
|
-
console.log("[ByteCave] Refreshing peer directory from relays...");
|
|
6120
|
-
for (const relayAddr of bootstrapPeers) {
|
|
6182
|
+
console.log("[ByteCave] Refreshing peer directory from relay HTTP endpoint...");
|
|
6183
|
+
if (this.relayDiscovery) {
|
|
6121
6184
|
try {
|
|
6122
|
-
const
|
|
6123
|
-
if (
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
if (directory && directory.peers.length > 0) {
|
|
6127
|
-
console.log("[ByteCave] Refresh: Got", directory.peers.length, "peers from relay directory");
|
|
6128
|
-
for (const peer of directory.peers) {
|
|
6185
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
6186
|
+
if (relayPeers.length > 0) {
|
|
6187
|
+
console.log("[ByteCave] Refresh: Got", relayPeers.length, "peers from relay HTTP endpoint");
|
|
6188
|
+
for (const peer of relayPeers) {
|
|
6129
6189
|
const isConnected = this.node.getPeers().some((p) => p.toString() === peer.peerId);
|
|
6130
6190
|
const knownPeer = this.knownPeers.get(peer.peerId);
|
|
6131
6191
|
if (!isConnected || !knownPeer) {
|
|
@@ -6163,7 +6223,6 @@ var ByteCaveClient = class {
|
|
|
6163
6223
|
console.debug("[ByteCave] Refresh: Peer already connected:", peer.peerId.slice(0, 12) + "...");
|
|
6164
6224
|
}
|
|
6165
6225
|
}
|
|
6166
|
-
break;
|
|
6167
6226
|
}
|
|
6168
6227
|
} catch (err) {
|
|
6169
6228
|
console.warn("[ByteCave] Refresh: Failed to get directory from relay:", err.message);
|
|
@@ -6184,10 +6243,7 @@ var ByteCaveClient = class {
|
|
|
6184
6243
|
return { success: false, error: "P2P node not initialized" };
|
|
6185
6244
|
}
|
|
6186
6245
|
const allPeers = this.node.getPeers();
|
|
6187
|
-
const
|
|
6188
|
-
this.config.relayPeers?.map((addr) => addr.split("/p2p/").pop()) || []
|
|
6189
|
-
);
|
|
6190
|
-
const connectedPeerIds = allPeers.map((p) => p.toString()).filter((peerId) => !relayPeerIds.has(peerId));
|
|
6246
|
+
const connectedPeerIds = allPeers.map((p) => p.toString());
|
|
6191
6247
|
console.log("[ByteCave] Store - connected storage peers:", connectedPeerIds.length);
|
|
6192
6248
|
console.log("[ByteCave] Store - knownPeers with registration info:", this.knownPeers.size);
|
|
6193
6249
|
if (connectedPeerIds.length === 0) {
|
|
@@ -6550,11 +6606,11 @@ Nonce: ${nonce}`;
|
|
|
6550
6606
|
* Check if a nodeId is registered in the on-chain registry
|
|
6551
6607
|
*/
|
|
6552
6608
|
async checkNodeRegistration(nodeId) {
|
|
6553
|
-
if (!this.
|
|
6609
|
+
if (!this.contractDiscovery) {
|
|
6554
6610
|
return true;
|
|
6555
6611
|
}
|
|
6556
6612
|
try {
|
|
6557
|
-
const registeredNodes = await this.
|
|
6613
|
+
const registeredNodes = await this.contractDiscovery.getActiveNodes();
|
|
6558
6614
|
return registeredNodes.some((node) => node.nodeId === nodeId);
|
|
6559
6615
|
} catch (error) {
|
|
6560
6616
|
console.warn("[ByteCave] Failed to check node registration:", error);
|
|
@@ -7058,6 +7114,7 @@ function useHashdUrl(hashdUrl) {
|
|
|
7058
7114
|
}
|
|
7059
7115
|
|
|
7060
7116
|
export {
|
|
7117
|
+
RelayDiscovery,
|
|
7061
7118
|
ContractDiscovery,
|
|
7062
7119
|
p2pProtocolClient,
|
|
7063
7120
|
ByteCaveClient,
|
package/dist/client.d.ts
CHANGED
|
@@ -7,7 +7,8 @@ import { ethers } from 'ethers';
|
|
|
7
7
|
import type { ByteCaveConfig, PeerInfo, StoreResult, RetrieveResult, ConnectionState, SignalingMessage } from './types.js';
|
|
8
8
|
export declare class ByteCaveClient {
|
|
9
9
|
private node;
|
|
10
|
-
private
|
|
10
|
+
private contractDiscovery?;
|
|
11
|
+
private relayDiscovery?;
|
|
11
12
|
private config;
|
|
12
13
|
private knownPeers;
|
|
13
14
|
private connectionState;
|
package/dist/discovery.d.ts
CHANGED
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Peer discovery for ByteCave browser clients
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Supports two discovery methods:
|
|
5
|
+
* 1. RelayDiscovery - Fast: Query relay's /peers endpoint for instant peer list
|
|
6
|
+
* 2. ContractDiscovery - Slow: Read from VaultNodeRegistry smart contract
|
|
6
7
|
*/
|
|
7
8
|
import type { NodeRegistryEntry } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* Relay-based peer discovery (FAST)
|
|
11
|
+
* Queries the relay's HTTP endpoint for currently connected storage nodes
|
|
12
|
+
*/
|
|
13
|
+
export declare class RelayDiscovery {
|
|
14
|
+
private relayHttpUrl;
|
|
15
|
+
constructor(relayHttpUrl: string);
|
|
16
|
+
/**
|
|
17
|
+
* Get currently connected storage nodes from relay
|
|
18
|
+
* Returns peer IDs and relay circuit multiaddrs
|
|
19
|
+
*/
|
|
20
|
+
getConnectedPeers(): Promise<Array<{
|
|
21
|
+
peerId: string;
|
|
22
|
+
multiaddrs: string[];
|
|
23
|
+
}>>;
|
|
24
|
+
}
|
|
8
25
|
export declare class ContractDiscovery {
|
|
9
26
|
private provider;
|
|
10
27
|
private contract;
|
package/dist/index.cjs
CHANGED
|
@@ -38,6 +38,7 @@ __export(src_exports, {
|
|
|
38
38
|
HashdAudio: () => HashdAudio,
|
|
39
39
|
HashdImage: () => HashdImage,
|
|
40
40
|
HashdVideo: () => HashdVideo,
|
|
41
|
+
RelayDiscovery: () => RelayDiscovery,
|
|
41
42
|
TEST_EXPORT: () => TEST_EXPORT,
|
|
42
43
|
clearHashdCache: () => clearHashdCache,
|
|
43
44
|
createHashdUrl: () => createHashdUrl,
|
|
@@ -72,6 +73,29 @@ var import_ethers2 = require("ethers");
|
|
|
72
73
|
|
|
73
74
|
// src/discovery.ts
|
|
74
75
|
var import_ethers = require("ethers");
|
|
76
|
+
var RelayDiscovery = class {
|
|
77
|
+
constructor(relayHttpUrl) {
|
|
78
|
+
this.relayHttpUrl = relayHttpUrl;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get currently connected storage nodes from relay
|
|
82
|
+
* Returns peer IDs and relay circuit multiaddrs
|
|
83
|
+
*/
|
|
84
|
+
async getConnectedPeers() {
|
|
85
|
+
try {
|
|
86
|
+
const response = await fetch(`${this.relayHttpUrl}/peers`);
|
|
87
|
+
if (!response.ok) {
|
|
88
|
+
throw new Error(`Relay returned ${response.status}`);
|
|
89
|
+
}
|
|
90
|
+
const peers = await response.json();
|
|
91
|
+
console.log("[RelayDiscovery] Found connected peers:", peers.length);
|
|
92
|
+
return peers;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.warn("[RelayDiscovery] Failed to fetch peers from relay:", error);
|
|
95
|
+
return [];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
75
99
|
var VAULT_REGISTRY_ABI = [
|
|
76
100
|
"function getActiveNodes() external view returns (bytes32[] memory)",
|
|
77
101
|
"function getNode(bytes32 nodeId) external view returns (tuple(address owner, bytes publicKey, string url, bytes32 metadataHash, uint256 registeredAt, bool active))",
|
|
@@ -6008,8 +6032,13 @@ var ByteCaveClient = class {
|
|
|
6008
6032
|
connectionTimeout: 3e4,
|
|
6009
6033
|
...config
|
|
6010
6034
|
};
|
|
6035
|
+
if (config.relayPeers && config.relayPeers.length > 0) {
|
|
6036
|
+
const relayHttpUrl = config.relayPeers[0];
|
|
6037
|
+
this.relayDiscovery = new RelayDiscovery(relayHttpUrl);
|
|
6038
|
+
console.log("[ByteCave] Using relay HTTP URL for peer discovery:", relayHttpUrl);
|
|
6039
|
+
}
|
|
6011
6040
|
if (config.vaultNodeRegistryAddress && config.rpcUrl) {
|
|
6012
|
-
this.
|
|
6041
|
+
this.contractDiscovery = new ContractDiscovery(config.vaultNodeRegistryAddress, config.rpcUrl);
|
|
6013
6042
|
}
|
|
6014
6043
|
}
|
|
6015
6044
|
/**
|
|
@@ -6028,12 +6057,8 @@ var ByteCaveClient = class {
|
|
|
6028
6057
|
console.log("[ByteCave] Using direct node addresses:", this.config.directNodeAddrs);
|
|
6029
6058
|
bootstrapPeers.push(...this.config.directNodeAddrs);
|
|
6030
6059
|
}
|
|
6031
|
-
if (this.config.relayPeers && this.config.relayPeers.length > 0) {
|
|
6032
|
-
console.log("[ByteCave] Using relay peers as fallback:", this.config.relayPeers);
|
|
6033
|
-
bootstrapPeers.push(...this.config.relayPeers);
|
|
6034
|
-
}
|
|
6035
6060
|
if (bootstrapPeers.length === 0) {
|
|
6036
|
-
console.warn("[ByteCave] No
|
|
6061
|
+
console.warn("[ByteCave] No direct node addresses configured - will rely on relay peer discovery");
|
|
6037
6062
|
}
|
|
6038
6063
|
console.log("[ByteCave] Bootstrap peers:", bootstrapPeers);
|
|
6039
6064
|
this.node = await (0, import_libp2p.createLibp2p)({
|
|
@@ -6081,7 +6106,50 @@ var ByteCaveClient = class {
|
|
|
6081
6106
|
const connectedPeers = this.node.getPeers();
|
|
6082
6107
|
console.log("[ByteCave] Connected peers after relay dial:", connectedPeers.length, connectedPeers.map((p) => p.toString()));
|
|
6083
6108
|
p2pProtocolClient.setNode(this.node);
|
|
6084
|
-
|
|
6109
|
+
if (this.relayDiscovery) {
|
|
6110
|
+
console.log("[ByteCave] Querying relay HTTP endpoint for instant peer list...");
|
|
6111
|
+
try {
|
|
6112
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
6113
|
+
if (relayPeers.length > 0) {
|
|
6114
|
+
console.log("[ByteCave] Got", relayPeers.length, "peers from relay HTTP endpoint");
|
|
6115
|
+
for (const peer of relayPeers) {
|
|
6116
|
+
try {
|
|
6117
|
+
console.log("[ByteCave] Dialing peer:", peer.peerId.slice(0, 12) + "...");
|
|
6118
|
+
let connected = false;
|
|
6119
|
+
for (const addr of peer.multiaddrs) {
|
|
6120
|
+
try {
|
|
6121
|
+
const ma = (0, import_multiaddr.multiaddr)(addr);
|
|
6122
|
+
await this.node.dial(ma);
|
|
6123
|
+
connected = true;
|
|
6124
|
+
console.log("[ByteCave] \u2713 Connected via relay circuit");
|
|
6125
|
+
break;
|
|
6126
|
+
} catch (dialErr) {
|
|
6127
|
+
console.warn("[ByteCave] Failed to dial:", dialErr.message);
|
|
6128
|
+
}
|
|
6129
|
+
}
|
|
6130
|
+
if (connected) {
|
|
6131
|
+
const health = await p2pProtocolClient.getHealthFromPeer(peer.peerId);
|
|
6132
|
+
if (health) {
|
|
6133
|
+
this.knownPeers.set(peer.peerId, {
|
|
6134
|
+
peerId: peer.peerId,
|
|
6135
|
+
publicKey: health.publicKey || "",
|
|
6136
|
+
contentTypes: health.contentTypes || "all",
|
|
6137
|
+
connected: true,
|
|
6138
|
+
nodeId: health.nodeId
|
|
6139
|
+
});
|
|
6140
|
+
console.log("[ByteCave] \u2713 Discovered peer via HTTP relay:", health.nodeId || peer.peerId.slice(0, 12));
|
|
6141
|
+
}
|
|
6142
|
+
}
|
|
6143
|
+
} catch (err) {
|
|
6144
|
+
console.warn("[ByteCave] Failed to process peer from HTTP relay:", err.message);
|
|
6145
|
+
}
|
|
6146
|
+
}
|
|
6147
|
+
}
|
|
6148
|
+
} catch (err) {
|
|
6149
|
+
console.warn("[ByteCave] HTTP relay discovery failed, falling back to P2P directory:", err.message);
|
|
6150
|
+
}
|
|
6151
|
+
}
|
|
6152
|
+
console.log("[ByteCave] Querying relay for peer directory via P2P...");
|
|
6085
6153
|
for (const relayAddr of bootstrapPeers) {
|
|
6086
6154
|
try {
|
|
6087
6155
|
const parts = relayAddr.split("/p2p/");
|
|
@@ -6164,20 +6232,13 @@ var ByteCaveClient = class {
|
|
|
6164
6232
|
console.warn("[ByteCave] Cannot refresh - node not initialized");
|
|
6165
6233
|
return;
|
|
6166
6234
|
}
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
...this.config.relayPeers || []
|
|
6170
|
-
];
|
|
6171
|
-
console.log("[ByteCave] Refreshing peer directory from relays...");
|
|
6172
|
-
for (const relayAddr of bootstrapPeers) {
|
|
6235
|
+
console.log("[ByteCave] Refreshing peer directory from relay HTTP endpoint...");
|
|
6236
|
+
if (this.relayDiscovery) {
|
|
6173
6237
|
try {
|
|
6174
|
-
const
|
|
6175
|
-
if (
|
|
6176
|
-
|
|
6177
|
-
|
|
6178
|
-
if (directory && directory.peers.length > 0) {
|
|
6179
|
-
console.log("[ByteCave] Refresh: Got", directory.peers.length, "peers from relay directory");
|
|
6180
|
-
for (const peer of directory.peers) {
|
|
6238
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
6239
|
+
if (relayPeers.length > 0) {
|
|
6240
|
+
console.log("[ByteCave] Refresh: Got", relayPeers.length, "peers from relay HTTP endpoint");
|
|
6241
|
+
for (const peer of relayPeers) {
|
|
6181
6242
|
const isConnected = this.node.getPeers().some((p) => p.toString() === peer.peerId);
|
|
6182
6243
|
const knownPeer = this.knownPeers.get(peer.peerId);
|
|
6183
6244
|
if (!isConnected || !knownPeer) {
|
|
@@ -6215,7 +6276,6 @@ var ByteCaveClient = class {
|
|
|
6215
6276
|
console.debug("[ByteCave] Refresh: Peer already connected:", peer.peerId.slice(0, 12) + "...");
|
|
6216
6277
|
}
|
|
6217
6278
|
}
|
|
6218
|
-
break;
|
|
6219
6279
|
}
|
|
6220
6280
|
} catch (err) {
|
|
6221
6281
|
console.warn("[ByteCave] Refresh: Failed to get directory from relay:", err.message);
|
|
@@ -6236,10 +6296,7 @@ var ByteCaveClient = class {
|
|
|
6236
6296
|
return { success: false, error: "P2P node not initialized" };
|
|
6237
6297
|
}
|
|
6238
6298
|
const allPeers = this.node.getPeers();
|
|
6239
|
-
const
|
|
6240
|
-
this.config.relayPeers?.map((addr) => addr.split("/p2p/").pop()) || []
|
|
6241
|
-
);
|
|
6242
|
-
const connectedPeerIds = allPeers.map((p) => p.toString()).filter((peerId) => !relayPeerIds.has(peerId));
|
|
6299
|
+
const connectedPeerIds = allPeers.map((p) => p.toString());
|
|
6243
6300
|
console.log("[ByteCave] Store - connected storage peers:", connectedPeerIds.length);
|
|
6244
6301
|
console.log("[ByteCave] Store - knownPeers with registration info:", this.knownPeers.size);
|
|
6245
6302
|
if (connectedPeerIds.length === 0) {
|
|
@@ -6602,11 +6659,11 @@ Nonce: ${nonce}`;
|
|
|
6602
6659
|
* Check if a nodeId is registered in the on-chain registry
|
|
6603
6660
|
*/
|
|
6604
6661
|
async checkNodeRegistration(nodeId) {
|
|
6605
|
-
if (!this.
|
|
6662
|
+
if (!this.contractDiscovery) {
|
|
6606
6663
|
return true;
|
|
6607
6664
|
}
|
|
6608
6665
|
try {
|
|
6609
|
-
const registeredNodes = await this.
|
|
6666
|
+
const registeredNodes = await this.contractDiscovery.getActiveNodes();
|
|
6610
6667
|
return registeredNodes.some((node) => node.nodeId === nodeId);
|
|
6611
6668
|
} catch (error) {
|
|
6612
6669
|
console.warn("[ByteCave] Failed to check node registration:", error);
|
|
@@ -7261,6 +7318,7 @@ var TEST_EXPORT = "ByteCave Browser Package v1.0.0";
|
|
|
7261
7318
|
HashdAudio,
|
|
7262
7319
|
HashdImage,
|
|
7263
7320
|
HashdVideo,
|
|
7321
|
+
RelayDiscovery,
|
|
7264
7322
|
TEST_EXPORT,
|
|
7265
7323
|
clearHashdCache,
|
|
7266
7324
|
createHashdUrl,
|
package/dist/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
export declare const TEST_EXPORT = "ByteCave Browser Package v1.0.0";
|
|
8
8
|
export { ByteCaveClient } from './client.js';
|
|
9
|
-
export { ContractDiscovery } from './discovery.js';
|
|
9
|
+
export { ContractDiscovery, RelayDiscovery } from './discovery.js';
|
|
10
10
|
export { p2pProtocolClient } from './p2p-protocols.js';
|
|
11
11
|
export { ByteCaveProvider, useByteCaveContext } from './provider.js';
|
|
12
12
|
export type { P2PHealthResponse, P2PInfoResponse } from './p2p-protocols.js';
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
HashdAudio,
|
|
6
6
|
HashdImage,
|
|
7
7
|
HashdVideo,
|
|
8
|
+
RelayDiscovery,
|
|
8
9
|
p2pProtocolClient,
|
|
9
10
|
useByteCaveContext,
|
|
10
11
|
useHashdBatch,
|
|
@@ -12,7 +13,7 @@ import {
|
|
|
12
13
|
useHashdImage,
|
|
13
14
|
useHashdMedia,
|
|
14
15
|
useHashdUrl
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-O56JMOMV.js";
|
|
16
17
|
import {
|
|
17
18
|
clearHashdCache,
|
|
18
19
|
createHashdUrl,
|
|
@@ -32,6 +33,7 @@ export {
|
|
|
32
33
|
HashdAudio,
|
|
33
34
|
HashdImage,
|
|
34
35
|
HashdVideo,
|
|
36
|
+
RelayDiscovery,
|
|
35
37
|
TEST_EXPORT,
|
|
36
38
|
clearHashdCache,
|
|
37
39
|
createHashdUrl,
|
package/dist/react/index.js
CHANGED
package/package.json
CHANGED
package/src/client.ts
CHANGED
|
@@ -17,7 +17,7 @@ import { multiaddr } from '@multiformats/multiaddr';
|
|
|
17
17
|
import { peerIdFromString } from '@libp2p/peer-id';
|
|
18
18
|
import { fromString, toString } from 'uint8arrays';
|
|
19
19
|
import { ethers } from 'ethers';
|
|
20
|
-
import { ContractDiscovery } from './discovery.js';
|
|
20
|
+
import { ContractDiscovery, RelayDiscovery } from './discovery.js';
|
|
21
21
|
import { p2pProtocolClient } from './p2p-protocols.js';
|
|
22
22
|
import { CONTENT_REGISTRY_ABI } from './contracts/ContentRegistry.js';
|
|
23
23
|
import type {
|
|
@@ -34,7 +34,8 @@ const SIGNALING_TOPIC_PREFIX = 'bytecave-signaling-';
|
|
|
34
34
|
|
|
35
35
|
export class ByteCaveClient {
|
|
36
36
|
private node: Libp2p | null = null;
|
|
37
|
-
private
|
|
37
|
+
private contractDiscovery?: ContractDiscovery; // Optional - only if contract address provided
|
|
38
|
+
private relayDiscovery?: RelayDiscovery; // Optional - for fast peer discovery via relay
|
|
38
39
|
private config: ByteCaveConfig;
|
|
39
40
|
private knownPeers: Map<string, PeerInfo> = new Map();
|
|
40
41
|
private connectionState: ConnectionState = 'disconnected';
|
|
@@ -46,9 +47,18 @@ export class ByteCaveClient {
|
|
|
46
47
|
connectionTimeout: 30000,
|
|
47
48
|
...config
|
|
48
49
|
};
|
|
49
|
-
|
|
50
|
+
|
|
51
|
+
// Initialize relay discovery using HTTP URLs from relayPeers
|
|
52
|
+
if (config.relayPeers && config.relayPeers.length > 0) {
|
|
53
|
+
// Use first relay HTTP URL for peer discovery
|
|
54
|
+
const relayHttpUrl = config.relayPeers[0];
|
|
55
|
+
this.relayDiscovery = new RelayDiscovery(relayHttpUrl);
|
|
56
|
+
console.log('[ByteCave] Using relay HTTP URL for peer discovery:', relayHttpUrl);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Initialize contract discovery if contract address is provided
|
|
50
60
|
if (config.vaultNodeRegistryAddress && config.rpcUrl) {
|
|
51
|
-
this.
|
|
61
|
+
this.contractDiscovery = new ContractDiscovery(config.vaultNodeRegistryAddress, config.rpcUrl);
|
|
52
62
|
}
|
|
53
63
|
}
|
|
54
64
|
|
|
@@ -73,14 +83,8 @@ export class ByteCaveClient {
|
|
|
73
83
|
bootstrapPeers.push(...this.config.directNodeAddrs);
|
|
74
84
|
}
|
|
75
85
|
|
|
76
|
-
// Use relay peers for fallback (circuit relay connections)
|
|
77
|
-
if (this.config.relayPeers && this.config.relayPeers.length > 0) {
|
|
78
|
-
console.log('[ByteCave] Using relay peers as fallback:', this.config.relayPeers);
|
|
79
|
-
bootstrapPeers.push(...this.config.relayPeers);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
86
|
if (bootstrapPeers.length === 0) {
|
|
83
|
-
console.warn('[ByteCave] No
|
|
87
|
+
console.warn('[ByteCave] No direct node addresses configured - will rely on relay peer discovery');
|
|
84
88
|
}
|
|
85
89
|
|
|
86
90
|
console.log('[ByteCave] Bootstrap peers:', bootstrapPeers);
|
|
@@ -144,8 +148,58 @@ export class ByteCaveClient {
|
|
|
144
148
|
// Initialize P2P protocol client with the libp2p node
|
|
145
149
|
p2pProtocolClient.setNode(this.node);
|
|
146
150
|
|
|
147
|
-
//
|
|
148
|
-
|
|
151
|
+
// FASTEST discovery: Query relay HTTP endpoint for connected peers
|
|
152
|
+
if (this.relayDiscovery) {
|
|
153
|
+
console.log('[ByteCave] Querying relay HTTP endpoint for instant peer list...');
|
|
154
|
+
try {
|
|
155
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
156
|
+
if (relayPeers.length > 0) {
|
|
157
|
+
console.log('[ByteCave] Got', relayPeers.length, 'peers from relay HTTP endpoint');
|
|
158
|
+
|
|
159
|
+
// Dial each peer using their relay circuit multiaddrs
|
|
160
|
+
for (const peer of relayPeers) {
|
|
161
|
+
try {
|
|
162
|
+
console.log('[ByteCave] Dialing peer:', peer.peerId.slice(0, 12) + '...');
|
|
163
|
+
|
|
164
|
+
let connected = false;
|
|
165
|
+
for (const addr of peer.multiaddrs) {
|
|
166
|
+
try {
|
|
167
|
+
const ma = multiaddr(addr);
|
|
168
|
+
await this.node.dial(ma as any);
|
|
169
|
+
connected = true;
|
|
170
|
+
console.log('[ByteCave] ✓ Connected via relay circuit');
|
|
171
|
+
break;
|
|
172
|
+
} catch (dialErr: any) {
|
|
173
|
+
console.warn('[ByteCave] Failed to dial:', dialErr.message);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (connected) {
|
|
178
|
+
// Fetch health data
|
|
179
|
+
const health = await p2pProtocolClient.getHealthFromPeer(peer.peerId);
|
|
180
|
+
if (health) {
|
|
181
|
+
this.knownPeers.set(peer.peerId, {
|
|
182
|
+
peerId: peer.peerId,
|
|
183
|
+
publicKey: health.publicKey || '',
|
|
184
|
+
contentTypes: health.contentTypes || 'all',
|
|
185
|
+
connected: true,
|
|
186
|
+
nodeId: health.nodeId
|
|
187
|
+
});
|
|
188
|
+
console.log('[ByteCave] ✓ Discovered peer via HTTP relay:', health.nodeId || peer.peerId.slice(0, 12));
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
} catch (err: any) {
|
|
192
|
+
console.warn('[ByteCave] Failed to process peer from HTTP relay:', err.message);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
} catch (err: any) {
|
|
197
|
+
console.warn('[ByteCave] HTTP relay discovery failed, falling back to P2P directory:', err.message);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Fallback: Query relay for peer directory via P2P protocol
|
|
202
|
+
console.log('[ByteCave] Querying relay for peer directory via P2P...');
|
|
149
203
|
for (const relayAddr of bootstrapPeers) {
|
|
150
204
|
try {
|
|
151
205
|
// Extract relay peer ID from multiaddr
|
|
@@ -244,33 +298,24 @@ export class ByteCaveClient {
|
|
|
244
298
|
return;
|
|
245
299
|
}
|
|
246
300
|
|
|
247
|
-
|
|
248
|
-
...(this.config.directNodeAddrs || []),
|
|
249
|
-
...(this.config.relayPeers || [])
|
|
250
|
-
];
|
|
251
|
-
|
|
252
|
-
console.log('[ByteCave] Refreshing peer directory from relays...');
|
|
301
|
+
console.log('[ByteCave] Refreshing peer directory from relay HTTP endpoint...');
|
|
253
302
|
|
|
254
|
-
|
|
303
|
+
// Use HTTP relay discovery for instant refresh
|
|
304
|
+
if (this.relayDiscovery) {
|
|
255
305
|
try {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
const relayPeerId = parts[parts.length - 1];
|
|
260
|
-
|
|
261
|
-
const directory = await p2pProtocolClient.getPeerDirectoryFromRelay(relayPeerId);
|
|
262
|
-
if (directory && directory.peers.length > 0) {
|
|
263
|
-
console.log('[ByteCave] Refresh: Got', directory.peers.length, 'peers from relay directory');
|
|
306
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
307
|
+
if (relayPeers.length > 0) {
|
|
308
|
+
console.log('[ByteCave] Refresh: Got', relayPeers.length, 'peers from relay HTTP endpoint');
|
|
264
309
|
|
|
265
310
|
// Check each peer and reconnect if disconnected
|
|
266
|
-
for (const peer of
|
|
311
|
+
for (const peer of relayPeers) {
|
|
267
312
|
const isConnected = this.node.getPeers().some(p => p.toString() === peer.peerId);
|
|
268
313
|
const knownPeer = this.knownPeers.get(peer.peerId);
|
|
269
314
|
|
|
270
315
|
if (!isConnected || !knownPeer) {
|
|
271
316
|
console.log('[ByteCave] Refresh: Reconnecting to peer:', peer.peerId.slice(0, 12) + '...');
|
|
272
317
|
|
|
273
|
-
// Try to dial using circuit
|
|
318
|
+
// Try to dial using relay circuit multiaddr
|
|
274
319
|
let connected = false;
|
|
275
320
|
for (const addr of peer.multiaddrs) {
|
|
276
321
|
try {
|
|
@@ -306,8 +351,6 @@ export class ByteCaveClient {
|
|
|
306
351
|
console.debug('[ByteCave] Refresh: Peer already connected:', peer.peerId.slice(0, 12) + '...');
|
|
307
352
|
}
|
|
308
353
|
}
|
|
309
|
-
|
|
310
|
-
break; // Successfully refreshed from one relay
|
|
311
354
|
}
|
|
312
355
|
} catch (err: any) {
|
|
313
356
|
console.warn('[ByteCave] Refresh: Failed to get directory from relay:', err.message);
|
|
@@ -330,15 +373,9 @@ export class ByteCaveClient {
|
|
|
330
373
|
return { success: false, error: 'P2P node not initialized' };
|
|
331
374
|
}
|
|
332
375
|
|
|
333
|
-
// Get all connected peers
|
|
376
|
+
// Get all connected peers
|
|
334
377
|
const allPeers = this.node.getPeers();
|
|
335
|
-
const
|
|
336
|
-
this.config.relayPeers?.map(addr => addr.split('/p2p/').pop()) || []
|
|
337
|
-
);
|
|
338
|
-
|
|
339
|
-
const connectedPeerIds = allPeers
|
|
340
|
-
.map(p => p.toString())
|
|
341
|
-
.filter(peerId => !relayPeerIds.has(peerId));
|
|
378
|
+
const connectedPeerIds = allPeers.map(p => p.toString());
|
|
342
379
|
|
|
343
380
|
console.log('[ByteCave] Store - connected storage peers:', connectedPeerIds.length);
|
|
344
381
|
console.log('[ByteCave] Store - knownPeers with registration info:', this.knownPeers.size);
|
|
@@ -807,14 +844,14 @@ Nonce: ${nonce}`;
|
|
|
807
844
|
* Check if a nodeId is registered in the on-chain registry
|
|
808
845
|
*/
|
|
809
846
|
private async checkNodeRegistration(nodeId: string): Promise<boolean> {
|
|
810
|
-
if (!this.
|
|
847
|
+
if (!this.contractDiscovery) {
|
|
811
848
|
// No contract configured - skip registration check
|
|
812
849
|
return true;
|
|
813
850
|
}
|
|
814
851
|
|
|
815
852
|
try {
|
|
816
|
-
const registeredNodes = await this.
|
|
817
|
-
return registeredNodes.some(node => node.nodeId === nodeId);
|
|
853
|
+
const registeredNodes = await this.contractDiscovery.getActiveNodes();
|
|
854
|
+
return registeredNodes.some((node: any) => node.nodeId === nodeId);
|
|
818
855
|
} catch (error) {
|
|
819
856
|
console.warn('[ByteCave] Failed to check node registration:', error);
|
|
820
857
|
return false;
|
package/src/discovery.ts
CHANGED
|
@@ -1,13 +1,45 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Peer discovery for ByteCave browser clients
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Supports two discovery methods:
|
|
5
|
+
* 1. RelayDiscovery - Fast: Query relay's /peers endpoint for instant peer list
|
|
6
|
+
* 2. ContractDiscovery - Slow: Read from VaultNodeRegistry smart contract
|
|
6
7
|
*/
|
|
7
8
|
|
|
8
9
|
import { ethers } from 'ethers';
|
|
9
10
|
import type { NodeRegistryEntry } from './types.js';
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Relay-based peer discovery (FAST)
|
|
14
|
+
* Queries the relay's HTTP endpoint for currently connected storage nodes
|
|
15
|
+
*/
|
|
16
|
+
export class RelayDiscovery {
|
|
17
|
+
private relayHttpUrl: string;
|
|
18
|
+
|
|
19
|
+
constructor(relayHttpUrl: string) {
|
|
20
|
+
this.relayHttpUrl = relayHttpUrl;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get currently connected storage nodes from relay
|
|
25
|
+
* Returns peer IDs and relay circuit multiaddrs
|
|
26
|
+
*/
|
|
27
|
+
async getConnectedPeers(): Promise<Array<{ peerId: string; multiaddrs: string[] }>> {
|
|
28
|
+
try {
|
|
29
|
+
const response = await fetch(`${this.relayHttpUrl}/peers`);
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
throw new Error(`Relay returned ${response.status}`);
|
|
32
|
+
}
|
|
33
|
+
const peers = await response.json();
|
|
34
|
+
console.log('[RelayDiscovery] Found connected peers:', peers.length);
|
|
35
|
+
return peers;
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.warn('[RelayDiscovery] Failed to fetch peers from relay:', error);
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
11
43
|
const VAULT_REGISTRY_ABI = [
|
|
12
44
|
'function getActiveNodes() external view returns (bytes32[] memory)',
|
|
13
45
|
'function getNode(bytes32 nodeId) external view returns (tuple(address owner, bytes publicKey, string url, bytes32 metadataHash, uint256 registeredAt, bool active))',
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
export const TEST_EXPORT = "ByteCave Browser Package v1.0.0";
|
|
10
10
|
|
|
11
11
|
export { ByteCaveClient } from './client.js';
|
|
12
|
-
export { ContractDiscovery } from './discovery.js';
|
|
12
|
+
export { ContractDiscovery, RelayDiscovery } from './discovery.js';
|
|
13
13
|
export { p2pProtocolClient } from './p2p-protocols.js';
|
|
14
14
|
|
|
15
15
|
// Provider exports
|
package/src/types.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface ByteCaveConfig {
|
|
|
8
8
|
rpcUrl?: string; // Optional - required if vaultNodeRegistryAddress is provided
|
|
9
9
|
appId: string; // Application identifier for storage authorization
|
|
10
10
|
directNodeAddrs?: string[]; // Direct node multiaddrs for WebRTC connections (no relay)
|
|
11
|
-
relayPeers?: string[]; // Relay
|
|
11
|
+
relayPeers?: string[]; // Relay HTTP URLs (e.g., http://localhost:9090) for peer discovery
|
|
12
12
|
maxPeers?: number;
|
|
13
13
|
connectionTimeout?: number;
|
|
14
14
|
}
|