Skip to content

Commit dc60e03

Browse files
committed
Completely removed py-substrate-interface.
1 parent 19ac760 commit dc60e03

File tree

6 files changed

+365
-2
lines changed

6 files changed

+365
-2
lines changed

bittensor/core/errors.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ class SubstrateRequestException(Exception):
2727
pass
2828

2929

30+
class StorageFunctionNotFound(ValueError):
31+
pass
32+
33+
3034
class BlockNotFound(Exception):
3135
pass
3236

bittensor/utils/substrate_interface.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
from scalecodec.base import ScaleBytes, ScaleType, RuntimeConfigurationObject
3232
from scalecodec.type_registry import load_type_registry_preset
3333
from scalecodec.types import GenericCall, GenericRuntimeCallDefinition
34-
from substrateinterface.storage import StorageKey
3534
from websockets.asyncio.client import connect
3635
from websockets.exceptions import ConnectionClosed
3736

@@ -43,6 +42,7 @@
4342
from bittensor.utils import execute_coroutine
4443
from bittensor.utils import hex_to_bytes
4544
from bittensor.utils.btlogging import logging
45+
from bittensor.utils.substrate_utils.storage import StorageKey
4646

4747
if TYPE_CHECKING:
4848
from websockets.asyncio.client import ClientConnection

bittensor/utils/substrate_utils/__init__.py

Whitespace-only changes.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
"""Helper functions used to calculate keys for Substrate storage items"""
2+
3+
from hashlib import blake2b
4+
import xxhash
5+
6+
7+
def blake2_256(data):
8+
"""
9+
Helper function to calculate a 32 bytes Blake2b hash for provided data, used as key for Substrate storage items
10+
"""
11+
return blake2b(data, digest_size=32).digest()
12+
13+
14+
def blake2_128(data):
15+
"""
16+
Helper function to calculate a 16 bytes Blake2b hash for provided data, used as key for Substrate storage items
17+
"""
18+
return blake2b(data, digest_size=16).digest()
19+
20+
21+
def blake2_128_concat(data):
22+
"""
23+
Helper function to calculate a 16 bytes Blake2b hash for provided data, concatenated with data, used as key
24+
for Substrate storage items
25+
"""
26+
return blake2b(data, digest_size=16).digest() + data
27+
28+
29+
def xxh128(data):
30+
"""
31+
Helper function to calculate a 2 concatenated xxh64 hash for provided data, used as key for several Substrate
32+
"""
33+
storage_key1 = bytearray(xxhash.xxh64(data, seed=0).digest())
34+
storage_key1.reverse()
35+
36+
storage_key2 = bytearray(xxhash.xxh64(data, seed=1).digest())
37+
storage_key2.reverse()
38+
39+
return storage_key1 + storage_key2
40+
41+
42+
def two_x64_concat(data):
43+
"""
44+
Helper function to calculate a xxh64 hash with concatenated data for provided data,
45+
used as key for several Substrate
46+
"""
47+
storage_key = bytearray(xxhash.xxh64(data, seed=0).digest())
48+
storage_key.reverse()
49+
50+
return storage_key + data
51+
52+
53+
def xxh64(data):
54+
storage_key = bytearray(xxhash.xxh64(data, seed=0).digest())
55+
storage_key.reverse()
56+
57+
return storage_key
58+
59+
60+
def identity(data):
61+
return data
Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
import binascii
2+
from typing import Any, Optional
3+
4+
from bittensor.core.errors import StorageFunctionNotFound
5+
6+
from scalecodec import ScaleBytes, GenericMetadataVersioned, ss58_decode
7+
from scalecodec.base import ScaleDecoder, RuntimeConfigurationObject, ScaleType
8+
from bittensor.utils.substrate_utils.hasher import (
9+
blake2_256,
10+
two_x64_concat,
11+
xxh128,
12+
blake2_128,
13+
blake2_128_concat,
14+
identity,
15+
)
16+
17+
18+
class StorageKey:
19+
"""
20+
A StorageKey instance is a representation of a single state entry.
21+
22+
Substrate uses a simple key-value data store implemented as a database-backed, modified Merkle tree.
23+
All of Substrate's higher-level storage abstractions are built on top of this simple key-value store.
24+
"""
25+
26+
def __init__(
27+
self,
28+
pallet: str,
29+
storage_function: str,
30+
params: list,
31+
data: bytes,
32+
value_scale_type: str,
33+
metadata: GenericMetadataVersioned,
34+
runtime_config: RuntimeConfigurationObject,
35+
):
36+
self.pallet = pallet
37+
self.storage_function = storage_function
38+
self.params = params
39+
self.params_encoded = []
40+
self.data = data
41+
self.metadata = metadata
42+
self.runtime_config = runtime_config
43+
self.value_scale_type = value_scale_type
44+
self.metadata_storage_function = None
45+
46+
@classmethod
47+
def create_from_data(
48+
cls,
49+
data: bytes,
50+
runtime_config: RuntimeConfigurationObject,
51+
metadata: GenericMetadataVersioned,
52+
value_scale_type: str = None,
53+
pallet: str = None,
54+
storage_function: str = None,
55+
) -> "StorageKey":
56+
"""
57+
Create a StorageKey instance providing raw storage key bytes
58+
59+
Parameters
60+
----------
61+
data: bytes representation of the storage key
62+
runtime_config: RuntimeConfigurationObject
63+
metadata: GenericMetadataVersioned
64+
value_scale_type: type string of to decode result data
65+
pallet: name of pallet
66+
storage_function: name of storage function
67+
68+
Returns
69+
-------
70+
StorageKey
71+
"""
72+
if not value_scale_type and pallet and storage_function:
73+
metadata_pallet = metadata.get_metadata_pallet(pallet)
74+
75+
if not metadata_pallet:
76+
raise StorageFunctionNotFound(f'Pallet "{pallet}" not found')
77+
78+
storage_item = metadata_pallet.get_storage_function(storage_function)
79+
80+
if not storage_item:
81+
raise StorageFunctionNotFound(
82+
f'Storage function "{pallet}.{storage_function}" not found'
83+
)
84+
85+
# Process specific type of storage function
86+
value_scale_type = storage_item.get_value_type_string()
87+
88+
return cls(
89+
pallet=None,
90+
storage_function=None,
91+
params=None,
92+
data=data,
93+
metadata=metadata,
94+
value_scale_type=value_scale_type,
95+
runtime_config=runtime_config,
96+
)
97+
98+
@classmethod
99+
def create_from_storage_function(
100+
cls,
101+
pallet: str,
102+
storage_function: str,
103+
params: list,
104+
runtime_config: RuntimeConfigurationObject,
105+
metadata: GenericMetadataVersioned,
106+
) -> "StorageKey":
107+
"""
108+
Create a StorageKey instance providing storage function details
109+
110+
Parameters
111+
----------
112+
pallet: name of pallet
113+
storage_function: name of storage function
114+
params: Optional list of parameters in case of a Mapped storage function
115+
runtime_config: RuntimeConfigurationObject
116+
metadata: GenericMetadataVersioned
117+
118+
Returns
119+
-------
120+
StorageKey
121+
"""
122+
storage_key_obj = cls(
123+
pallet=pallet,
124+
storage_function=storage_function,
125+
params=params,
126+
data=None,
127+
runtime_config=runtime_config,
128+
metadata=metadata,
129+
value_scale_type=None,
130+
)
131+
132+
storage_key_obj.generate()
133+
134+
return storage_key_obj
135+
136+
def convert_storage_parameter(self, scale_type: str, value: Any):
137+
if type(value) is bytes:
138+
value = f"0x{value.hex()}"
139+
140+
if scale_type == "AccountId":
141+
if value[0:2] != "0x":
142+
return "0x{}".format(
143+
ss58_decode(value, self.runtime_config.ss58_format)
144+
)
145+
146+
return value
147+
148+
def to_hex(self) -> str:
149+
"""
150+
Returns a Hex-string representation of current StorageKey data
151+
152+
Returns
153+
-------
154+
str
155+
Hex string
156+
"""
157+
if self.data:
158+
return f"0x{self.data.hex()}"
159+
160+
def generate(self) -> bytes:
161+
"""
162+
Generate a storage key for current specified pallet/function/params
163+
164+
Returns
165+
-------
166+
bytes
167+
"""
168+
169+
# Search storage call in metadata
170+
metadata_pallet = self.metadata.get_metadata_pallet(self.pallet)
171+
172+
if not metadata_pallet:
173+
raise StorageFunctionNotFound(f'Pallet "{self.pallet}" not found')
174+
175+
self.metadata_storage_function = metadata_pallet.get_storage_function(
176+
self.storage_function
177+
)
178+
179+
if not self.metadata_storage_function:
180+
raise StorageFunctionNotFound(
181+
f'Storage function "{self.pallet}.{self.storage_function}" not found'
182+
)
183+
184+
# Process specific type of storage function
185+
self.value_scale_type = self.metadata_storage_function.get_value_type_string()
186+
param_types = self.metadata_storage_function.get_params_type_string()
187+
188+
hashers = self.metadata_storage_function.get_param_hashers()
189+
190+
storage_hash = xxh128(
191+
metadata_pallet.value["storage"]["prefix"].encode()
192+
) + xxh128(self.storage_function.encode())
193+
194+
# Encode parameters
195+
self.params_encoded = []
196+
if self.params:
197+
for idx, param in enumerate(self.params):
198+
if type(param) is ScaleBytes:
199+
# Already encoded
200+
self.params_encoded.append(param)
201+
else:
202+
param = self.convert_storage_parameter(param_types[idx], param)
203+
param_obj = self.runtime_config.create_scale_object(
204+
type_string=param_types[idx]
205+
)
206+
self.params_encoded.append(param_obj.encode(param))
207+
208+
for idx, param in enumerate(self.params_encoded):
209+
# Get hasher assiociated with param
210+
try:
211+
param_hasher = hashers[idx]
212+
except IndexError:
213+
raise ValueError(f"No hasher found for param #{idx + 1}")
214+
215+
params_key = bytes()
216+
217+
# Convert param to bytes
218+
if type(param) is str:
219+
params_key += binascii.unhexlify(param)
220+
elif type(param) is ScaleBytes:
221+
params_key += param.data
222+
elif isinstance(param, ScaleDecoder):
223+
params_key += param.data.data
224+
225+
if not param_hasher:
226+
param_hasher = "Twox128"
227+
228+
if param_hasher == "Blake2_256":
229+
storage_hash += blake2_256(params_key)
230+
231+
elif param_hasher == "Blake2_128":
232+
storage_hash += blake2_128(params_key)
233+
234+
elif param_hasher == "Blake2_128Concat":
235+
storage_hash += blake2_128_concat(params_key)
236+
237+
elif param_hasher == "Twox128":
238+
storage_hash += xxh128(params_key)
239+
240+
elif param_hasher == "Twox64Concat":
241+
storage_hash += two_x64_concat(params_key)
242+
243+
elif param_hasher == "Identity":
244+
storage_hash += identity(params_key)
245+
246+
else:
247+
raise ValueError('Unknown storage hasher "{}"'.format(param_hasher))
248+
249+
self.data = storage_hash
250+
251+
return self.data
252+
253+
def decode_scale_value(self, data: Optional[ScaleBytes] = None) -> ScaleType:
254+
"""
255+
256+
Parameters
257+
----------
258+
data
259+
260+
Returns
261+
-------
262+
263+
"""
264+
265+
result_found = False
266+
267+
if data is not None:
268+
change_scale_type = self.value_scale_type
269+
result_found = True
270+
elif self.metadata_storage_function.value["modifier"] == "Default":
271+
# Fallback to default value of storage function if no result
272+
change_scale_type = self.value_scale_type
273+
data = ScaleBytes(
274+
self.metadata_storage_function.value_object["default"].value_object
275+
)
276+
else:
277+
# No result is interpreted as an Option<...> result
278+
change_scale_type = f"Option<{self.value_scale_type}>"
279+
data = ScaleBytes(
280+
self.metadata_storage_function.value_object["default"].value_object
281+
)
282+
283+
# Decode SCALE result data
284+
updated_obj = self.runtime_config.create_scale_object(
285+
type_string=change_scale_type, data=data, metadata=self.metadata
286+
)
287+
updated_obj.decode()
288+
updated_obj.meta_info = {"result_found": result_found}
289+
290+
return updated_obj
291+
292+
def __repr__(self):
293+
if self.pallet and self.storage_function:
294+
return f"<StorageKey(pallet={self.pallet}, storage_function={self.storage_function}, params={self.params})>"
295+
elif self.data:
296+
return f"<StorageKey(data=0x{self.data.hex()})>"
297+
else:
298+
return repr(self)

requirements/prod.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ rich
2222
pydantic>=2.3, <3
2323
python-Levenshtein
2424
scalecodec==1.2.11
25-
substrate-interface~=1.7.9 # still needed for StorageKey in substrate_interface.py
2625
uvicorn
2726
websockets>=14.1
27+
xxhash
2828
bittensor-wallet>=2.1.3
2929
bittensor-commit-reveal>=0.1.0

0 commit comments

Comments
 (0)