ipfs-node 0.1.6__py3-none-any.whl
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.
- ipfs_node/__init__.py +4 -0
- ipfs_node/ipfs_files.py +233 -0
- ipfs_node/ipfs_node.py +230 -0
- ipfs_node/ipfs_peers.py +71 -0
- ipfs_node/ipfs_pubsub.py +474 -0
- ipfs_node/ipfs_tunnels.py +263 -0
- ipfs_node/libkubo/libkubo_linux_x86_64.h +237 -0
- ipfs_node/libkubo/libkubo_linux_x86_64.so +0 -0
- ipfs_node/utils/__init__.py +3 -0
- ipfs_node/utils/cid_utils.py +55 -0
- ipfs_node/utils/peer_utils.py +62 -0
- ipfs_node-0.1.6.dist-info/LICENSE +21 -0
- ipfs_node-0.1.6.dist-info/METADATA +134 -0
- ipfs_node-0.1.6.dist-info/RECORD +18 -0
- ipfs_node-0.1.6.dist-info/WHEEL +5 -0
- ipfs_node-0.1.6.dist-info/top_level.txt +2 -0
- libkubo/__init__.py +1 -0
- libkubo/libkubo_loader.py +45 -0
ipfs_node/__init__.py
ADDED
ipfs_node/ipfs_files.py
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
import ctypes
|
|
4
|
+
import shutil
|
|
5
|
+
import platform
|
|
6
|
+
import json
|
|
7
|
+
import time
|
|
8
|
+
import threading
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional, Union, List, Dict, Any, Callable, Tuple, Iterator, Set
|
|
12
|
+
from libkubo import libkubo, c_str, c_bool, from_c_str, ffi
|
|
13
|
+
|
|
14
|
+
from ipfs_tk_generics.files import BaseFiles
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class NodeFiles(BaseFiles):
|
|
18
|
+
def __init__(self, node):
|
|
19
|
+
self._node = node
|
|
20
|
+
self._repo_path = self._node._repo_path
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def read(self, cid: str, *args, **kwargs) -> bytes:
|
|
24
|
+
"""
|
|
25
|
+
Get bytes data from IPFS.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
cid: The Content Identifier of the data to retrieve.
|
|
29
|
+
Note: This method only works with file content, not directories.
|
|
30
|
+
For directories, use the download() method instead.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
bytes: The retrieved data.
|
|
34
|
+
"""
|
|
35
|
+
temp_file = None
|
|
36
|
+
temp_file_path = None
|
|
37
|
+
try:
|
|
38
|
+
# Create a temporary file to store the retrieved data
|
|
39
|
+
temp_file = tempfile.NamedTemporaryFile(delete=False)
|
|
40
|
+
temp_file_path = temp_file.name
|
|
41
|
+
temp_file.close()
|
|
42
|
+
|
|
43
|
+
# Get the file from IPFS
|
|
44
|
+
success = self.download(cid, temp_file_path)
|
|
45
|
+
if not success:
|
|
46
|
+
raise RuntimeError(f"Failed to retrieve data for CID: {cid}")
|
|
47
|
+
|
|
48
|
+
# Read the data from the temporary file
|
|
49
|
+
with open(temp_file_path, 'rb') as f:
|
|
50
|
+
return f.read()
|
|
51
|
+
except Exception as e:
|
|
52
|
+
raise RuntimeError(f"Error retrieving bytes from IPFS: {e}")
|
|
53
|
+
finally:
|
|
54
|
+
# Clean up the temporary file
|
|
55
|
+
if temp_file_path is not None and os.path.exists(temp_file_path):
|
|
56
|
+
try:
|
|
57
|
+
os.unlink(temp_file_path)
|
|
58
|
+
except Exception:
|
|
59
|
+
# Silently ignore cleanup errors
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
def publish(self, file_path: str, *args, **kwargs) -> str:
|
|
63
|
+
return self._add(file_path)
|
|
64
|
+
def _add(self, file_path: str, only_hash: bool = False, *args, **kwargs) -> str:
|
|
65
|
+
"""
|
|
66
|
+
Add a file to IPFS.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
file_path: Path to the file to add.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
str: The CID (Content Identifier) of the added file.
|
|
73
|
+
"""
|
|
74
|
+
if not os.path.exists(file_path):
|
|
75
|
+
raise FileNotFoundError(f"File not found: {file_path}")
|
|
76
|
+
|
|
77
|
+
repo_path = c_str(self._repo_path.encode('utf-8'))
|
|
78
|
+
file_path_c = c_str(os.path.abspath(file_path).encode('utf-8'))
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
cid_ptr = libkubo.AddFile(
|
|
82
|
+
repo_path, file_path_c, c_bool(only_hash))
|
|
83
|
+
if not cid_ptr:
|
|
84
|
+
raise RuntimeError("Failed to add file to IPFS")
|
|
85
|
+
|
|
86
|
+
# Copy the string content before freeing the pointer
|
|
87
|
+
cid = from_c_str(cid_ptr)
|
|
88
|
+
|
|
89
|
+
# Store the memory freeing operation in a separate try block
|
|
90
|
+
try:
|
|
91
|
+
# Free the memory allocated by C.CString in Go
|
|
92
|
+
libkubo.FreeString(cid_ptr)
|
|
93
|
+
except Exception as e:
|
|
94
|
+
print(f"Warning: Failed to free memory: {e}")
|
|
95
|
+
|
|
96
|
+
if not cid:
|
|
97
|
+
raise RuntimeError("Failed to add file to IPFS")
|
|
98
|
+
|
|
99
|
+
return cid
|
|
100
|
+
except Exception as e:
|
|
101
|
+
# Handle any exceptions during the process
|
|
102
|
+
raise RuntimeError(f"Error adding file to IPFS: {e}")
|
|
103
|
+
|
|
104
|
+
def download(self, cid: str, dest_path: str=".", **kwargs) -> bool:
|
|
105
|
+
"""
|
|
106
|
+
Retrieve a file or directory from IPFS by its CID.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
cid: The Content Identifier of the content to retrieve.
|
|
110
|
+
dest_path: Destination path where the file or directory will be saved.
|
|
111
|
+
- For a file: The complete file path including filename.
|
|
112
|
+
- For a directory: The path where the directory and its contents
|
|
113
|
+
will be placed. All directory contents will be created inside
|
|
114
|
+
this path.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
bool: True if the content was successfully retrieved, False otherwise.
|
|
118
|
+
"""
|
|
119
|
+
try:
|
|
120
|
+
dest_path = os.path.abspath(dest_path)
|
|
121
|
+
repo_path = c_str(self._repo_path.encode('utf-8'))
|
|
122
|
+
cid_c = c_str(cid.encode('utf-8'))
|
|
123
|
+
dest_path_c = c_str(os.path.abspath(dest_path).encode('utf-8'))
|
|
124
|
+
|
|
125
|
+
result = libkubo.Download(repo_path, cid_c, dest_path_c)
|
|
126
|
+
|
|
127
|
+
return result == 0
|
|
128
|
+
except Exception as e:
|
|
129
|
+
# Handle any exceptions during the process
|
|
130
|
+
raise RuntimeError(f"Error retrieving file from IPFS: {e}")
|
|
131
|
+
|
|
132
|
+
def pin(self, cid: str, recursive: bool = True) -> bool:
|
|
133
|
+
"""
|
|
134
|
+
Pin a CID to the local IPFS node.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
cid: The Content Identifier to pin.
|
|
138
|
+
recursive: Whether to recursively pin the object and its references.
|
|
139
|
+
Currently, only recursive pinning is supported.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
bool: True if the CID was successfully pinned, False otherwise.
|
|
143
|
+
"""
|
|
144
|
+
try:
|
|
145
|
+
repo_path = c_str(self._repo_path.encode('utf-8'))
|
|
146
|
+
cid_c = c_str(cid.encode('utf-8'))
|
|
147
|
+
|
|
148
|
+
result = libkubo.PinCID(repo_path, cid_c)
|
|
149
|
+
|
|
150
|
+
return result == 0
|
|
151
|
+
except Exception as e:
|
|
152
|
+
# Handle any exceptions during the process
|
|
153
|
+
raise RuntimeError(f"Error pinning CID: {e}")
|
|
154
|
+
|
|
155
|
+
def unpin(self, cid: str, recursive: bool = True) -> bool:
|
|
156
|
+
"""
|
|
157
|
+
Unpin a CID from the local IPFS node.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
cid: The Content Identifier to unpin.
|
|
161
|
+
recursive: Whether to recursively unpin the object and its references.
|
|
162
|
+
Currently, this parameter is ignored as all unpinning is recursive.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
bool: True if the CID was successfully unpinned, False otherwise.
|
|
166
|
+
"""
|
|
167
|
+
try:
|
|
168
|
+
repo_path = c_str(self._repo_path.encode('utf-8'))
|
|
169
|
+
cid_c = c_str(cid.encode('utf-8'))
|
|
170
|
+
|
|
171
|
+
result = libkubo.UnpinCID(repo_path, cid_c)
|
|
172
|
+
|
|
173
|
+
return result == 0
|
|
174
|
+
except Exception as e:
|
|
175
|
+
# Handle any exceptions during the process
|
|
176
|
+
raise RuntimeError(f"Error unpinning CID: {e}")
|
|
177
|
+
|
|
178
|
+
def list_pins(self, *args, **kwargs) -> list[str]:
|
|
179
|
+
"""
|
|
180
|
+
List all pinned CIDs in the local IPFS node.
|
|
181
|
+
|
|
182
|
+
Returns:
|
|
183
|
+
list[str]: A list of pinned CIDs.
|
|
184
|
+
"""
|
|
185
|
+
try:
|
|
186
|
+
repo_path = c_str(self._repo_path.encode('utf-8'))
|
|
187
|
+
|
|
188
|
+
pins_json_ptr = libkubo.ListPins(repo_path)
|
|
189
|
+
if not pins_json_ptr:
|
|
190
|
+
raise RuntimeError("Failed to list pins")
|
|
191
|
+
|
|
192
|
+
# Copy the string content before freeing the pointer
|
|
193
|
+
pins_json = from_c_str(pins_json_ptr)
|
|
194
|
+
|
|
195
|
+
# Store the memory freeing operation in a separate try block
|
|
196
|
+
try:
|
|
197
|
+
# Free the memory allocated by C.CString in Go
|
|
198
|
+
libkubo.FreeString(pins_json_ptr)
|
|
199
|
+
except Exception as e:
|
|
200
|
+
print(f"Warning: Failed to free memory: {e}")
|
|
201
|
+
|
|
202
|
+
if not pins_json:
|
|
203
|
+
return []
|
|
204
|
+
|
|
205
|
+
# Parse the JSON string into a Python list
|
|
206
|
+
pins = json.loads(pins_json)
|
|
207
|
+
return pins
|
|
208
|
+
except Exception as e:
|
|
209
|
+
# Handle any exceptions during the process
|
|
210
|
+
raise RuntimeError(f"Error listing pins: {e}")
|
|
211
|
+
|
|
212
|
+
def remove(self, cid: str) -> bool:
|
|
213
|
+
"""
|
|
214
|
+
Remove a pinned object.
|
|
215
|
+
|
|
216
|
+
This is an alias for unpin that makes the API more intuitive.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
cid: The Content Identifier to remove.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
bool: True if the CID was successfully removed, False otherwise.
|
|
223
|
+
"""
|
|
224
|
+
return self.unpin(cid)
|
|
225
|
+
|
|
226
|
+
def predict_cid(self, filepath: str, *args, **kwargs):
|
|
227
|
+
return self._add(filepath, only_hash=True)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def terminate(self):
|
|
231
|
+
pass
|
|
232
|
+
def __del__(self):
|
|
233
|
+
self.terminate()
|
ipfs_node/ipfs_node.py
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
import ctypes
|
|
4
|
+
import shutil
|
|
5
|
+
import platform
|
|
6
|
+
import json
|
|
7
|
+
import time
|
|
8
|
+
import threading
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional, Union, List, Dict, Any, Callable, Tuple, Iterator, Set
|
|
12
|
+
from .ipfs_pubsub import IPFSMessage, IPFSSubscription, NodePubsub
|
|
13
|
+
from libkubo import libkubo, c_str, from_c_str, ffi
|
|
14
|
+
from .ipfs_tunnels import NodeTunnels
|
|
15
|
+
from .ipfs_files import NodeFiles
|
|
16
|
+
from .ipfs_peers import NodePeers
|
|
17
|
+
|
|
18
|
+
from ipfs_tk_generics.client import IpfsClient
|
|
19
|
+
class IpfsNode(IpfsClient):
|
|
20
|
+
"""
|
|
21
|
+
Python wrapper for a Kubo IPFS node.
|
|
22
|
+
|
|
23
|
+
This class provides an interface to work with IPFS functionality
|
|
24
|
+
through the Kubo implementation.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(self, repo_path: Optional[str] = None, online: bool = True, enable_pubsub: bool = True):
|
|
28
|
+
"""
|
|
29
|
+
Initialize an IPFS node with a specific repository path.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
repo_path: Path to the IPFS repository. If None, a temporary
|
|
33
|
+
repository will be created.
|
|
34
|
+
online: Whether the node should connect to the IPFS network.
|
|
35
|
+
enable_pubsub: Whether to enable pubsub functionality.
|
|
36
|
+
"""
|
|
37
|
+
self._temp_dir = None
|
|
38
|
+
self._repo_path = repo_path
|
|
39
|
+
self._online = online
|
|
40
|
+
self._enable_pubsub = enable_pubsub
|
|
41
|
+
self._peer_id = None # Will be set when connecting to the network
|
|
42
|
+
# If no repo path is provided, create a temporary directory
|
|
43
|
+
if self._repo_path is None:
|
|
44
|
+
self._temp_dir = tempfile.TemporaryDirectory()
|
|
45
|
+
self._repo_path = self._temp_dir.name
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# Initialize the repository if it doesn't exist
|
|
49
|
+
if not os.path.exists(os.path.join(self._repo_path, "config")):
|
|
50
|
+
self._init_repo()
|
|
51
|
+
else:
|
|
52
|
+
print("Loading existing IPFS repo")
|
|
53
|
+
libkubo.RunNode(c_str(self._repo_path.encode('utf-8')))
|
|
54
|
+
|
|
55
|
+
# Get the node ID if online
|
|
56
|
+
if self._online:
|
|
57
|
+
self._peer_id = self.get_node_id()
|
|
58
|
+
self._pubsub = NodePubsub(self)
|
|
59
|
+
self._tunnels = NodeTunnels(self)
|
|
60
|
+
self._files = NodeFiles(self)
|
|
61
|
+
self._peers = NodePeers(self)
|
|
62
|
+
|
|
63
|
+
# Enable pubsub if requested
|
|
64
|
+
if self._enable_pubsub and self._online:
|
|
65
|
+
self.pubsub._enable_pubsub_config()
|
|
66
|
+
@property
|
|
67
|
+
def tunnels(self)->NodeTunnels:
|
|
68
|
+
return self._tunnels
|
|
69
|
+
@property
|
|
70
|
+
def pubsub(self)->NodePubsub:
|
|
71
|
+
return self._pubsub
|
|
72
|
+
@property
|
|
73
|
+
def files(self)->NodeFiles:
|
|
74
|
+
return self._files
|
|
75
|
+
@property
|
|
76
|
+
def peers (self)->NodePeers:
|
|
77
|
+
return self._peers
|
|
78
|
+
def _run(self):
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
def _stop(self):
|
|
82
|
+
pass
|
|
83
|
+
|
|
84
|
+
def _init_repo(self):
|
|
85
|
+
"""Initialize the IPFS repository."""
|
|
86
|
+
repo_path = c_str(self._repo_path.encode('utf-8'))
|
|
87
|
+
result = libkubo.CreateRepo(repo_path)
|
|
88
|
+
|
|
89
|
+
if result < 0:
|
|
90
|
+
raise RuntimeError(
|
|
91
|
+
f"Failed to initialize IPFS repository: {result}")
|
|
92
|
+
# print(f"Initalised repo at: {repo_path}")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def terminate(self):
|
|
99
|
+
"""Close the IPFS node and clean up resources."""
|
|
100
|
+
self._pubsub.terminate()
|
|
101
|
+
self._tunnels.terminate()
|
|
102
|
+
self._files.terminate()
|
|
103
|
+
self._peers.terminate()
|
|
104
|
+
# Force cleanup of the node in Go
|
|
105
|
+
if self._repo_path:
|
|
106
|
+
try:
|
|
107
|
+
repo_path = c_str(self._repo_path.encode('utf-8'))
|
|
108
|
+
print("Cleaning up node...")
|
|
109
|
+
libkubo.CleanupNode(repo_path)
|
|
110
|
+
# print(f"Node for repo {self._repo_path} explicitly cleaned up")
|
|
111
|
+
except Exception as e:
|
|
112
|
+
print(f"Warning: Error cleaning up node: {e}")
|
|
113
|
+
|
|
114
|
+
# Clean up temporary directory if one was created
|
|
115
|
+
if self._temp_dir is not None:
|
|
116
|
+
self._temp_dir.cleanup()
|
|
117
|
+
self._temp_dir = None
|
|
118
|
+
|
|
119
|
+
def __enter__(self):
|
|
120
|
+
"""Support for context manager protocol."""
|
|
121
|
+
return self
|
|
122
|
+
|
|
123
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
124
|
+
"""Clean up when exiting the context manager."""
|
|
125
|
+
self.terminate()
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _ipfs_host_ip(self,):
|
|
129
|
+
return "127.0.0.1"
|
|
130
|
+
|
|
131
|
+
def test_get_string(self) -> str:
|
|
132
|
+
"""Test function to check basic string passing from Go to Python"""
|
|
133
|
+
try:
|
|
134
|
+
id_ptr = libkubo.TestGetString()
|
|
135
|
+
if not id_ptr:
|
|
136
|
+
print("TEST: No string returned from TestGetString")
|
|
137
|
+
return ""
|
|
138
|
+
|
|
139
|
+
test_str = from_c_str(id_ptr)
|
|
140
|
+
# print(f"TEST: String from Go: '{test_str}', length: {len(test_str)}")
|
|
141
|
+
return test_str
|
|
142
|
+
except Exception as e:
|
|
143
|
+
print(f"TEST ERROR: {e}")
|
|
144
|
+
return f"ERROR: {e}"
|
|
145
|
+
|
|
146
|
+
def get_node_id(self) -> str:
|
|
147
|
+
"""
|
|
148
|
+
Get the peer ID of this IPFS node.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
str: The peer ID of the node, or empty string if not available.
|
|
152
|
+
"""
|
|
153
|
+
if not self._online:
|
|
154
|
+
print("IPFS: not online")
|
|
155
|
+
return ""
|
|
156
|
+
|
|
157
|
+
# try to get the node ID
|
|
158
|
+
try:
|
|
159
|
+
repo_path = c_str(self._repo_path.encode('utf-8'))
|
|
160
|
+
|
|
161
|
+
id_ptr = libkubo.GetNodeID(repo_path)
|
|
162
|
+
|
|
163
|
+
if not id_ptr:
|
|
164
|
+
print("IPFS: NO ID_PTR")
|
|
165
|
+
return ""
|
|
166
|
+
|
|
167
|
+
# Copy the string content
|
|
168
|
+
peer_id = from_c_str(id_ptr)
|
|
169
|
+
|
|
170
|
+
# Don't free the memory - let Go's finalizer handle it
|
|
171
|
+
# The memory will be freed when Go's garbage collector runs
|
|
172
|
+
|
|
173
|
+
# Strip the prefix we added for debugging
|
|
174
|
+
if peer_id.startswith("ID:"):
|
|
175
|
+
peer_id = peer_id[3:]
|
|
176
|
+
|
|
177
|
+
return peer_id
|
|
178
|
+
except Exception as e:
|
|
179
|
+
print(f"IPFS ERROR in get_node_id: {e}")
|
|
180
|
+
return f"ERROR: {e}"
|
|
181
|
+
|
|
182
|
+
@property
|
|
183
|
+
def peer_id(self) -> str:
|
|
184
|
+
"""Get the peer ID of this node."""
|
|
185
|
+
if not self._peer_id:
|
|
186
|
+
self._peer_id = self.get_node_id()
|
|
187
|
+
return self._peer_id
|
|
188
|
+
|
|
189
|
+
@classmethod
|
|
190
|
+
def ephemeral(cls, online: bool = True, enable_pubsub: bool = True):
|
|
191
|
+
"""
|
|
192
|
+
Create an ephemeral IPFS node with a temporary repository.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
online: Whether the node should connect to the IPFS network.
|
|
196
|
+
enable_pubsub: Whether to enable pubsub functionality.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
IpfsNode: A new IPFS node instance with a temporary repository.
|
|
200
|
+
"""
|
|
201
|
+
return cls(None, online, enable_pubsub)
|
|
202
|
+
def get_addrs(self):
|
|
203
|
+
if not self._online:
|
|
204
|
+
print("IPFS: not online")
|
|
205
|
+
return ""
|
|
206
|
+
|
|
207
|
+
# try to get the node ID
|
|
208
|
+
try:
|
|
209
|
+
repo_path = c_str(self._repo_path.encode('utf-8'))
|
|
210
|
+
|
|
211
|
+
id_ptr = libkubo.GetNodeMultiAddrs(repo_path)
|
|
212
|
+
|
|
213
|
+
if not id_ptr:
|
|
214
|
+
print("IPFS: NO ID_PTR")
|
|
215
|
+
return ""
|
|
216
|
+
|
|
217
|
+
# Copy the string content
|
|
218
|
+
json_data = from_c_str(id_ptr)
|
|
219
|
+
|
|
220
|
+
# Don't free the memory - let Go's finalizer handle it
|
|
221
|
+
# The memory will be freed when Go's garbage collector runs
|
|
222
|
+
|
|
223
|
+
# Strip the prefix we added for debugging
|
|
224
|
+
return json.loads(json_data)
|
|
225
|
+
except Exception as e:
|
|
226
|
+
print(f"IPFS ERROR in get_node_id: {e}")
|
|
227
|
+
return f"ERROR: {e}"
|
|
228
|
+
|
|
229
|
+
def __del__(self):
|
|
230
|
+
self.terminate()
|
ipfs_node/ipfs_peers.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import tempfile
|
|
3
|
+
import ctypes
|
|
4
|
+
import shutil
|
|
5
|
+
import platform
|
|
6
|
+
import json
|
|
7
|
+
import time
|
|
8
|
+
import threading
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
from typing import Optional, Union, List, Dict, Any, Callable, Tuple, Iterator, Set
|
|
12
|
+
from libkubo import libkubo, c_str, from_c_str, ffi
|
|
13
|
+
DEF_FIND_TIMEOUT=10
|
|
14
|
+
from ipfs_tk_generics.peers import BasePeers
|
|
15
|
+
class NodePeers(BasePeers):
|
|
16
|
+
def __init__(self, node):
|
|
17
|
+
self._node = node
|
|
18
|
+
self._repo_path = self._node._repo_path
|
|
19
|
+
def find(self, peer_id:str, timeout=DEF_FIND_TIMEOUT)->list[str]:
|
|
20
|
+
data = from_c_str(
|
|
21
|
+
libkubo.FindPeer(c_str(self._repo_path), c_str(peer_id), timeout),
|
|
22
|
+
)
|
|
23
|
+
return json.loads(data)
|
|
24
|
+
def list_peers(self)->list[str]:
|
|
25
|
+
data = from_c_str(
|
|
26
|
+
libkubo.ListPeers(c_str(self._repo_path))
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
return json.loads(data)
|
|
30
|
+
def list_ids(self)->list[str]:
|
|
31
|
+
data = from_c_str(
|
|
32
|
+
libkubo.ListPeersIDs(c_str(self._repo_path))
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
return json.loads(data)
|
|
36
|
+
def connect(self, peer_addr: str) -> bool:
|
|
37
|
+
"""
|
|
38
|
+
Connect to an IPFS peer.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
peer_addr: Multiaddress of the peer to connect to.
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
bool: True if successfully connected, False otherwise.
|
|
45
|
+
"""
|
|
46
|
+
if not self._node._online:
|
|
47
|
+
raise RuntimeError("Cannot connect to peers in offline mode")
|
|
48
|
+
|
|
49
|
+
try:
|
|
50
|
+
repo_path = c_str(self._repo_path.encode('utf-8'))
|
|
51
|
+
peer_addr_c = c_str(peer_addr.encode('utf-8'))
|
|
52
|
+
|
|
53
|
+
result = libkubo.ConnectToPeer(repo_path, peer_addr_c)
|
|
54
|
+
|
|
55
|
+
return result == 0
|
|
56
|
+
except Exception as e:
|
|
57
|
+
# Handle any exceptions during the process
|
|
58
|
+
raise RuntimeError(f"Error connecting to peer: {e}")
|
|
59
|
+
def list_peers(self):
|
|
60
|
+
data = from_c_str(
|
|
61
|
+
libkubo.ListPeers(c_str(self._repo_path))
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return json.loads(data)
|
|
65
|
+
def is_connected(self, peer_id:str, *args, **kwargs):
|
|
66
|
+
#TODO: replace with ping
|
|
67
|
+
return peer_id in self.list_ids()
|
|
68
|
+
def terminate(self):
|
|
69
|
+
pass
|
|
70
|
+
def __del__(self):
|
|
71
|
+
self.terminate()
|