synapse-filecoin-sdk 0.1.0__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.
- pynapse/__init__.py +6 -0
- pynapse/_version.py +1 -0
- pynapse/contracts/__init__.py +34 -0
- pynapse/contracts/abi_registry.py +11 -0
- pynapse/contracts/addresses.json +30 -0
- pynapse/contracts/erc20_abi.json +92 -0
- pynapse/contracts/errorsAbi.json +933 -0
- pynapse/contracts/filecoinPayV1Abi.json +2424 -0
- pynapse/contracts/filecoinWarmStorageServiceAbi.json +2363 -0
- pynapse/contracts/filecoinWarmStorageServiceStateViewAbi.json +651 -0
- pynapse/contracts/generated.py +35 -0
- pynapse/contracts/payments_abi.json +205 -0
- pynapse/contracts/pdpVerifierAbi.json +1266 -0
- pynapse/contracts/providerIdSetAbi.json +161 -0
- pynapse/contracts/serviceProviderRegistryAbi.json +1479 -0
- pynapse/contracts/sessionKeyRegistryAbi.json +147 -0
- pynapse/core/__init__.py +68 -0
- pynapse/core/abis.py +25 -0
- pynapse/core/chains.py +97 -0
- pynapse/core/constants.py +27 -0
- pynapse/core/errors.py +22 -0
- pynapse/core/piece.py +263 -0
- pynapse/core/rand.py +14 -0
- pynapse/core/typed_data.py +320 -0
- pynapse/core/utils.py +30 -0
- pynapse/evm/__init__.py +3 -0
- pynapse/evm/client.py +26 -0
- pynapse/filbeam/__init__.py +3 -0
- pynapse/filbeam/service.py +39 -0
- pynapse/payments/__init__.py +17 -0
- pynapse/payments/service.py +826 -0
- pynapse/pdp/__init__.py +21 -0
- pynapse/pdp/server.py +331 -0
- pynapse/pdp/types.py +38 -0
- pynapse/pdp/verifier.py +82 -0
- pynapse/retriever/__init__.py +12 -0
- pynapse/retriever/async_chain.py +227 -0
- pynapse/retriever/chain.py +209 -0
- pynapse/session/__init__.py +12 -0
- pynapse/session/key.py +30 -0
- pynapse/session/permissions.py +57 -0
- pynapse/session/registry.py +90 -0
- pynapse/sp_registry/__init__.py +11 -0
- pynapse/sp_registry/capabilities.py +25 -0
- pynapse/sp_registry/pdp_capabilities.py +102 -0
- pynapse/sp_registry/service.py +446 -0
- pynapse/sp_registry/types.py +52 -0
- pynapse/storage/__init__.py +57 -0
- pynapse/storage/async_context.py +682 -0
- pynapse/storage/async_manager.py +757 -0
- pynapse/storage/context.py +680 -0
- pynapse/storage/manager.py +758 -0
- pynapse/synapse.py +191 -0
- pynapse/utils/__init__.py +25 -0
- pynapse/utils/constants.py +25 -0
- pynapse/utils/errors.py +3 -0
- pynapse/utils/metadata.py +35 -0
- pynapse/utils/piece_url.py +16 -0
- pynapse/warm_storage/__init__.py +13 -0
- pynapse/warm_storage/service.py +513 -0
- synapse_filecoin_sdk-0.1.0.dist-info/METADATA +74 -0
- synapse_filecoin_sdk-0.1.0.dist-info/RECORD +64 -0
- synapse_filecoin_sdk-0.1.0.dist-info/WHEEL +4 -0
- synapse_filecoin_sdk-0.1.0.dist-info/licenses/LICENSE.md +228 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Dict, List, Tuple
|
|
4
|
+
|
|
5
|
+
from multiformats import multibase
|
|
6
|
+
from web3 import Web3
|
|
7
|
+
|
|
8
|
+
from .capabilities import capabilities_list_to_object, decode_address_capability
|
|
9
|
+
from .types import PDPOffering, ProviderWithProduct
|
|
10
|
+
|
|
11
|
+
CAP_SERVICE_URL = "serviceURL"
|
|
12
|
+
CAP_MIN_PIECE_SIZE = "minPieceSizeInBytes"
|
|
13
|
+
CAP_MAX_PIECE_SIZE = "maxPieceSizeInBytes"
|
|
14
|
+
CAP_STORAGE_PRICE = "storagePricePerTibPerDay"
|
|
15
|
+
CAP_MIN_PROVING_PERIOD = "minProvingPeriodInEpochs"
|
|
16
|
+
CAP_LOCATION = "location"
|
|
17
|
+
CAP_PAYMENT_TOKEN = "paymentTokenAddress"
|
|
18
|
+
CAP_IPNI_PIECE = "ipniPiece"
|
|
19
|
+
CAP_IPNI_IPFS = "ipniIpfs"
|
|
20
|
+
CAP_IPNI_PEER_ID = "ipniPeerId"
|
|
21
|
+
CAP_IPNI_PEER_ID_LEGACY = "IPNIPeerID"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def decode_pdp_offering(provider: ProviderWithProduct) -> PDPOffering:
|
|
25
|
+
capabilities = capabilities_list_to_object(provider.product.capability_keys, provider.product_capability_values)
|
|
26
|
+
return decode_pdp_capabilities(capabilities)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _hex_to_int(hex_value: str) -> int:
|
|
30
|
+
return int(hex_value, 16)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _hex_to_str(hex_value: str) -> str:
|
|
34
|
+
return Web3.to_text(hexstr=hex_value)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def decode_pdp_capabilities(capabilities: Dict[str, str]) -> PDPOffering:
|
|
38
|
+
required = {
|
|
39
|
+
"service_url": _hex_to_str(capabilities[CAP_SERVICE_URL]),
|
|
40
|
+
"min_piece_size_in_bytes": _hex_to_int(capabilities[CAP_MIN_PIECE_SIZE]),
|
|
41
|
+
"max_piece_size_in_bytes": _hex_to_int(capabilities[CAP_MAX_PIECE_SIZE]),
|
|
42
|
+
"storage_price_per_tib_per_day": _hex_to_int(capabilities[CAP_STORAGE_PRICE]),
|
|
43
|
+
"min_proving_period_in_epochs": _hex_to_int(capabilities[CAP_MIN_PROVING_PERIOD]),
|
|
44
|
+
"location": _hex_to_str(capabilities[CAP_LOCATION]),
|
|
45
|
+
"payment_token_address": decode_address_capability(capabilities[CAP_PAYMENT_TOKEN]),
|
|
46
|
+
}
|
|
47
|
+
ipni_piece = capabilities.get(CAP_IPNI_PIECE) == "0x01"
|
|
48
|
+
ipni_ipfs = capabilities.get(CAP_IPNI_IPFS) == "0x01"
|
|
49
|
+
peer_hex = capabilities.get(CAP_IPNI_PEER_ID) or capabilities.get(CAP_IPNI_PEER_ID_LEGACY)
|
|
50
|
+
ipni_peer_id = None
|
|
51
|
+
if peer_hex:
|
|
52
|
+
try:
|
|
53
|
+
ipni_peer_id = multibase.encode("base58btc", Web3.to_bytes(hexstr=peer_hex)).decode()
|
|
54
|
+
except Exception:
|
|
55
|
+
ipni_peer_id = None
|
|
56
|
+
return PDPOffering(
|
|
57
|
+
ipni_piece=ipni_piece,
|
|
58
|
+
ipni_ipfs=ipni_ipfs,
|
|
59
|
+
ipni_peer_id=ipni_peer_id,
|
|
60
|
+
**required,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def encode_pdp_capabilities(pdp_offering: PDPOffering, capabilities: Dict[str, str] | None = None) -> Tuple[List[str], List[bytes]]:
|
|
65
|
+
keys: List[str] = []
|
|
66
|
+
values: List[bytes] = []
|
|
67
|
+
|
|
68
|
+
def add(key: str, value: bytes):
|
|
69
|
+
keys.append(key)
|
|
70
|
+
values.append(value)
|
|
71
|
+
|
|
72
|
+
add(CAP_SERVICE_URL, Web3.to_bytes(text=pdp_offering.service_url))
|
|
73
|
+
add(CAP_MIN_PIECE_SIZE, pdp_offering.min_piece_size_in_bytes.to_bytes(32, "big"))
|
|
74
|
+
add(CAP_MAX_PIECE_SIZE, pdp_offering.max_piece_size_in_bytes.to_bytes(32, "big"))
|
|
75
|
+
if pdp_offering.ipni_piece:
|
|
76
|
+
add(CAP_IPNI_PIECE, b"\x01")
|
|
77
|
+
if pdp_offering.ipni_ipfs:
|
|
78
|
+
add(CAP_IPNI_IPFS, b"\x01")
|
|
79
|
+
add(CAP_STORAGE_PRICE, pdp_offering.storage_price_per_tib_per_day.to_bytes(32, "big"))
|
|
80
|
+
add(CAP_MIN_PROVING_PERIOD, pdp_offering.min_proving_period_in_epochs.to_bytes(32, "big"))
|
|
81
|
+
add(CAP_LOCATION, Web3.to_bytes(text=pdp_offering.location))
|
|
82
|
+
add(CAP_PAYMENT_TOKEN, Web3.to_bytes(hexstr=pdp_offering.payment_token_address))
|
|
83
|
+
|
|
84
|
+
if pdp_offering.ipni_peer_id:
|
|
85
|
+
try:
|
|
86
|
+
peer_bytes = multibase.decode(pdp_offering.ipni_peer_id)
|
|
87
|
+
add(CAP_IPNI_PEER_ID, peer_bytes)
|
|
88
|
+
except Exception:
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
if capabilities:
|
|
92
|
+
for key, value in capabilities.items():
|
|
93
|
+
keys.append(key)
|
|
94
|
+
if value is None or value == "":
|
|
95
|
+
values.append(b"\x01")
|
|
96
|
+
else:
|
|
97
|
+
if value.startswith("0x"):
|
|
98
|
+
values.append(Web3.to_bytes(hexstr=value))
|
|
99
|
+
else:
|
|
100
|
+
values.append(Web3.to_bytes(text=value))
|
|
101
|
+
|
|
102
|
+
return keys, values
|
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import List, Optional, Tuple
|
|
5
|
+
|
|
6
|
+
from eth_account import Account
|
|
7
|
+
from web3 import AsyncWeb3, Web3
|
|
8
|
+
|
|
9
|
+
from pynapse.contracts import SERVICE_PROVIDER_REGISTRY_ABI
|
|
10
|
+
from pynapse.core.chains import Chain
|
|
11
|
+
from .pdp_capabilities import encode_pdp_capabilities
|
|
12
|
+
from .types import PDPOffering, ProviderInfo, ProviderRegistrationInfo, ProviderWithProduct, ServiceProduct
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class SyncSPRegistryService:
|
|
16
|
+
def __init__(self, web3: Web3, chain: Chain, private_key: Optional[str] = None) -> None:
|
|
17
|
+
self._web3 = web3
|
|
18
|
+
self._chain = chain
|
|
19
|
+
self._private_key = private_key
|
|
20
|
+
self._contract = web3.eth.contract(address=chain.contracts.sp_registry, abi=SERVICE_PROVIDER_REGISTRY_ABI)
|
|
21
|
+
|
|
22
|
+
def get_provider(self, provider_id: int) -> ProviderInfo:
|
|
23
|
+
info = self._contract.functions.getProvider(provider_id).call()
|
|
24
|
+
provider_id = int(info[0])
|
|
25
|
+
inner = info[1]
|
|
26
|
+
return ProviderInfo(
|
|
27
|
+
provider_id=provider_id,
|
|
28
|
+
service_provider=inner[0],
|
|
29
|
+
payee=inner[1],
|
|
30
|
+
name=inner[2],
|
|
31
|
+
description=inner[3],
|
|
32
|
+
is_active=inner[4],
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
def get_provider_by_address(self, address: str) -> Optional[ProviderInfo]:
|
|
36
|
+
info = self._contract.functions.getProviderByAddress(address).call()
|
|
37
|
+
provider_id = int(info[0])
|
|
38
|
+
if provider_id == 0:
|
|
39
|
+
return None
|
|
40
|
+
inner = info[1]
|
|
41
|
+
return ProviderInfo(
|
|
42
|
+
provider_id=provider_id,
|
|
43
|
+
service_provider=inner[0],
|
|
44
|
+
payee=inner[1],
|
|
45
|
+
name=inner[2],
|
|
46
|
+
description=inner[3],
|
|
47
|
+
is_active=inner[4],
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def get_provider_id_by_address(self, address: str) -> int:
|
|
51
|
+
return int(self._contract.functions.getProviderIdByAddress(address).call())
|
|
52
|
+
|
|
53
|
+
def get_provider_count(self) -> int:
|
|
54
|
+
return int(self._contract.functions.getProviderCount().call())
|
|
55
|
+
|
|
56
|
+
def is_provider_active(self, provider_id: int) -> bool:
|
|
57
|
+
return bool(self._contract.functions.isProviderActive(provider_id).call())
|
|
58
|
+
|
|
59
|
+
def is_registered_provider(self, address: str) -> bool:
|
|
60
|
+
return bool(self._contract.functions.isRegisteredProvider(address).call())
|
|
61
|
+
|
|
62
|
+
def get_provider_with_product(self, provider_id: int, product_type: int) -> ProviderWithProduct:
|
|
63
|
+
data = self._contract.functions.getProviderWithProduct(provider_id, product_type).call()
|
|
64
|
+
provider_id = int(data[0])
|
|
65
|
+
provider_info_tuple = data[1]
|
|
66
|
+
product_tuple = data[2]
|
|
67
|
+
values = data[3]
|
|
68
|
+
|
|
69
|
+
provider_info = ProviderInfo(
|
|
70
|
+
provider_id=provider_id,
|
|
71
|
+
service_provider=provider_info_tuple[0],
|
|
72
|
+
payee=provider_info_tuple[1],
|
|
73
|
+
name=provider_info_tuple[2],
|
|
74
|
+
description=provider_info_tuple[3],
|
|
75
|
+
is_active=provider_info_tuple[4],
|
|
76
|
+
)
|
|
77
|
+
product = ServiceProduct(
|
|
78
|
+
product_type=int(product_tuple[0]),
|
|
79
|
+
capability_keys=list(product_tuple[1]),
|
|
80
|
+
is_active=product_tuple[2],
|
|
81
|
+
)
|
|
82
|
+
return ProviderWithProduct(
|
|
83
|
+
provider_id=provider_id,
|
|
84
|
+
provider_info=provider_info,
|
|
85
|
+
product=product,
|
|
86
|
+
product_capability_values=list(values),
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
def get_providers_by_product_type(self, product_type: int, only_active: bool, offset: int, limit: int):
|
|
90
|
+
result = self._contract.functions.getProvidersByProductType(product_type, only_active, offset, limit).call()
|
|
91
|
+
providers = []
|
|
92
|
+
for item in result[0]:
|
|
93
|
+
providers.append(self._convert_provider_with_product(item))
|
|
94
|
+
return providers, bool(result[1])
|
|
95
|
+
|
|
96
|
+
def get_all_active_providers(self) -> List[ProviderInfo]:
|
|
97
|
+
providers: List[ProviderInfo] = []
|
|
98
|
+
limit = 50
|
|
99
|
+
offset = 0
|
|
100
|
+
has_more = True
|
|
101
|
+
while has_more:
|
|
102
|
+
result = self._contract.functions.getAllActiveProviders(offset, limit).call()
|
|
103
|
+
for info in result[0]:
|
|
104
|
+
providers.append(
|
|
105
|
+
ProviderInfo(
|
|
106
|
+
provider_id=int(info[0]),
|
|
107
|
+
service_provider=info[1][0],
|
|
108
|
+
payee=info[1][1],
|
|
109
|
+
name=info[1][2],
|
|
110
|
+
description=info[1][3],
|
|
111
|
+
is_active=info[1][4],
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
has_more = bool(result[1])
|
|
115
|
+
offset += limit
|
|
116
|
+
return providers
|
|
117
|
+
|
|
118
|
+
def register_provider(self, account: str, info: ProviderRegistrationInfo, product_type: int = 1) -> str:
|
|
119
|
+
if not self._private_key:
|
|
120
|
+
raise ValueError("private_key required for register_provider")
|
|
121
|
+
keys, values = encode_pdp_capabilities(info.pdp_offering, info.capabilities)
|
|
122
|
+
txn = self._contract.functions.registerProvider(
|
|
123
|
+
info.payee,
|
|
124
|
+
info.name,
|
|
125
|
+
info.description,
|
|
126
|
+
product_type,
|
|
127
|
+
keys,
|
|
128
|
+
values,
|
|
129
|
+
).build_transaction(
|
|
130
|
+
{
|
|
131
|
+
"from": account,
|
|
132
|
+
"nonce": self._web3.eth.get_transaction_count(account),
|
|
133
|
+
}
|
|
134
|
+
)
|
|
135
|
+
signed = self._web3.eth.account.sign_transaction(txn, private_key=self._private_key)
|
|
136
|
+
tx_hash = self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
137
|
+
return tx_hash.hex()
|
|
138
|
+
|
|
139
|
+
def update_provider_info(self, account: str, name: str, description: str) -> str:
|
|
140
|
+
if not self._private_key:
|
|
141
|
+
raise ValueError("private_key required for update_provider_info")
|
|
142
|
+
txn = self._contract.functions.updateProviderInfo(name, description).build_transaction(
|
|
143
|
+
{
|
|
144
|
+
"from": account,
|
|
145
|
+
"nonce": self._web3.eth.get_transaction_count(account),
|
|
146
|
+
}
|
|
147
|
+
)
|
|
148
|
+
signed = self._web3.eth.account.sign_transaction(txn, private_key=self._private_key)
|
|
149
|
+
tx_hash = self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
150
|
+
return tx_hash.hex()
|
|
151
|
+
|
|
152
|
+
def remove_provider(self, account: str) -> str:
|
|
153
|
+
if not self._private_key:
|
|
154
|
+
raise ValueError("private_key required for remove_provider")
|
|
155
|
+
txn = self._contract.functions.removeProvider().build_transaction(
|
|
156
|
+
{
|
|
157
|
+
"from": account,
|
|
158
|
+
"nonce": self._web3.eth.get_transaction_count(account),
|
|
159
|
+
}
|
|
160
|
+
)
|
|
161
|
+
signed = self._web3.eth.account.sign_transaction(txn, private_key=self._private_key)
|
|
162
|
+
tx_hash = self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
163
|
+
return tx_hash.hex()
|
|
164
|
+
|
|
165
|
+
def add_product(self, account: str, product_type: int, pdp_offering: PDPOffering, capabilities: Optional[dict] = None) -> str:
|
|
166
|
+
if not self._private_key:
|
|
167
|
+
raise ValueError("private_key required for add_product")
|
|
168
|
+
keys, values = encode_pdp_capabilities(pdp_offering, capabilities)
|
|
169
|
+
txn = self._contract.functions.addProduct(product_type, keys, values).build_transaction(
|
|
170
|
+
{
|
|
171
|
+
"from": account,
|
|
172
|
+
"nonce": self._web3.eth.get_transaction_count(account),
|
|
173
|
+
}
|
|
174
|
+
)
|
|
175
|
+
signed = self._web3.eth.account.sign_transaction(txn, private_key=self._private_key)
|
|
176
|
+
tx_hash = self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
177
|
+
return tx_hash.hex()
|
|
178
|
+
|
|
179
|
+
def update_product(self, account: str, product_type: int, pdp_offering: PDPOffering, capabilities: Optional[dict] = None) -> str:
|
|
180
|
+
if not self._private_key:
|
|
181
|
+
raise ValueError("private_key required for update_product")
|
|
182
|
+
keys, values = encode_pdp_capabilities(pdp_offering, capabilities)
|
|
183
|
+
txn = self._contract.functions.updateProduct(product_type, keys, values).build_transaction(
|
|
184
|
+
{
|
|
185
|
+
"from": account,
|
|
186
|
+
"nonce": self._web3.eth.get_transaction_count(account),
|
|
187
|
+
}
|
|
188
|
+
)
|
|
189
|
+
signed = self._web3.eth.account.sign_transaction(txn, private_key=self._private_key)
|
|
190
|
+
tx_hash = self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
191
|
+
return tx_hash.hex()
|
|
192
|
+
|
|
193
|
+
def remove_product(self, account: str, product_type: int) -> str:
|
|
194
|
+
if not self._private_key:
|
|
195
|
+
raise ValueError("private_key required for remove_product")
|
|
196
|
+
txn = self._contract.functions.removeProduct(product_type).build_transaction(
|
|
197
|
+
{
|
|
198
|
+
"from": account,
|
|
199
|
+
"nonce": self._web3.eth.get_transaction_count(account),
|
|
200
|
+
}
|
|
201
|
+
)
|
|
202
|
+
signed = self._web3.eth.account.sign_transaction(txn, private_key=self._private_key)
|
|
203
|
+
tx_hash = self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
204
|
+
return tx_hash.hex()
|
|
205
|
+
|
|
206
|
+
def _convert_provider_with_product(self, data) -> ProviderWithProduct:
|
|
207
|
+
provider_id = int(data[0])
|
|
208
|
+
provider_info_tuple = data[1]
|
|
209
|
+
product_tuple = data[2]
|
|
210
|
+
values = data[3]
|
|
211
|
+
provider_info = ProviderInfo(
|
|
212
|
+
provider_id=provider_id,
|
|
213
|
+
service_provider=provider_info_tuple[0],
|
|
214
|
+
payee=provider_info_tuple[1],
|
|
215
|
+
name=provider_info_tuple[2],
|
|
216
|
+
description=provider_info_tuple[3],
|
|
217
|
+
is_active=provider_info_tuple[4],
|
|
218
|
+
)
|
|
219
|
+
product = ServiceProduct(
|
|
220
|
+
product_type=int(product_tuple[0]),
|
|
221
|
+
capability_keys=list(product_tuple[1]),
|
|
222
|
+
is_active=product_tuple[2],
|
|
223
|
+
)
|
|
224
|
+
return ProviderWithProduct(
|
|
225
|
+
provider_id=provider_id,
|
|
226
|
+
provider_info=provider_info,
|
|
227
|
+
product=product,
|
|
228
|
+
product_capability_values=list(values),
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
class AsyncSPRegistryService:
|
|
233
|
+
def __init__(self, web3: AsyncWeb3, chain: Chain, private_key: Optional[str] = None) -> None:
|
|
234
|
+
self._web3 = web3
|
|
235
|
+
self._chain = chain
|
|
236
|
+
self._private_key = private_key
|
|
237
|
+
self._contract = web3.eth.contract(address=chain.contracts.sp_registry, abi=SERVICE_PROVIDER_REGISTRY_ABI)
|
|
238
|
+
|
|
239
|
+
async def get_provider(self, provider_id: int) -> ProviderInfo:
|
|
240
|
+
info = await self._contract.functions.getProvider(provider_id).call()
|
|
241
|
+
provider_id = int(info[0])
|
|
242
|
+
inner = info[1]
|
|
243
|
+
return ProviderInfo(
|
|
244
|
+
provider_id=provider_id,
|
|
245
|
+
service_provider=inner[0],
|
|
246
|
+
payee=inner[1],
|
|
247
|
+
name=inner[2],
|
|
248
|
+
description=inner[3],
|
|
249
|
+
is_active=inner[4],
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
async def get_provider_by_address(self, address: str) -> Optional[ProviderInfo]:
|
|
253
|
+
info = await self._contract.functions.getProviderByAddress(address).call()
|
|
254
|
+
provider_id = int(info[0])
|
|
255
|
+
if provider_id == 0:
|
|
256
|
+
return None
|
|
257
|
+
inner = info[1]
|
|
258
|
+
return ProviderInfo(
|
|
259
|
+
provider_id=provider_id,
|
|
260
|
+
service_provider=inner[0],
|
|
261
|
+
payee=inner[1],
|
|
262
|
+
name=inner[2],
|
|
263
|
+
description=inner[3],
|
|
264
|
+
is_active=inner[4],
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
async def get_provider_id_by_address(self, address: str) -> int:
|
|
268
|
+
return int(await self._contract.functions.getProviderIdByAddress(address).call())
|
|
269
|
+
|
|
270
|
+
async def get_provider_count(self) -> int:
|
|
271
|
+
return int(await self._contract.functions.getProviderCount().call())
|
|
272
|
+
|
|
273
|
+
async def is_provider_active(self, provider_id: int) -> bool:
|
|
274
|
+
return bool(await self._contract.functions.isProviderActive(provider_id).call())
|
|
275
|
+
|
|
276
|
+
async def is_registered_provider(self, address: str) -> bool:
|
|
277
|
+
return bool(await self._contract.functions.isRegisteredProvider(address).call())
|
|
278
|
+
|
|
279
|
+
async def get_provider_with_product(self, provider_id: int, product_type: int) -> ProviderWithProduct:
|
|
280
|
+
data = await self._contract.functions.getProviderWithProduct(provider_id, product_type).call()
|
|
281
|
+
provider_id = int(data[0])
|
|
282
|
+
provider_info_tuple = data[1]
|
|
283
|
+
product_tuple = data[2]
|
|
284
|
+
values = data[3]
|
|
285
|
+
|
|
286
|
+
provider_info = ProviderInfo(
|
|
287
|
+
provider_id=provider_id,
|
|
288
|
+
service_provider=provider_info_tuple[0],
|
|
289
|
+
payee=provider_info_tuple[1],
|
|
290
|
+
name=provider_info_tuple[2],
|
|
291
|
+
description=provider_info_tuple[3],
|
|
292
|
+
is_active=provider_info_tuple[4],
|
|
293
|
+
)
|
|
294
|
+
product = ServiceProduct(
|
|
295
|
+
product_type=int(product_tuple[0]),
|
|
296
|
+
capability_keys=list(product_tuple[1]),
|
|
297
|
+
is_active=product_tuple[2],
|
|
298
|
+
)
|
|
299
|
+
return ProviderWithProduct(
|
|
300
|
+
provider_id=provider_id,
|
|
301
|
+
provider_info=provider_info,
|
|
302
|
+
product=product,
|
|
303
|
+
product_capability_values=list(values),
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
async def get_providers_by_product_type(self, product_type: int, only_active: bool, offset: int, limit: int):
|
|
307
|
+
result = await self._contract.functions.getProvidersByProductType(product_type, only_active, offset, limit).call()
|
|
308
|
+
providers = []
|
|
309
|
+
for item in result[0]:
|
|
310
|
+
providers.append(self._convert_provider_with_product(item))
|
|
311
|
+
return providers, bool(result[1])
|
|
312
|
+
|
|
313
|
+
async def get_all_active_providers(self) -> List[ProviderInfo]:
|
|
314
|
+
providers: List[ProviderInfo] = []
|
|
315
|
+
limit = 50
|
|
316
|
+
offset = 0
|
|
317
|
+
has_more = True
|
|
318
|
+
while has_more:
|
|
319
|
+
result = await self._contract.functions.getAllActiveProviders(offset, limit).call()
|
|
320
|
+
for info in result[0]:
|
|
321
|
+
providers.append(
|
|
322
|
+
ProviderInfo(
|
|
323
|
+
provider_id=int(info[0]),
|
|
324
|
+
service_provider=info[1][0],
|
|
325
|
+
payee=info[1][1],
|
|
326
|
+
name=info[1][2],
|
|
327
|
+
description=info[1][3],
|
|
328
|
+
is_active=info[1][4],
|
|
329
|
+
)
|
|
330
|
+
)
|
|
331
|
+
has_more = bool(result[1])
|
|
332
|
+
offset += limit
|
|
333
|
+
return providers
|
|
334
|
+
|
|
335
|
+
async def register_provider(self, account: str, info: ProviderRegistrationInfo, product_type: int = 1) -> str:
|
|
336
|
+
if not self._private_key:
|
|
337
|
+
raise ValueError("private_key required for register_provider")
|
|
338
|
+
keys, values = encode_pdp_capabilities(info.pdp_offering, info.capabilities)
|
|
339
|
+
txn = await self._contract.functions.registerProvider(
|
|
340
|
+
info.payee,
|
|
341
|
+
info.name,
|
|
342
|
+
info.description,
|
|
343
|
+
product_type,
|
|
344
|
+
keys,
|
|
345
|
+
values,
|
|
346
|
+
).build_transaction(
|
|
347
|
+
{
|
|
348
|
+
"from": account,
|
|
349
|
+
"nonce": await self._web3.eth.get_transaction_count(account),
|
|
350
|
+
}
|
|
351
|
+
)
|
|
352
|
+
signed = Account.sign_transaction(txn, private_key=self._private_key)
|
|
353
|
+
tx_hash = await self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
354
|
+
return tx_hash.hex()
|
|
355
|
+
|
|
356
|
+
async def update_provider_info(self, account: str, name: str, description: str) -> str:
|
|
357
|
+
if not self._private_key:
|
|
358
|
+
raise ValueError("private_key required for update_provider_info")
|
|
359
|
+
txn = await self._contract.functions.updateProviderInfo(name, description).build_transaction(
|
|
360
|
+
{
|
|
361
|
+
"from": account,
|
|
362
|
+
"nonce": await self._web3.eth.get_transaction_count(account),
|
|
363
|
+
}
|
|
364
|
+
)
|
|
365
|
+
signed = Account.sign_transaction(txn, private_key=self._private_key)
|
|
366
|
+
tx_hash = await self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
367
|
+
return tx_hash.hex()
|
|
368
|
+
|
|
369
|
+
async def remove_provider(self, account: str) -> str:
|
|
370
|
+
if not self._private_key:
|
|
371
|
+
raise ValueError("private_key required for remove_provider")
|
|
372
|
+
txn = await self._contract.functions.removeProvider().build_transaction(
|
|
373
|
+
{
|
|
374
|
+
"from": account,
|
|
375
|
+
"nonce": await self._web3.eth.get_transaction_count(account),
|
|
376
|
+
}
|
|
377
|
+
)
|
|
378
|
+
signed = Account.sign_transaction(txn, private_key=self._private_key)
|
|
379
|
+
tx_hash = await self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
380
|
+
return tx_hash.hex()
|
|
381
|
+
|
|
382
|
+
async def add_product(self, account: str, product_type: int, pdp_offering: PDPOffering, capabilities: Optional[dict] = None) -> str:
|
|
383
|
+
if not self._private_key:
|
|
384
|
+
raise ValueError("private_key required for add_product")
|
|
385
|
+
keys, values = encode_pdp_capabilities(pdp_offering, capabilities)
|
|
386
|
+
txn = await self._contract.functions.addProduct(product_type, keys, values).build_transaction(
|
|
387
|
+
{
|
|
388
|
+
"from": account,
|
|
389
|
+
"nonce": await self._web3.eth.get_transaction_count(account),
|
|
390
|
+
}
|
|
391
|
+
)
|
|
392
|
+
signed = Account.sign_transaction(txn, private_key=self._private_key)
|
|
393
|
+
tx_hash = await self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
394
|
+
return tx_hash.hex()
|
|
395
|
+
|
|
396
|
+
async def update_product(self, account: str, product_type: int, pdp_offering: PDPOffering, capabilities: Optional[dict] = None) -> str:
|
|
397
|
+
if not self._private_key:
|
|
398
|
+
raise ValueError("private_key required for update_product")
|
|
399
|
+
keys, values = encode_pdp_capabilities(pdp_offering, capabilities)
|
|
400
|
+
txn = await self._contract.functions.updateProduct(product_type, keys, values).build_transaction(
|
|
401
|
+
{
|
|
402
|
+
"from": account,
|
|
403
|
+
"nonce": await self._web3.eth.get_transaction_count(account),
|
|
404
|
+
}
|
|
405
|
+
)
|
|
406
|
+
signed = Account.sign_transaction(txn, private_key=self._private_key)
|
|
407
|
+
tx_hash = await self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
408
|
+
return tx_hash.hex()
|
|
409
|
+
|
|
410
|
+
async def remove_product(self, account: str, product_type: int) -> str:
|
|
411
|
+
if not self._private_key:
|
|
412
|
+
raise ValueError("private_key required for remove_product")
|
|
413
|
+
txn = await self._contract.functions.removeProduct(product_type).build_transaction(
|
|
414
|
+
{
|
|
415
|
+
"from": account,
|
|
416
|
+
"nonce": await self._web3.eth.get_transaction_count(account),
|
|
417
|
+
}
|
|
418
|
+
)
|
|
419
|
+
signed = Account.sign_transaction(txn, private_key=self._private_key)
|
|
420
|
+
tx_hash = await self._web3.eth.send_raw_transaction(signed.rawTransaction)
|
|
421
|
+
return tx_hash.hex()
|
|
422
|
+
|
|
423
|
+
def _convert_provider_with_product(self, data) -> ProviderWithProduct:
|
|
424
|
+
provider_id = int(data[0])
|
|
425
|
+
provider_info_tuple = data[1]
|
|
426
|
+
product_tuple = data[2]
|
|
427
|
+
values = data[3]
|
|
428
|
+
provider_info = ProviderInfo(
|
|
429
|
+
provider_id=provider_id,
|
|
430
|
+
service_provider=provider_info_tuple[0],
|
|
431
|
+
payee=provider_info_tuple[1],
|
|
432
|
+
name=provider_info_tuple[2],
|
|
433
|
+
description=provider_info_tuple[3],
|
|
434
|
+
is_active=provider_info_tuple[4],
|
|
435
|
+
)
|
|
436
|
+
product = ServiceProduct(
|
|
437
|
+
product_type=int(product_tuple[0]),
|
|
438
|
+
capability_keys=list(product_tuple[1]),
|
|
439
|
+
is_active=product_tuple[2],
|
|
440
|
+
)
|
|
441
|
+
return ProviderWithProduct(
|
|
442
|
+
provider_id=provider_id,
|
|
443
|
+
provider_info=provider_info,
|
|
444
|
+
product=product,
|
|
445
|
+
product_capability_values=list(values),
|
|
446
|
+
)
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Dict, List, Optional
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@dataclass
|
|
8
|
+
class ProviderInfo:
|
|
9
|
+
provider_id: int
|
|
10
|
+
service_provider: str
|
|
11
|
+
payee: str
|
|
12
|
+
name: str
|
|
13
|
+
description: str
|
|
14
|
+
is_active: bool
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass
|
|
18
|
+
class ServiceProduct:
|
|
19
|
+
product_type: int
|
|
20
|
+
capability_keys: List[str]
|
|
21
|
+
is_active: bool
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass
|
|
25
|
+
class ProviderWithProduct:
|
|
26
|
+
provider_id: int
|
|
27
|
+
provider_info: ProviderInfo
|
|
28
|
+
product: ServiceProduct
|
|
29
|
+
product_capability_values: List[bytes]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass
|
|
33
|
+
class PDPOffering:
|
|
34
|
+
service_url: str
|
|
35
|
+
min_piece_size_in_bytes: int
|
|
36
|
+
max_piece_size_in_bytes: int
|
|
37
|
+
storage_price_per_tib_per_day: int
|
|
38
|
+
min_proving_period_in_epochs: int
|
|
39
|
+
location: str
|
|
40
|
+
payment_token_address: str
|
|
41
|
+
ipni_piece: bool = False
|
|
42
|
+
ipni_ipfs: bool = False
|
|
43
|
+
ipni_peer_id: Optional[str] = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass
|
|
47
|
+
class ProviderRegistrationInfo:
|
|
48
|
+
payee: str
|
|
49
|
+
name: str
|
|
50
|
+
description: str
|
|
51
|
+
pdp_offering: PDPOffering
|
|
52
|
+
capabilities: Optional[Dict[str, str]] = None
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from .context import (
|
|
2
|
+
ProviderSelectionResult,
|
|
3
|
+
StorageContext,
|
|
4
|
+
StorageContextOptions,
|
|
5
|
+
UploadResult,
|
|
6
|
+
)
|
|
7
|
+
from .manager import (
|
|
8
|
+
DataSetMatch,
|
|
9
|
+
PreflightInfo,
|
|
10
|
+
ProviderFilter,
|
|
11
|
+
ServiceParameters,
|
|
12
|
+
StorageInfo,
|
|
13
|
+
StorageManager,
|
|
14
|
+
StoragePricing,
|
|
15
|
+
)
|
|
16
|
+
from .async_context import (
|
|
17
|
+
AsyncProviderSelectionResult,
|
|
18
|
+
AsyncStorageContext,
|
|
19
|
+
AsyncStorageContextOptions,
|
|
20
|
+
AsyncUploadResult,
|
|
21
|
+
)
|
|
22
|
+
from .async_manager import (
|
|
23
|
+
AsyncDataSetMatch,
|
|
24
|
+
AsyncPreflightInfo,
|
|
25
|
+
AsyncProviderFilter,
|
|
26
|
+
AsyncServiceParameters,
|
|
27
|
+
AsyncStorageInfo,
|
|
28
|
+
AsyncStorageManager,
|
|
29
|
+
AsyncStoragePricing,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
__all__ = [
|
|
33
|
+
# Sync classes
|
|
34
|
+
"DataSetMatch",
|
|
35
|
+
"PreflightInfo",
|
|
36
|
+
"ProviderFilter",
|
|
37
|
+
"ProviderSelectionResult",
|
|
38
|
+
"ServiceParameters",
|
|
39
|
+
"StorageContext",
|
|
40
|
+
"StorageContextOptions",
|
|
41
|
+
"StorageInfo",
|
|
42
|
+
"StorageManager",
|
|
43
|
+
"StoragePricing",
|
|
44
|
+
"UploadResult",
|
|
45
|
+
# Async classes
|
|
46
|
+
"AsyncDataSetMatch",
|
|
47
|
+
"AsyncPreflightInfo",
|
|
48
|
+
"AsyncProviderFilter",
|
|
49
|
+
"AsyncProviderSelectionResult",
|
|
50
|
+
"AsyncServiceParameters",
|
|
51
|
+
"AsyncStorageContext",
|
|
52
|
+
"AsyncStorageContextOptions",
|
|
53
|
+
"AsyncStorageInfo",
|
|
54
|
+
"AsyncStorageManager",
|
|
55
|
+
"AsyncStoragePricing",
|
|
56
|
+
"AsyncUploadResult",
|
|
57
|
+
]
|