synapse-filecoin-sdk 0.1.0__tar.gz → 0.2.0__tar.gz
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.
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/PKG-INFO +53 -6
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/README.md +48 -3
- synapse_filecoin_sdk-0.2.0/full_demo.py +224 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/pyproject.toml +6 -3
- synapse_filecoin_sdk-0.2.0/src/pynapse/_version.py +1 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/piece.py +4 -9
- synapse_filecoin_sdk-0.2.0/src/pynapse/integrations/__init__.py +4 -0
- synapse_filecoin_sdk-0.2.0/src/pynapse/integrations/langchain.py +214 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/tests/test_piece.py +3 -2
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/uv.lock +552 -5
- synapse_filecoin_sdk-0.1.0/src/pynapse/_version.py +0 -1
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/.github/workflows/publish-pypi.yml +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/.gitignore +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/LICENSE.md +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/QUICKSTART.md +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/demo.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/get_usdfc.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/abi_registry.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/addresses.json +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/erc20_abi.json +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/errorsAbi.json +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/filecoinPayV1Abi.json +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/filecoinWarmStorageServiceAbi.json +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/filecoinWarmStorageServiceStateViewAbi.json +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/generated.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/payments_abi.json +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/pdpVerifierAbi.json +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/providerIdSetAbi.json +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/serviceProviderRegistryAbi.json +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/contracts/sessionKeyRegistryAbi.json +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/abis.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/chains.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/constants.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/errors.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/rand.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/typed_data.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/core/utils.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/evm/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/evm/client.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/filbeam/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/filbeam/service.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/payments/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/payments/service.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/pdp/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/pdp/server.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/pdp/types.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/pdp/verifier.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/retriever/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/retriever/async_chain.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/retriever/chain.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/session/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/session/key.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/session/permissions.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/session/registry.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/sp_registry/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/sp_registry/capabilities.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/sp_registry/pdp_capabilities.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/sp_registry/service.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/sp_registry/types.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/storage/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/storage/async_context.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/storage/async_manager.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/storage/context.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/storage/manager.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/synapse.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/utils/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/utils/constants.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/utils/errors.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/utils/metadata.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/utils/piece_url.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/warm_storage/__init__.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/src/pynapse/warm_storage/service.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/tests/test_async_storage.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/tests/test_chains.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/tests/test_metadata.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/tests/test_pdp_capabilities.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/tests/test_piece_url.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/tests/test_rand.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/tests/test_session_permissions.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/tests/test_storage_manager.py +0 -0
- {synapse_filecoin_sdk-0.1.0 → synapse_filecoin_sdk-0.2.0}/tests/test_utils.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: synapse-filecoin-sdk
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
4
4
|
Summary: Python SDK for Filecoin Onchain Cloud (Synapse)
|
|
5
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
5
|
+
Project-URL: Homepage, https://github.com/anjor/pynapse
|
|
6
|
+
Project-URL: Repository, https://github.com/anjor/pynapse
|
|
7
7
|
Author: FilOz / Data Preservation Programs
|
|
8
8
|
License: Apache-2.0 OR MIT
|
|
9
9
|
License-File: LICENSE.md
|
|
@@ -18,6 +18,8 @@ Requires-Python: >=3.11
|
|
|
18
18
|
Requires-Dist: httpx<0.28.0,>=0.25.0
|
|
19
19
|
Requires-Dist: multiformats<0.4.0,>=0.3.1
|
|
20
20
|
Requires-Dist: web3<7,>=6.0.0
|
|
21
|
+
Provides-Extra: langchain
|
|
22
|
+
Requires-Dist: langchain-core<0.4,>=0.1.0; extra == 'langchain'
|
|
21
23
|
Provides-Extra: test
|
|
22
24
|
Requires-Dist: pytest-asyncio<0.24,>=0.23.0; extra == 'test'
|
|
23
25
|
Requires-Dist: pytest<9,>=8.0.0; extra == 'test'
|
|
@@ -49,10 +51,55 @@ Python import: `pynapse`
|
|
|
49
51
|
pip install synapse-filecoin-sdk
|
|
50
52
|
```
|
|
51
53
|
|
|
54
|
+
## Supported Networks
|
|
55
|
+
|
|
56
|
+
Both **Filecoin Mainnet** and **Calibration testnet** are supported:
|
|
57
|
+
|
|
58
|
+
```python
|
|
59
|
+
from pynapse import AsyncSynapse
|
|
60
|
+
|
|
61
|
+
# Mainnet
|
|
62
|
+
synapse = await AsyncSynapse.create(
|
|
63
|
+
rpc_url="https://api.node.glif.io/rpc/v1",
|
|
64
|
+
chain="mainnet",
|
|
65
|
+
private_key=PRIVATE_KEY
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Calibration testnet (for testing)
|
|
69
|
+
synapse = await AsyncSynapse.create(
|
|
70
|
+
rpc_url="https://api.calibration.node.glif.io/rpc/v1",
|
|
71
|
+
chain="calibration",
|
|
72
|
+
private_key=PRIVATE_KEY
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
See [QUICKSTART.md](QUICKSTART.md) for a full tutorial using Calibration testnet.
|
|
77
|
+
|
|
52
78
|
## CommP / PieceCID
|
|
53
79
|
|
|
54
80
|
`pynapse` uses `stream-commp` from `go-fil-commp-hashhash` for PieceCID calculation.
|
|
55
|
-
|
|
81
|
+
|
|
82
|
+
### Install `stream-commp`
|
|
83
|
+
|
|
84
|
+
`stream-commp` is an external runtime dependency and is not installed by `pip`.
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
git clone https://github.com/filecoin-project/go-fil-commp-hashhash.git
|
|
88
|
+
cd go-fil-commp-hashhash/cmd/stream-commp
|
|
89
|
+
go build -o stream-commp .
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Install it to your PATH (for example `/usr/local/bin`) or set:
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
export PYNAPSE_COMMP_HELPER=/absolute/path/to/stream-commp
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Verify:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
stream-commp --help
|
|
102
|
+
```
|
|
56
103
|
|
|
57
104
|
## License
|
|
58
105
|
|
|
@@ -67,8 +114,8 @@ Publishing is automated via GitHub Actions in `.github/workflows/publish-pypi.ym
|
|
|
67
114
|
3. Tag a release and push the tag:
|
|
68
115
|
|
|
69
116
|
```bash
|
|
70
|
-
git tag v0.
|
|
71
|
-
git push origin v0.
|
|
117
|
+
git tag v0.2.0
|
|
118
|
+
git push origin v0.2.0
|
|
72
119
|
```
|
|
73
120
|
|
|
74
121
|
The workflow builds the package, runs `twine check`, and publishes to PyPI via OIDC (no API token required).
|
|
@@ -24,10 +24,55 @@ Python import: `pynapse`
|
|
|
24
24
|
pip install synapse-filecoin-sdk
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
## Supported Networks
|
|
28
|
+
|
|
29
|
+
Both **Filecoin Mainnet** and **Calibration testnet** are supported:
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from pynapse import AsyncSynapse
|
|
33
|
+
|
|
34
|
+
# Mainnet
|
|
35
|
+
synapse = await AsyncSynapse.create(
|
|
36
|
+
rpc_url="https://api.node.glif.io/rpc/v1",
|
|
37
|
+
chain="mainnet",
|
|
38
|
+
private_key=PRIVATE_KEY
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# Calibration testnet (for testing)
|
|
42
|
+
synapse = await AsyncSynapse.create(
|
|
43
|
+
rpc_url="https://api.calibration.node.glif.io/rpc/v1",
|
|
44
|
+
chain="calibration",
|
|
45
|
+
private_key=PRIVATE_KEY
|
|
46
|
+
)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
See [QUICKSTART.md](QUICKSTART.md) for a full tutorial using Calibration testnet.
|
|
50
|
+
|
|
27
51
|
## CommP / PieceCID
|
|
28
52
|
|
|
29
53
|
`pynapse` uses `stream-commp` from `go-fil-commp-hashhash` for PieceCID calculation.
|
|
30
|
-
|
|
54
|
+
|
|
55
|
+
### Install `stream-commp`
|
|
56
|
+
|
|
57
|
+
`stream-commp` is an external runtime dependency and is not installed by `pip`.
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
git clone https://github.com/filecoin-project/go-fil-commp-hashhash.git
|
|
61
|
+
cd go-fil-commp-hashhash/cmd/stream-commp
|
|
62
|
+
go build -o stream-commp .
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Install it to your PATH (for example `/usr/local/bin`) or set:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
export PYNAPSE_COMMP_HELPER=/absolute/path/to/stream-commp
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Verify:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
stream-commp --help
|
|
75
|
+
```
|
|
31
76
|
|
|
32
77
|
## License
|
|
33
78
|
|
|
@@ -42,8 +87,8 @@ Publishing is automated via GitHub Actions in `.github/workflows/publish-pypi.ym
|
|
|
42
87
|
3. Tag a release and push the tag:
|
|
43
88
|
|
|
44
89
|
```bash
|
|
45
|
-
git tag v0.
|
|
46
|
-
git push origin v0.
|
|
90
|
+
git tag v0.2.0
|
|
91
|
+
git push origin v0.2.0
|
|
47
92
|
```
|
|
48
93
|
|
|
49
94
|
The workflow builds the package, runs `twine check`, and publishes to PyPI via OIDC (no API token required).
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
pynapse Full Demo - Complete end-to-end test on Filecoin Calibration
|
|
4
|
+
Handles all setup steps: deposit, service approval, then upload/download
|
|
5
|
+
"""
|
|
6
|
+
import asyncio
|
|
7
|
+
import sys
|
|
8
|
+
from web3 import AsyncWeb3
|
|
9
|
+
from eth_account import Account
|
|
10
|
+
|
|
11
|
+
# Demo wallet (Calibration testnet only)
|
|
12
|
+
PRIVATE_KEY = "0x7666381bff20b2f0819c77d3846b1d22637be4996012aaaff25e8b93f73a6985"
|
|
13
|
+
RPC_URL = "https://api.calibration.node.glif.io/rpc/v1"
|
|
14
|
+
CHAIN_ID = 314159
|
|
15
|
+
|
|
16
|
+
# Contract addresses (Calibration)
|
|
17
|
+
USDFC = "0xb3042734b608a1B16e9e86B374A3f3e389B4cDf0"
|
|
18
|
+
PAYMENTS = "0x09a0fDc2723fAd1A7b8e3e00eE5DF73841df55a0" # FilecoinPay (updated by sub-agent)
|
|
19
|
+
FWSS = "0x02925630df557F957f70E112bA06e50965417CA0" # WarmStorage service
|
|
20
|
+
|
|
21
|
+
# ABIs
|
|
22
|
+
ERC20_ABI = [
|
|
23
|
+
{'inputs': [{'name': 'account', 'type': 'address'}], 'name': 'balanceOf', 'outputs': [{'name': '', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'},
|
|
24
|
+
{'inputs': [{'name': 'spender', 'type': 'address'}, {'name': 'amount', 'type': 'uint256'}], 'name': 'approve', 'outputs': [{'name': '', 'type': 'bool'}], 'stateMutability': 'nonpayable', 'type': 'function'},
|
|
25
|
+
{'inputs': [{'name': 'owner', 'type': 'address'}, {'name': 'spender', 'type': 'address'}], 'name': 'allowance', 'outputs': [{'name': '', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'},
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
PAYMENTS_ABI = [
|
|
29
|
+
# accounts(token, account) returns (funds, lockupCurrent, lockupRate, lockupLastSettledAt)
|
|
30
|
+
{'inputs': [{'name': 'token', 'type': 'address'}, {'name': 'account', 'type': 'address'}], 'name': 'accounts', 'outputs': [{'name': 'funds', 'type': 'uint256'}, {'name': 'lockupCurrent', 'type': 'uint256'}, {'name': 'lockupRate', 'type': 'uint256'}, {'name': 'lockupLastSettledAt', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'},
|
|
31
|
+
{'inputs': [{'name': 'token', 'type': 'address'}, {'name': 'to', 'type': 'address'}, {'name': 'amount', 'type': 'uint256'}], 'name': 'deposit', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'},
|
|
32
|
+
# operatorApprovals(token, client, operator) - note: 3 args not 2
|
|
33
|
+
{'inputs': [{'name': 'token', 'type': 'address'}, {'name': 'client', 'type': 'address'}, {'name': 'operator', 'type': 'address'}], 'name': 'operatorApprovals', 'outputs': [{'name': 'approved', 'type': 'bool'}, {'name': 'rateAllowance', 'type': 'uint256'}, {'name': 'lockupAllowance', 'type': 'uint256'}, {'name': 'rateUsage', 'type': 'uint256'}, {'name': 'lockupUsage', 'type': 'uint256'}, {'name': 'maxLockupPeriod', 'type': 'uint256'}], 'stateMutability': 'view', 'type': 'function'},
|
|
34
|
+
{'inputs': [{'name': 'token', 'type': 'address'}, {'name': 'operator', 'type': 'address'}, {'name': 'approved', 'type': 'bool'}, {'name': 'rateAllowance', 'type': 'uint256'}, {'name': 'lockupAllowance', 'type': 'uint256'}, {'name': 'maxLockupPeriod', 'type': 'uint256'}], 'name': 'setOperatorApproval', 'outputs': [], 'stateMutability': 'nonpayable', 'type': 'function'},
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
async def send_tx(w3, account, tx_data, description):
|
|
39
|
+
"""Send a transaction with proper Filecoin gas handling"""
|
|
40
|
+
print(f" → {description}...")
|
|
41
|
+
|
|
42
|
+
nonce = await w3.eth.get_transaction_count(account.address)
|
|
43
|
+
|
|
44
|
+
# Get gas estimate
|
|
45
|
+
try:
|
|
46
|
+
gas = await w3.eth.estimate_gas({**tx_data, 'from': account.address})
|
|
47
|
+
gas = int(gas * 1.2) # 20% buffer
|
|
48
|
+
except Exception as e:
|
|
49
|
+
print(f" Gas estimate failed: {e}")
|
|
50
|
+
gas = 30000000
|
|
51
|
+
|
|
52
|
+
# Build transaction with EIP-1559 style fees for Filecoin
|
|
53
|
+
base_fee = await w3.eth.gas_price
|
|
54
|
+
|
|
55
|
+
tx = {
|
|
56
|
+
**tx_data,
|
|
57
|
+
'from': account.address,
|
|
58
|
+
'nonce': nonce,
|
|
59
|
+
'gas': gas,
|
|
60
|
+
'maxFeePerGas': base_fee * 2,
|
|
61
|
+
'maxPriorityFeePerGas': base_fee,
|
|
62
|
+
'chainId': CHAIN_ID,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
signed = account.sign_transaction(tx)
|
|
66
|
+
tx_hash = await w3.eth.send_raw_transaction(signed.rawTransaction)
|
|
67
|
+
print(f" Tx: {tx_hash.hex()[:20]}...")
|
|
68
|
+
|
|
69
|
+
receipt = await w3.eth.wait_for_transaction_receipt(tx_hash, timeout=120)
|
|
70
|
+
if receipt.status == 1:
|
|
71
|
+
print(f" ✅ Success (gas: {receipt.gasUsed:,})")
|
|
72
|
+
return True
|
|
73
|
+
else:
|
|
74
|
+
print(f" ❌ Failed")
|
|
75
|
+
return False
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
async def main():
|
|
79
|
+
print("=" * 60)
|
|
80
|
+
print("pynapse Full Demo - Filecoin Calibration Testnet")
|
|
81
|
+
print("=" * 60)
|
|
82
|
+
|
|
83
|
+
# Connect
|
|
84
|
+
w3 = AsyncWeb3(AsyncWeb3.AsyncHTTPProvider(RPC_URL))
|
|
85
|
+
account = Account.from_key(PRIVATE_KEY)
|
|
86
|
+
print(f"\nWallet: {account.address}")
|
|
87
|
+
|
|
88
|
+
usdfc = w3.eth.contract(address=USDFC, abi=ERC20_ABI)
|
|
89
|
+
payments = w3.eth.contract(address=PAYMENTS, abi=PAYMENTS_ABI)
|
|
90
|
+
|
|
91
|
+
# Check balances
|
|
92
|
+
print("\n[1/5] Checking balances...")
|
|
93
|
+
fil_balance = await w3.eth.get_balance(account.address)
|
|
94
|
+
usdfc_balance = await usdfc.functions.balanceOf(account.address).call()
|
|
95
|
+
print(f" FIL: {fil_balance / 1e18:.4f} tFIL")
|
|
96
|
+
print(f" USDFC (wallet): {usdfc_balance / 1e18:.2f}")
|
|
97
|
+
|
|
98
|
+
if fil_balance < 1e18:
|
|
99
|
+
print("\n❌ Need more tFIL for gas. Get from: https://faucet.calibnet.chainsafe-fil.io/")
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
# Check payments contract balance using accounts(token, account)
|
|
103
|
+
try:
|
|
104
|
+
funds, _, _, _ = await payments.functions.accounts(USDFC, account.address).call()
|
|
105
|
+
payments_balance = int(funds)
|
|
106
|
+
print(f" USDFC (payments): {payments_balance / 1e18:.2f}")
|
|
107
|
+
except Exception as e:
|
|
108
|
+
payments_balance = 0
|
|
109
|
+
print(f" USDFC (payments): 0 (query failed: {e})")
|
|
110
|
+
|
|
111
|
+
# Check if we have enough funds (either in wallet or already deposited)
|
|
112
|
+
total_usdfc = usdfc_balance + payments_balance
|
|
113
|
+
if total_usdfc < 10e18:
|
|
114
|
+
print(f"\n❌ Need more USDFC. Total: {total_usdfc / 1e18:.2f}, need at least 10")
|
|
115
|
+
print(" Mint at: https://stg.usdfc.net")
|
|
116
|
+
return
|
|
117
|
+
|
|
118
|
+
# Step 2: Approve USDFC for payments contract (only if we have wallet USDFC)
|
|
119
|
+
print("\n[2/5] Checking USDFC allowance...")
|
|
120
|
+
if usdfc_balance > 0:
|
|
121
|
+
allowance = await usdfc.functions.allowance(account.address, PAYMENTS).call()
|
|
122
|
+
print(f" Current allowance: {allowance / 1e18:.2f}")
|
|
123
|
+
|
|
124
|
+
if allowance < usdfc_balance:
|
|
125
|
+
print(" Need to approve...")
|
|
126
|
+
tx_data = await usdfc.functions.approve(PAYMENTS, 2**256 - 1).build_transaction({'from': account.address})
|
|
127
|
+
if not await send_tx(w3, account, tx_data, "Approving USDFC"):
|
|
128
|
+
return
|
|
129
|
+
else:
|
|
130
|
+
print(" ✅ Already approved")
|
|
131
|
+
else:
|
|
132
|
+
print(" ✅ Skipped (no wallet USDFC to approve)")
|
|
133
|
+
|
|
134
|
+
# Step 3: Deposit USDFC to payments (only if we need more and have wallet USDFC)
|
|
135
|
+
print("\n[3/5] Depositing USDFC to payments contract...")
|
|
136
|
+
min_required = int(10e18) # Need at least 10 USDFC
|
|
137
|
+
|
|
138
|
+
if payments_balance >= min_required:
|
|
139
|
+
print(f" ✅ Already have {payments_balance/1e18:.2f} USDFC deposited")
|
|
140
|
+
elif usdfc_balance > 0:
|
|
141
|
+
deposit_amount = min(usdfc_balance, int(50e18)) # Deposit up to 50 USDFC
|
|
142
|
+
tx_data = await payments.functions.deposit(USDFC, account.address, deposit_amount).build_transaction({'from': account.address})
|
|
143
|
+
if not await send_tx(w3, account, tx_data, f"Depositing {deposit_amount/1e18:.0f} USDFC"):
|
|
144
|
+
print(" ⚠️ Deposit failed - may need different approach")
|
|
145
|
+
# Refresh payments balance
|
|
146
|
+
payments_balance = await payments.functions.balanceOf(USDFC).call({'from': account.address})
|
|
147
|
+
else:
|
|
148
|
+
print(" ⚠️ No wallet USDFC to deposit, hoping payments balance is sufficient")
|
|
149
|
+
|
|
150
|
+
# Step 4: Approve FWSS service
|
|
151
|
+
print("\n[4/5] Checking FWSS operator approval...")
|
|
152
|
+
try:
|
|
153
|
+
# operatorApprovals(token, client, operator)
|
|
154
|
+
approval = await payments.functions.operatorApprovals(USDFC, account.address, FWSS).call()
|
|
155
|
+
is_approved = approval[0]
|
|
156
|
+
print(f" Approved: {is_approved}")
|
|
157
|
+
except Exception as e:
|
|
158
|
+
is_approved = False
|
|
159
|
+
print(f" Query failed: {e}")
|
|
160
|
+
|
|
161
|
+
if not is_approved:
|
|
162
|
+
print(" Need to approve operator...")
|
|
163
|
+
# Approve with generous limits
|
|
164
|
+
rate_allowance = int(1e24) # Large allowance
|
|
165
|
+
lockup_allowance = int(1e24)
|
|
166
|
+
max_lockup_period = 365 * 24 * 60 * 60 # 1 year in seconds
|
|
167
|
+
|
|
168
|
+
tx_data = await payments.functions.setOperatorApproval(
|
|
169
|
+
USDFC, FWSS, True, rate_allowance, lockup_allowance, max_lockup_period
|
|
170
|
+
).build_transaction({'from': account.address})
|
|
171
|
+
if not await send_tx(w3, account, tx_data, "Approving FWSS operator"):
|
|
172
|
+
print(" ⚠️ Operator approval failed")
|
|
173
|
+
else:
|
|
174
|
+
print(" ✅ Operator already approved")
|
|
175
|
+
|
|
176
|
+
# Step 5: Try upload with pynapse
|
|
177
|
+
print("\n[5/5] Testing pynapse upload...")
|
|
178
|
+
try:
|
|
179
|
+
from pynapse import AsyncSynapse
|
|
180
|
+
|
|
181
|
+
synapse = await AsyncSynapse.create(
|
|
182
|
+
rpc_url=RPC_URL,
|
|
183
|
+
chain='calibration',
|
|
184
|
+
private_key=PRIVATE_KEY
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
print(f" Connected as {synapse.account}")
|
|
188
|
+
|
|
189
|
+
# Check storage providers
|
|
190
|
+
info = await synapse.storage.get_storage_info()
|
|
191
|
+
providers = getattr(info, 'providers', [])
|
|
192
|
+
print(f" Found {len(providers)} storage providers")
|
|
193
|
+
|
|
194
|
+
if not providers:
|
|
195
|
+
print(" ❌ No providers available")
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
# Try to create context and upload
|
|
199
|
+
test_data = b"Hello from pynapse! " + bytes(range(256)) * 10
|
|
200
|
+
print(f" Uploading {len(test_data)} bytes...")
|
|
201
|
+
|
|
202
|
+
ctx = await synapse.storage.get_context()
|
|
203
|
+
piece_cid = await ctx.upload(test_data)
|
|
204
|
+
print(f" ✅ Uploaded! PieceCID: {piece_cid}")
|
|
205
|
+
|
|
206
|
+
# Download and verify
|
|
207
|
+
print(" Downloading...")
|
|
208
|
+
downloaded = await synapse.storage.download(piece_cid)
|
|
209
|
+
if downloaded == test_data:
|
|
210
|
+
print(" ✅ Download verified!")
|
|
211
|
+
else:
|
|
212
|
+
print(f" ⚠️ Data mismatch")
|
|
213
|
+
|
|
214
|
+
print("\n" + "=" * 60)
|
|
215
|
+
print("🎉 pynapse is working end-to-end!")
|
|
216
|
+
print("=" * 60)
|
|
217
|
+
|
|
218
|
+
except Exception as e:
|
|
219
|
+
print(f" ❌ Upload failed: {e}")
|
|
220
|
+
print("\n This may need additional debugging.")
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
if __name__ == "__main__":
|
|
224
|
+
asyncio.run(main())
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "synapse-filecoin-sdk"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.2.0"
|
|
4
4
|
description = "Python SDK for Filecoin Onchain Cloud (Synapse)"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.11"
|
|
@@ -24,14 +24,17 @@ dependencies = [
|
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
[project.urls]
|
|
27
|
-
Homepage = "https://github.com/
|
|
28
|
-
Repository = "https://github.com/
|
|
27
|
+
Homepage = "https://github.com/anjor/pynapse"
|
|
28
|
+
Repository = "https://github.com/anjor/pynapse"
|
|
29
29
|
|
|
30
30
|
[project.optional-dependencies]
|
|
31
31
|
test = [
|
|
32
32
|
"pytest>=8.0.0,<9",
|
|
33
33
|
"pytest-asyncio>=0.23.0,<0.24"
|
|
34
34
|
]
|
|
35
|
+
langchain = [
|
|
36
|
+
"langchain-core>=0.1.0,<0.4"
|
|
37
|
+
]
|
|
35
38
|
|
|
36
39
|
[build-system]
|
|
37
40
|
requires = ["hatchling"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
|
@@ -11,11 +11,6 @@ from typing import BinaryIO, Optional, Union
|
|
|
11
11
|
|
|
12
12
|
from .errors import create_error
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
DEFAULT_STREAM_COMMP_PATH = Path(
|
|
16
|
-
"/Users/anjor/repos/filecoin-project/go-fil-commp-hashhash/cmd/stream-commp/stream-commp"
|
|
17
|
-
)
|
|
18
|
-
|
|
19
14
|
# Multicodec constants
|
|
20
15
|
RAW_CODEC = 0x55 # raw codec for PieceCIDv2
|
|
21
16
|
FIL_COMMITMENT_UNSEALED = 0xf101 # fil-commitment-unsealed for PieceCIDv1
|
|
@@ -178,9 +173,6 @@ def _resolve_commp_helper() -> Path:
|
|
|
178
173
|
if override:
|
|
179
174
|
return Path(override)
|
|
180
175
|
|
|
181
|
-
if DEFAULT_STREAM_COMMP_PATH.exists():
|
|
182
|
-
return DEFAULT_STREAM_COMMP_PATH
|
|
183
|
-
|
|
184
176
|
found = shutil.which("stream-commp")
|
|
185
177
|
if found:
|
|
186
178
|
return Path(found)
|
|
@@ -188,7 +180,10 @@ def _resolve_commp_helper() -> Path:
|
|
|
188
180
|
raise create_error(
|
|
189
181
|
"piece",
|
|
190
182
|
"calculate",
|
|
191
|
-
|
|
183
|
+
(
|
|
184
|
+
"stream-commp helper not found. Install stream-commp and ensure it is on PATH, "
|
|
185
|
+
"or set PYNAPSE_COMMP_HELPER=/absolute/path/to/stream-commp."
|
|
186
|
+
),
|
|
192
187
|
)
|
|
193
188
|
|
|
194
189
|
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""
|
|
2
|
+
LangChain integration for pynapse.
|
|
3
|
+
|
|
4
|
+
This module provides LangChain-compatible components for storing and loading
|
|
5
|
+
documents on Filecoin via the Synapse SDK.
|
|
6
|
+
|
|
7
|
+
Installation:
|
|
8
|
+
pip install synapse-filecoin-sdk[langchain]
|
|
9
|
+
|
|
10
|
+
Usage:
|
|
11
|
+
from pynapse.integrations.langchain import FilecoinDocumentLoader, FilecoinStorageTool
|
|
12
|
+
|
|
13
|
+
# Load documents from Filecoin
|
|
14
|
+
loader = FilecoinDocumentLoader(
|
|
15
|
+
rpc_url="https://api.node.glif.io/rpc/v1",
|
|
16
|
+
chain="mainnet",
|
|
17
|
+
private_key=PRIVATE_KEY
|
|
18
|
+
)
|
|
19
|
+
docs = await loader.aload(piece_cid="baga6ea4seaq...")
|
|
20
|
+
|
|
21
|
+
# Store documents on Filecoin (as a LangChain tool for agents)
|
|
22
|
+
tool = FilecoinStorageTool(
|
|
23
|
+
rpc_url="https://api.node.glif.io/rpc/v1",
|
|
24
|
+
chain="mainnet",
|
|
25
|
+
private_key=PRIVATE_KEY
|
|
26
|
+
)
|
|
27
|
+
result = await tool._arun(content="Hello, Filecoin!")
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import asyncio
|
|
33
|
+
from typing import Any, Dict, List, Optional, Type
|
|
34
|
+
|
|
35
|
+
try:
|
|
36
|
+
from langchain_core.documents import Document
|
|
37
|
+
from langchain_core.document_loaders import BaseLoader
|
|
38
|
+
from langchain_core.tools import BaseTool
|
|
39
|
+
from pydantic import BaseModel, Field
|
|
40
|
+
except ImportError as e:
|
|
41
|
+
raise ImportError(
|
|
42
|
+
"LangChain dependencies not installed. "
|
|
43
|
+
"Install with: pip install synapse-filecoin-sdk[langchain]"
|
|
44
|
+
) from e
|
|
45
|
+
|
|
46
|
+
from pynapse import AsyncSynapse
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class FilecoinDocumentLoader(BaseLoader):
|
|
50
|
+
"""Load documents from Filecoin using pynapse.
|
|
51
|
+
|
|
52
|
+
This loader retrieves data stored on Filecoin by Piece CID and converts
|
|
53
|
+
it into LangChain Document objects.
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
loader = FilecoinDocumentLoader(
|
|
57
|
+
rpc_url="https://api.node.glif.io/rpc/v1",
|
|
58
|
+
chain="mainnet",
|
|
59
|
+
private_key="0x..."
|
|
60
|
+
)
|
|
61
|
+
docs = await loader.aload(piece_cid="baga6ea4seaq...")
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
def __init__(
|
|
65
|
+
self,
|
|
66
|
+
rpc_url: str,
|
|
67
|
+
chain: str = "mainnet",
|
|
68
|
+
private_key: Optional[str] = None,
|
|
69
|
+
):
|
|
70
|
+
"""Initialize the Filecoin document loader.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
rpc_url: RPC URL for Filecoin node
|
|
74
|
+
chain: Network name ("mainnet" or "calibration")
|
|
75
|
+
private_key: Wallet private key (optional for read-only operations)
|
|
76
|
+
"""
|
|
77
|
+
self.rpc_url = rpc_url
|
|
78
|
+
self.chain = chain
|
|
79
|
+
self.private_key = private_key
|
|
80
|
+
self._synapse: Optional[AsyncSynapse] = None
|
|
81
|
+
|
|
82
|
+
async def _get_synapse(self) -> AsyncSynapse:
|
|
83
|
+
"""Get or create AsyncSynapse instance."""
|
|
84
|
+
if self._synapse is None:
|
|
85
|
+
self._synapse = await AsyncSynapse.create(
|
|
86
|
+
rpc_url=self.rpc_url,
|
|
87
|
+
chain=self.chain,
|
|
88
|
+
private_key=self.private_key,
|
|
89
|
+
)
|
|
90
|
+
return self._synapse
|
|
91
|
+
|
|
92
|
+
def load(self) -> List[Document]:
|
|
93
|
+
"""Synchronous load - not supported, use aload instead."""
|
|
94
|
+
raise NotImplementedError(
|
|
95
|
+
"FilecoinDocumentLoader is async-only. Use aload() instead."
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
async def aload(self, piece_cid: str) -> List[Document]:
|
|
99
|
+
"""Load a document from Filecoin by Piece CID.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
piece_cid: The Piece CID of the stored data
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
List containing a single Document with the retrieved content
|
|
106
|
+
"""
|
|
107
|
+
synapse = await self._get_synapse()
|
|
108
|
+
ctx = await synapse.storage.get_context()
|
|
109
|
+
|
|
110
|
+
# Download the data
|
|
111
|
+
data = await ctx.download(piece_cid)
|
|
112
|
+
|
|
113
|
+
# Try to decode as text, fall back to repr for binary
|
|
114
|
+
try:
|
|
115
|
+
content = data.decode("utf-8")
|
|
116
|
+
except UnicodeDecodeError:
|
|
117
|
+
content = repr(data)
|
|
118
|
+
|
|
119
|
+
return [
|
|
120
|
+
Document(
|
|
121
|
+
page_content=content,
|
|
122
|
+
metadata={
|
|
123
|
+
"source": f"filecoin://{piece_cid}",
|
|
124
|
+
"piece_cid": piece_cid,
|
|
125
|
+
"chain": self.chain,
|
|
126
|
+
"size": len(data),
|
|
127
|
+
}
|
|
128
|
+
)
|
|
129
|
+
]
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class FilecoinStorageInput(BaseModel):
|
|
133
|
+
"""Input schema for FilecoinStorageTool."""
|
|
134
|
+
content: str = Field(description="The text content to store on Filecoin")
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class FilecoinStorageTool(BaseTool):
|
|
138
|
+
"""LangChain tool for storing data on Filecoin.
|
|
139
|
+
|
|
140
|
+
This tool allows LangChain agents to store arbitrary text content on
|
|
141
|
+
Filecoin and returns the Piece CID for later retrieval.
|
|
142
|
+
|
|
143
|
+
Example:
|
|
144
|
+
tool = FilecoinStorageTool(
|
|
145
|
+
rpc_url="https://api.node.glif.io/rpc/v1",
|
|
146
|
+
chain="mainnet",
|
|
147
|
+
private_key="0x..."
|
|
148
|
+
)
|
|
149
|
+
# Use in an agent or call directly
|
|
150
|
+
result = await tool._arun(content="Store this on Filecoin!")
|
|
151
|
+
"""
|
|
152
|
+
|
|
153
|
+
name: str = "filecoin_storage"
|
|
154
|
+
description: str = (
|
|
155
|
+
"Store text content on Filecoin decentralized storage. "
|
|
156
|
+
"Returns the Piece CID which can be used to retrieve the content later. "
|
|
157
|
+
"Use this when you need to permanently store data on decentralized storage."
|
|
158
|
+
)
|
|
159
|
+
args_schema: Type[BaseModel] = FilecoinStorageInput
|
|
160
|
+
|
|
161
|
+
rpc_url: str
|
|
162
|
+
chain: str = "mainnet"
|
|
163
|
+
private_key: str
|
|
164
|
+
_synapse: Optional[AsyncSynapse] = None
|
|
165
|
+
|
|
166
|
+
class Config:
|
|
167
|
+
arbitrary_types_allowed = True
|
|
168
|
+
|
|
169
|
+
async def _get_synapse(self) -> AsyncSynapse:
|
|
170
|
+
"""Get or create AsyncSynapse instance."""
|
|
171
|
+
if self._synapse is None:
|
|
172
|
+
self._synapse = await AsyncSynapse.create(
|
|
173
|
+
rpc_url=self.rpc_url,
|
|
174
|
+
chain=self.chain,
|
|
175
|
+
private_key=self.private_key,
|
|
176
|
+
)
|
|
177
|
+
return self._synapse
|
|
178
|
+
|
|
179
|
+
def _run(self, content: str) -> str:
|
|
180
|
+
"""Synchronous run - wraps async implementation."""
|
|
181
|
+
return asyncio.run(self._arun(content=content))
|
|
182
|
+
|
|
183
|
+
async def _arun(self, content: str) -> str:
|
|
184
|
+
"""Store content on Filecoin and return the Piece CID.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
content: Text content to store
|
|
188
|
+
|
|
189
|
+
Returns:
|
|
190
|
+
JSON string with piece_cid, size, and tx_hash
|
|
191
|
+
"""
|
|
192
|
+
synapse = await self._get_synapse()
|
|
193
|
+
ctx = await synapse.storage.get_context()
|
|
194
|
+
|
|
195
|
+
# Encode content and ensure minimum size (256 bytes)
|
|
196
|
+
data = content.encode("utf-8")
|
|
197
|
+
if len(data) < 256:
|
|
198
|
+
data = data + b'\x00' * (256 - len(data))
|
|
199
|
+
|
|
200
|
+
# Upload to Filecoin
|
|
201
|
+
result = await ctx.upload(data)
|
|
202
|
+
|
|
203
|
+
return (
|
|
204
|
+
f'{{"piece_cid": "{result.piece_cid}", '
|
|
205
|
+
f'"size": {result.size}, '
|
|
206
|
+
f'"tx_hash": "{result.tx_hash}"}}'
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
__all__ = [
|
|
211
|
+
"FilecoinDocumentLoader",
|
|
212
|
+
"FilecoinStorageTool",
|
|
213
|
+
"FilecoinStorageInput",
|
|
214
|
+
]
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import os
|
|
2
|
+
import shutil
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
import pytest
|
|
5
6
|
|
|
6
|
-
from pynapse.core.piece import
|
|
7
|
+
from pynapse.core.piece import calculate_piece_cid
|
|
7
8
|
|
|
8
9
|
|
|
9
10
|
@pytest.mark.skipif(
|
|
10
|
-
not (
|
|
11
|
+
not (shutil.which("stream-commp") or os.environ.get("PYNAPSE_COMMP_HELPER")),
|
|
11
12
|
reason="stream-commp helper not available",
|
|
12
13
|
)
|
|
13
14
|
def test_calculate_piece_cid_zero_block():
|