@gethashd/bytecave-browser 1.0.2 → 1.0.4
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-6R5YCAYU.js} +85 -23
- package/dist/client.d.ts +2 -1
- package/dist/discovery.d.ts +20 -3
- package/dist/index.cjs +86 -23
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/provider.d.ts +2 -1
- package/dist/react/index.js +1 -1
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
- package/src/client.ts +79 -38
- package/src/discovery.ts +35 -3
- package/src/index.ts +1 -1
- package/src/provider.tsx +3 -0
- package/src/types.ts +2 -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,12 @@ var ByteCaveClient = class {
|
|
|
5956
5979
|
connectionTimeout: 3e4,
|
|
5957
5980
|
...config
|
|
5958
5981
|
};
|
|
5982
|
+
if (config.relayHttpUrl) {
|
|
5983
|
+
this.relayDiscovery = new RelayDiscovery(config.relayHttpUrl);
|
|
5984
|
+
console.log("[ByteCave] Using relay HTTP URL for peer discovery:", config.relayHttpUrl);
|
|
5985
|
+
}
|
|
5959
5986
|
if (config.vaultNodeRegistryAddress && config.rpcUrl) {
|
|
5960
|
-
this.
|
|
5987
|
+
this.contractDiscovery = new ContractDiscovery(config.vaultNodeRegistryAddress, config.rpcUrl);
|
|
5961
5988
|
}
|
|
5962
5989
|
}
|
|
5963
5990
|
/**
|
|
@@ -5977,7 +6004,7 @@ var ByteCaveClient = class {
|
|
|
5977
6004
|
bootstrapPeers.push(...this.config.directNodeAddrs);
|
|
5978
6005
|
}
|
|
5979
6006
|
if (this.config.relayPeers && this.config.relayPeers.length > 0) {
|
|
5980
|
-
console.log("[ByteCave] Using relay peers
|
|
6007
|
+
console.log("[ByteCave] Using relay peers for circuit relay:", this.config.relayPeers);
|
|
5981
6008
|
bootstrapPeers.push(...this.config.relayPeers);
|
|
5982
6009
|
}
|
|
5983
6010
|
if (bootstrapPeers.length === 0) {
|
|
@@ -6029,7 +6056,50 @@ var ByteCaveClient = class {
|
|
|
6029
6056
|
const connectedPeers = this.node.getPeers();
|
|
6030
6057
|
console.log("[ByteCave] Connected peers after relay dial:", connectedPeers.length, connectedPeers.map((p) => p.toString()));
|
|
6031
6058
|
p2pProtocolClient.setNode(this.node);
|
|
6032
|
-
|
|
6059
|
+
if (this.relayDiscovery) {
|
|
6060
|
+
console.log("[ByteCave] Querying relay HTTP endpoint for instant peer list...");
|
|
6061
|
+
try {
|
|
6062
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
6063
|
+
if (relayPeers.length > 0) {
|
|
6064
|
+
console.log("[ByteCave] Got", relayPeers.length, "peers from relay HTTP endpoint");
|
|
6065
|
+
for (const peer of relayPeers) {
|
|
6066
|
+
try {
|
|
6067
|
+
console.log("[ByteCave] Dialing peer:", peer.peerId.slice(0, 12) + "...");
|
|
6068
|
+
let connected = false;
|
|
6069
|
+
for (const addr of peer.multiaddrs) {
|
|
6070
|
+
try {
|
|
6071
|
+
const ma = multiaddr(addr);
|
|
6072
|
+
await this.node.dial(ma);
|
|
6073
|
+
connected = true;
|
|
6074
|
+
console.log("[ByteCave] \u2713 Connected via relay circuit");
|
|
6075
|
+
break;
|
|
6076
|
+
} catch (dialErr) {
|
|
6077
|
+
console.warn("[ByteCave] Failed to dial:", dialErr.message);
|
|
6078
|
+
}
|
|
6079
|
+
}
|
|
6080
|
+
if (connected) {
|
|
6081
|
+
const health = await p2pProtocolClient.getHealthFromPeer(peer.peerId);
|
|
6082
|
+
if (health) {
|
|
6083
|
+
this.knownPeers.set(peer.peerId, {
|
|
6084
|
+
peerId: peer.peerId,
|
|
6085
|
+
publicKey: health.publicKey || "",
|
|
6086
|
+
contentTypes: health.contentTypes || "all",
|
|
6087
|
+
connected: true,
|
|
6088
|
+
nodeId: health.nodeId
|
|
6089
|
+
});
|
|
6090
|
+
console.log("[ByteCave] \u2713 Discovered peer via HTTP relay:", health.nodeId || peer.peerId.slice(0, 12));
|
|
6091
|
+
}
|
|
6092
|
+
}
|
|
6093
|
+
} catch (err) {
|
|
6094
|
+
console.warn("[ByteCave] Failed to process peer from HTTP relay:", err.message);
|
|
6095
|
+
}
|
|
6096
|
+
}
|
|
6097
|
+
}
|
|
6098
|
+
} catch (err) {
|
|
6099
|
+
console.warn("[ByteCave] HTTP relay discovery failed, falling back to P2P directory:", err.message);
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
6102
|
+
console.log("[ByteCave] Querying relay for peer directory via P2P...");
|
|
6033
6103
|
for (const relayAddr of bootstrapPeers) {
|
|
6034
6104
|
try {
|
|
6035
6105
|
const parts = relayAddr.split("/p2p/");
|
|
@@ -6112,20 +6182,13 @@ var ByteCaveClient = class {
|
|
|
6112
6182
|
console.warn("[ByteCave] Cannot refresh - node not initialized");
|
|
6113
6183
|
return;
|
|
6114
6184
|
}
|
|
6115
|
-
|
|
6116
|
-
|
|
6117
|
-
...this.config.relayPeers || []
|
|
6118
|
-
];
|
|
6119
|
-
console.log("[ByteCave] Refreshing peer directory from relays...");
|
|
6120
|
-
for (const relayAddr of bootstrapPeers) {
|
|
6185
|
+
console.log("[ByteCave] Refreshing peer directory from relay HTTP endpoint...");
|
|
6186
|
+
if (this.relayDiscovery) {
|
|
6121
6187
|
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) {
|
|
6188
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
6189
|
+
if (relayPeers.length > 0) {
|
|
6190
|
+
console.log("[ByteCave] Refresh: Got", relayPeers.length, "peers from relay HTTP endpoint");
|
|
6191
|
+
for (const peer of relayPeers) {
|
|
6129
6192
|
const isConnected = this.node.getPeers().some((p) => p.toString() === peer.peerId);
|
|
6130
6193
|
const knownPeer = this.knownPeers.get(peer.peerId);
|
|
6131
6194
|
if (!isConnected || !knownPeer) {
|
|
@@ -6163,7 +6226,6 @@ var ByteCaveClient = class {
|
|
|
6163
6226
|
console.debug("[ByteCave] Refresh: Peer already connected:", peer.peerId.slice(0, 12) + "...");
|
|
6164
6227
|
}
|
|
6165
6228
|
}
|
|
6166
|
-
break;
|
|
6167
6229
|
}
|
|
6168
6230
|
} catch (err) {
|
|
6169
6231
|
console.warn("[ByteCave] Refresh: Failed to get directory from relay:", err.message);
|
|
@@ -6184,10 +6246,7 @@ var ByteCaveClient = class {
|
|
|
6184
6246
|
return { success: false, error: "P2P node not initialized" };
|
|
6185
6247
|
}
|
|
6186
6248
|
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));
|
|
6249
|
+
const connectedPeerIds = allPeers.map((p) => p.toString());
|
|
6191
6250
|
console.log("[ByteCave] Store - connected storage peers:", connectedPeerIds.length);
|
|
6192
6251
|
console.log("[ByteCave] Store - knownPeers with registration info:", this.knownPeers.size);
|
|
6193
6252
|
if (connectedPeerIds.length === 0) {
|
|
@@ -6550,11 +6609,11 @@ Nonce: ${nonce}`;
|
|
|
6550
6609
|
* Check if a nodeId is registered in the on-chain registry
|
|
6551
6610
|
*/
|
|
6552
6611
|
async checkNodeRegistration(nodeId) {
|
|
6553
|
-
if (!this.
|
|
6612
|
+
if (!this.contractDiscovery) {
|
|
6554
6613
|
return true;
|
|
6555
6614
|
}
|
|
6556
6615
|
try {
|
|
6557
|
-
const registeredNodes = await this.
|
|
6616
|
+
const registeredNodes = await this.contractDiscovery.getActiveNodes();
|
|
6558
6617
|
return registeredNodes.some((node) => node.nodeId === nodeId);
|
|
6559
6618
|
} catch (error) {
|
|
6560
6619
|
console.warn("[ByteCave] Failed to check node registration:", error);
|
|
@@ -6615,6 +6674,7 @@ function ByteCaveProvider({
|
|
|
6615
6674
|
rpcUrl,
|
|
6616
6675
|
appId,
|
|
6617
6676
|
relayPeers = [],
|
|
6677
|
+
relayHttpUrl,
|
|
6618
6678
|
directNodeAddrs = []
|
|
6619
6679
|
}) {
|
|
6620
6680
|
const [connectionState, setConnectionState] = useState("disconnected");
|
|
@@ -6657,6 +6717,7 @@ function ByteCaveProvider({
|
|
|
6657
6717
|
appId,
|
|
6658
6718
|
directNodeAddrs,
|
|
6659
6719
|
relayPeers,
|
|
6720
|
+
relayHttpUrl,
|
|
6660
6721
|
maxPeers: 10,
|
|
6661
6722
|
connectionTimeout: 3e4
|
|
6662
6723
|
});
|
|
@@ -7058,6 +7119,7 @@ function useHashdUrl(hashdUrl) {
|
|
|
7058
7119
|
}
|
|
7059
7120
|
|
|
7060
7121
|
export {
|
|
7122
|
+
RelayDiscovery,
|
|
7061
7123
|
ContractDiscovery,
|
|
7062
7124
|
p2pProtocolClient,
|
|
7063
7125
|
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,12 @@ var ByteCaveClient = class {
|
|
|
6008
6032
|
connectionTimeout: 3e4,
|
|
6009
6033
|
...config
|
|
6010
6034
|
};
|
|
6035
|
+
if (config.relayHttpUrl) {
|
|
6036
|
+
this.relayDiscovery = new RelayDiscovery(config.relayHttpUrl);
|
|
6037
|
+
console.log("[ByteCave] Using relay HTTP URL for peer discovery:", config.relayHttpUrl);
|
|
6038
|
+
}
|
|
6011
6039
|
if (config.vaultNodeRegistryAddress && config.rpcUrl) {
|
|
6012
|
-
this.
|
|
6040
|
+
this.contractDiscovery = new ContractDiscovery(config.vaultNodeRegistryAddress, config.rpcUrl);
|
|
6013
6041
|
}
|
|
6014
6042
|
}
|
|
6015
6043
|
/**
|
|
@@ -6029,7 +6057,7 @@ var ByteCaveClient = class {
|
|
|
6029
6057
|
bootstrapPeers.push(...this.config.directNodeAddrs);
|
|
6030
6058
|
}
|
|
6031
6059
|
if (this.config.relayPeers && this.config.relayPeers.length > 0) {
|
|
6032
|
-
console.log("[ByteCave] Using relay peers
|
|
6060
|
+
console.log("[ByteCave] Using relay peers for circuit relay:", this.config.relayPeers);
|
|
6033
6061
|
bootstrapPeers.push(...this.config.relayPeers);
|
|
6034
6062
|
}
|
|
6035
6063
|
if (bootstrapPeers.length === 0) {
|
|
@@ -6081,7 +6109,50 @@ var ByteCaveClient = class {
|
|
|
6081
6109
|
const connectedPeers = this.node.getPeers();
|
|
6082
6110
|
console.log("[ByteCave] Connected peers after relay dial:", connectedPeers.length, connectedPeers.map((p) => p.toString()));
|
|
6083
6111
|
p2pProtocolClient.setNode(this.node);
|
|
6084
|
-
|
|
6112
|
+
if (this.relayDiscovery) {
|
|
6113
|
+
console.log("[ByteCave] Querying relay HTTP endpoint for instant peer list...");
|
|
6114
|
+
try {
|
|
6115
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
6116
|
+
if (relayPeers.length > 0) {
|
|
6117
|
+
console.log("[ByteCave] Got", relayPeers.length, "peers from relay HTTP endpoint");
|
|
6118
|
+
for (const peer of relayPeers) {
|
|
6119
|
+
try {
|
|
6120
|
+
console.log("[ByteCave] Dialing peer:", peer.peerId.slice(0, 12) + "...");
|
|
6121
|
+
let connected = false;
|
|
6122
|
+
for (const addr of peer.multiaddrs) {
|
|
6123
|
+
try {
|
|
6124
|
+
const ma = (0, import_multiaddr.multiaddr)(addr);
|
|
6125
|
+
await this.node.dial(ma);
|
|
6126
|
+
connected = true;
|
|
6127
|
+
console.log("[ByteCave] \u2713 Connected via relay circuit");
|
|
6128
|
+
break;
|
|
6129
|
+
} catch (dialErr) {
|
|
6130
|
+
console.warn("[ByteCave] Failed to dial:", dialErr.message);
|
|
6131
|
+
}
|
|
6132
|
+
}
|
|
6133
|
+
if (connected) {
|
|
6134
|
+
const health = await p2pProtocolClient.getHealthFromPeer(peer.peerId);
|
|
6135
|
+
if (health) {
|
|
6136
|
+
this.knownPeers.set(peer.peerId, {
|
|
6137
|
+
peerId: peer.peerId,
|
|
6138
|
+
publicKey: health.publicKey || "",
|
|
6139
|
+
contentTypes: health.contentTypes || "all",
|
|
6140
|
+
connected: true,
|
|
6141
|
+
nodeId: health.nodeId
|
|
6142
|
+
});
|
|
6143
|
+
console.log("[ByteCave] \u2713 Discovered peer via HTTP relay:", health.nodeId || peer.peerId.slice(0, 12));
|
|
6144
|
+
}
|
|
6145
|
+
}
|
|
6146
|
+
} catch (err) {
|
|
6147
|
+
console.warn("[ByteCave] Failed to process peer from HTTP relay:", err.message);
|
|
6148
|
+
}
|
|
6149
|
+
}
|
|
6150
|
+
}
|
|
6151
|
+
} catch (err) {
|
|
6152
|
+
console.warn("[ByteCave] HTTP relay discovery failed, falling back to P2P directory:", err.message);
|
|
6153
|
+
}
|
|
6154
|
+
}
|
|
6155
|
+
console.log("[ByteCave] Querying relay for peer directory via P2P...");
|
|
6085
6156
|
for (const relayAddr of bootstrapPeers) {
|
|
6086
6157
|
try {
|
|
6087
6158
|
const parts = relayAddr.split("/p2p/");
|
|
@@ -6164,20 +6235,13 @@ var ByteCaveClient = class {
|
|
|
6164
6235
|
console.warn("[ByteCave] Cannot refresh - node not initialized");
|
|
6165
6236
|
return;
|
|
6166
6237
|
}
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
...this.config.relayPeers || []
|
|
6170
|
-
];
|
|
6171
|
-
console.log("[ByteCave] Refreshing peer directory from relays...");
|
|
6172
|
-
for (const relayAddr of bootstrapPeers) {
|
|
6238
|
+
console.log("[ByteCave] Refreshing peer directory from relay HTTP endpoint...");
|
|
6239
|
+
if (this.relayDiscovery) {
|
|
6173
6240
|
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) {
|
|
6241
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
6242
|
+
if (relayPeers.length > 0) {
|
|
6243
|
+
console.log("[ByteCave] Refresh: Got", relayPeers.length, "peers from relay HTTP endpoint");
|
|
6244
|
+
for (const peer of relayPeers) {
|
|
6181
6245
|
const isConnected = this.node.getPeers().some((p) => p.toString() === peer.peerId);
|
|
6182
6246
|
const knownPeer = this.knownPeers.get(peer.peerId);
|
|
6183
6247
|
if (!isConnected || !knownPeer) {
|
|
@@ -6215,7 +6279,6 @@ var ByteCaveClient = class {
|
|
|
6215
6279
|
console.debug("[ByteCave] Refresh: Peer already connected:", peer.peerId.slice(0, 12) + "...");
|
|
6216
6280
|
}
|
|
6217
6281
|
}
|
|
6218
|
-
break;
|
|
6219
6282
|
}
|
|
6220
6283
|
} catch (err) {
|
|
6221
6284
|
console.warn("[ByteCave] Refresh: Failed to get directory from relay:", err.message);
|
|
@@ -6236,10 +6299,7 @@ var ByteCaveClient = class {
|
|
|
6236
6299
|
return { success: false, error: "P2P node not initialized" };
|
|
6237
6300
|
}
|
|
6238
6301
|
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));
|
|
6302
|
+
const connectedPeerIds = allPeers.map((p) => p.toString());
|
|
6243
6303
|
console.log("[ByteCave] Store - connected storage peers:", connectedPeerIds.length);
|
|
6244
6304
|
console.log("[ByteCave] Store - knownPeers with registration info:", this.knownPeers.size);
|
|
6245
6305
|
if (connectedPeerIds.length === 0) {
|
|
@@ -6602,11 +6662,11 @@ Nonce: ${nonce}`;
|
|
|
6602
6662
|
* Check if a nodeId is registered in the on-chain registry
|
|
6603
6663
|
*/
|
|
6604
6664
|
async checkNodeRegistration(nodeId) {
|
|
6605
|
-
if (!this.
|
|
6665
|
+
if (!this.contractDiscovery) {
|
|
6606
6666
|
return true;
|
|
6607
6667
|
}
|
|
6608
6668
|
try {
|
|
6609
|
-
const registeredNodes = await this.
|
|
6669
|
+
const registeredNodes = await this.contractDiscovery.getActiveNodes();
|
|
6610
6670
|
return registeredNodes.some((node) => node.nodeId === nodeId);
|
|
6611
6671
|
} catch (error) {
|
|
6612
6672
|
console.warn("[ByteCave] Failed to check node registration:", error);
|
|
@@ -6667,6 +6727,7 @@ function ByteCaveProvider({
|
|
|
6667
6727
|
rpcUrl,
|
|
6668
6728
|
appId,
|
|
6669
6729
|
relayPeers = [],
|
|
6730
|
+
relayHttpUrl,
|
|
6670
6731
|
directNodeAddrs = []
|
|
6671
6732
|
}) {
|
|
6672
6733
|
const [connectionState, setConnectionState] = (0, import_react.useState)("disconnected");
|
|
@@ -6709,6 +6770,7 @@ function ByteCaveProvider({
|
|
|
6709
6770
|
appId,
|
|
6710
6771
|
directNodeAddrs,
|
|
6711
6772
|
relayPeers,
|
|
6773
|
+
relayHttpUrl,
|
|
6712
6774
|
maxPeers: 10,
|
|
6713
6775
|
connectionTimeout: 3e4
|
|
6714
6776
|
});
|
|
@@ -7261,6 +7323,7 @@ var TEST_EXPORT = "ByteCave Browser Package v1.0.0";
|
|
|
7261
7323
|
HashdAudio,
|
|
7262
7324
|
HashdImage,
|
|
7263
7325
|
HashdVideo,
|
|
7326
|
+
RelayDiscovery,
|
|
7264
7327
|
TEST_EXPORT,
|
|
7265
7328
|
clearHashdCache,
|
|
7266
7329
|
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-6R5YCAYU.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/provider.d.ts
CHANGED
|
@@ -52,8 +52,9 @@ interface ByteCaveProviderProps {
|
|
|
52
52
|
rpcUrl: string;
|
|
53
53
|
appId: string;
|
|
54
54
|
relayPeers?: string[];
|
|
55
|
+
relayHttpUrl?: string;
|
|
55
56
|
directNodeAddrs?: string[];
|
|
56
57
|
}
|
|
57
|
-
export declare function ByteCaveProvider({ children, vaultNodeRegistryAddress, contentRegistryAddress, rpcUrl, appId, relayPeers, directNodeAddrs }: ByteCaveProviderProps): React.JSX.Element;
|
|
58
|
+
export declare function ByteCaveProvider({ children, vaultNodeRegistryAddress, contentRegistryAddress, rpcUrl, appId, relayPeers, relayHttpUrl, directNodeAddrs }: ByteCaveProviderProps): React.JSX.Element;
|
|
58
59
|
export declare function useByteCaveContext(): ByteCaveContextValue;
|
|
59
60
|
export {};
|
package/dist/react/index.js
CHANGED
package/dist/types.d.ts
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,16 @@ export class ByteCaveClient {
|
|
|
46
47
|
connectionTimeout: 30000,
|
|
47
48
|
...config
|
|
48
49
|
};
|
|
49
|
-
|
|
50
|
+
|
|
51
|
+
// Initialize relay discovery using HTTP URL if provided
|
|
52
|
+
if (config.relayHttpUrl) {
|
|
53
|
+
this.relayDiscovery = new RelayDiscovery(config.relayHttpUrl);
|
|
54
|
+
console.log('[ByteCave] Using relay HTTP URL for peer discovery:', config.relayHttpUrl);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Initialize contract discovery if contract address is provided
|
|
50
58
|
if (config.vaultNodeRegistryAddress && config.rpcUrl) {
|
|
51
|
-
this.
|
|
59
|
+
this.contractDiscovery = new ContractDiscovery(config.vaultNodeRegistryAddress, config.rpcUrl);
|
|
52
60
|
}
|
|
53
61
|
}
|
|
54
62
|
|
|
@@ -73,9 +81,9 @@ export class ByteCaveClient {
|
|
|
73
81
|
bootstrapPeers.push(...this.config.directNodeAddrs);
|
|
74
82
|
}
|
|
75
83
|
|
|
76
|
-
//
|
|
84
|
+
// Add relay peers for circuit relay connections
|
|
77
85
|
if (this.config.relayPeers && this.config.relayPeers.length > 0) {
|
|
78
|
-
console.log('[ByteCave] Using relay peers
|
|
86
|
+
console.log('[ByteCave] Using relay peers for circuit relay:', this.config.relayPeers);
|
|
79
87
|
bootstrapPeers.push(...this.config.relayPeers);
|
|
80
88
|
}
|
|
81
89
|
|
|
@@ -144,8 +152,58 @@ export class ByteCaveClient {
|
|
|
144
152
|
// Initialize P2P protocol client with the libp2p node
|
|
145
153
|
p2pProtocolClient.setNode(this.node);
|
|
146
154
|
|
|
147
|
-
//
|
|
148
|
-
|
|
155
|
+
// FASTEST discovery: Query relay HTTP endpoint for connected peers
|
|
156
|
+
if (this.relayDiscovery) {
|
|
157
|
+
console.log('[ByteCave] Querying relay HTTP endpoint for instant peer list...');
|
|
158
|
+
try {
|
|
159
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
160
|
+
if (relayPeers.length > 0) {
|
|
161
|
+
console.log('[ByteCave] Got', relayPeers.length, 'peers from relay HTTP endpoint');
|
|
162
|
+
|
|
163
|
+
// Dial each peer using their relay circuit multiaddrs
|
|
164
|
+
for (const peer of relayPeers) {
|
|
165
|
+
try {
|
|
166
|
+
console.log('[ByteCave] Dialing peer:', peer.peerId.slice(0, 12) + '...');
|
|
167
|
+
|
|
168
|
+
let connected = false;
|
|
169
|
+
for (const addr of peer.multiaddrs) {
|
|
170
|
+
try {
|
|
171
|
+
const ma = multiaddr(addr);
|
|
172
|
+
await this.node.dial(ma as any);
|
|
173
|
+
connected = true;
|
|
174
|
+
console.log('[ByteCave] ✓ Connected via relay circuit');
|
|
175
|
+
break;
|
|
176
|
+
} catch (dialErr: any) {
|
|
177
|
+
console.warn('[ByteCave] Failed to dial:', dialErr.message);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (connected) {
|
|
182
|
+
// Fetch health data
|
|
183
|
+
const health = await p2pProtocolClient.getHealthFromPeer(peer.peerId);
|
|
184
|
+
if (health) {
|
|
185
|
+
this.knownPeers.set(peer.peerId, {
|
|
186
|
+
peerId: peer.peerId,
|
|
187
|
+
publicKey: health.publicKey || '',
|
|
188
|
+
contentTypes: health.contentTypes || 'all',
|
|
189
|
+
connected: true,
|
|
190
|
+
nodeId: health.nodeId
|
|
191
|
+
});
|
|
192
|
+
console.log('[ByteCave] ✓ Discovered peer via HTTP relay:', health.nodeId || peer.peerId.slice(0, 12));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
} catch (err: any) {
|
|
196
|
+
console.warn('[ByteCave] Failed to process peer from HTTP relay:', err.message);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
} catch (err: any) {
|
|
201
|
+
console.warn('[ByteCave] HTTP relay discovery failed, falling back to P2P directory:', err.message);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Fallback: Query relay for peer directory via P2P protocol
|
|
206
|
+
console.log('[ByteCave] Querying relay for peer directory via P2P...');
|
|
149
207
|
for (const relayAddr of bootstrapPeers) {
|
|
150
208
|
try {
|
|
151
209
|
// Extract relay peer ID from multiaddr
|
|
@@ -244,33 +302,24 @@ export class ByteCaveClient {
|
|
|
244
302
|
return;
|
|
245
303
|
}
|
|
246
304
|
|
|
247
|
-
|
|
248
|
-
...(this.config.directNodeAddrs || []),
|
|
249
|
-
...(this.config.relayPeers || [])
|
|
250
|
-
];
|
|
251
|
-
|
|
252
|
-
console.log('[ByteCave] Refreshing peer directory from relays...');
|
|
305
|
+
console.log('[ByteCave] Refreshing peer directory from relay HTTP endpoint...');
|
|
253
306
|
|
|
254
|
-
|
|
307
|
+
// Use HTTP relay discovery for instant refresh
|
|
308
|
+
if (this.relayDiscovery) {
|
|
255
309
|
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');
|
|
310
|
+
const relayPeers = await this.relayDiscovery.getConnectedPeers();
|
|
311
|
+
if (relayPeers.length > 0) {
|
|
312
|
+
console.log('[ByteCave] Refresh: Got', relayPeers.length, 'peers from relay HTTP endpoint');
|
|
264
313
|
|
|
265
314
|
// Check each peer and reconnect if disconnected
|
|
266
|
-
for (const peer of
|
|
315
|
+
for (const peer of relayPeers) {
|
|
267
316
|
const isConnected = this.node.getPeers().some(p => p.toString() === peer.peerId);
|
|
268
317
|
const knownPeer = this.knownPeers.get(peer.peerId);
|
|
269
318
|
|
|
270
319
|
if (!isConnected || !knownPeer) {
|
|
271
320
|
console.log('[ByteCave] Refresh: Reconnecting to peer:', peer.peerId.slice(0, 12) + '...');
|
|
272
321
|
|
|
273
|
-
// Try to dial using circuit
|
|
322
|
+
// Try to dial using relay circuit multiaddr
|
|
274
323
|
let connected = false;
|
|
275
324
|
for (const addr of peer.multiaddrs) {
|
|
276
325
|
try {
|
|
@@ -306,8 +355,6 @@ export class ByteCaveClient {
|
|
|
306
355
|
console.debug('[ByteCave] Refresh: Peer already connected:', peer.peerId.slice(0, 12) + '...');
|
|
307
356
|
}
|
|
308
357
|
}
|
|
309
|
-
|
|
310
|
-
break; // Successfully refreshed from one relay
|
|
311
358
|
}
|
|
312
359
|
} catch (err: any) {
|
|
313
360
|
console.warn('[ByteCave] Refresh: Failed to get directory from relay:', err.message);
|
|
@@ -330,15 +377,9 @@ export class ByteCaveClient {
|
|
|
330
377
|
return { success: false, error: 'P2P node not initialized' };
|
|
331
378
|
}
|
|
332
379
|
|
|
333
|
-
// Get all connected peers
|
|
380
|
+
// Get all connected peers
|
|
334
381
|
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));
|
|
382
|
+
const connectedPeerIds = allPeers.map(p => p.toString());
|
|
342
383
|
|
|
343
384
|
console.log('[ByteCave] Store - connected storage peers:', connectedPeerIds.length);
|
|
344
385
|
console.log('[ByteCave] Store - knownPeers with registration info:', this.knownPeers.size);
|
|
@@ -807,14 +848,14 @@ Nonce: ${nonce}`;
|
|
|
807
848
|
* Check if a nodeId is registered in the on-chain registry
|
|
808
849
|
*/
|
|
809
850
|
private async checkNodeRegistration(nodeId: string): Promise<boolean> {
|
|
810
|
-
if (!this.
|
|
851
|
+
if (!this.contractDiscovery) {
|
|
811
852
|
// No contract configured - skip registration check
|
|
812
853
|
return true;
|
|
813
854
|
}
|
|
814
855
|
|
|
815
856
|
try {
|
|
816
|
-
const registeredNodes = await this.
|
|
817
|
-
return registeredNodes.some(node => node.nodeId === nodeId);
|
|
857
|
+
const registeredNodes = await this.contractDiscovery.getActiveNodes();
|
|
858
|
+
return registeredNodes.some((node: any) => node.nodeId === nodeId);
|
|
818
859
|
} catch (error) {
|
|
819
860
|
console.warn('[ByteCave] Failed to check node registration:', error);
|
|
820
861
|
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/provider.tsx
CHANGED
|
@@ -55,6 +55,7 @@ interface ByteCaveProviderProps {
|
|
|
55
55
|
rpcUrl: string;
|
|
56
56
|
appId: string;
|
|
57
57
|
relayPeers?: string[];
|
|
58
|
+
relayHttpUrl?: string;
|
|
58
59
|
directNodeAddrs?: string[];
|
|
59
60
|
}
|
|
60
61
|
|
|
@@ -67,6 +68,7 @@ export function ByteCaveProvider({
|
|
|
67
68
|
rpcUrl,
|
|
68
69
|
appId,
|
|
69
70
|
relayPeers = [],
|
|
71
|
+
relayHttpUrl,
|
|
70
72
|
directNodeAddrs = []
|
|
71
73
|
}: ByteCaveProviderProps) {
|
|
72
74
|
const [connectionState, setConnectionState] = useState<ConnectionState>('disconnected');
|
|
@@ -117,6 +119,7 @@ export function ByteCaveProvider({
|
|
|
117
119
|
appId,
|
|
118
120
|
directNodeAddrs,
|
|
119
121
|
relayPeers,
|
|
122
|
+
relayHttpUrl,
|
|
120
123
|
maxPeers: 10,
|
|
121
124
|
connectionTimeout: 30000
|
|
122
125
|
});
|
package/src/types.ts
CHANGED
|
@@ -8,7 +8,8 @@ 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 node multiaddrs for circuit relay
|
|
11
|
+
relayPeers?: string[]; // Relay node multiaddrs for circuit relay (e.g., /ip4/127.0.0.1/tcp/4002/ws/p2p/...)
|
|
12
|
+
relayHttpUrl?: string; // Optional - Relay HTTP URL for instant peer discovery (e.g., http://localhost:9090)
|
|
12
13
|
maxPeers?: number;
|
|
13
14
|
connectionTimeout?: number;
|
|
14
15
|
}
|