Skip to content

Commit 9cfb21c

Browse files
committed
Merge branch 'development'
2 parents 308d42e + fcf4f61 commit 9cfb21c

29 files changed

+836
-490
lines changed

docs/migration_guide.rst

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,53 @@
11
Migration guide
22
===============
33

4+
**********************
5+
0.18.3 Migration guide
6+
**********************
7+
8+
Version 0.18.3 of **starknet.py** comes with support for RPC 0.5.0!
9+
10+
11+
0.18.3 Targeted versions
12+
------------------------
13+
14+
- Starknet - `0.12.2 <https://community.starknet.io/t/introducing-p2p-authentication-and-mismatch-resolution-in-v0-12-2/97993>`_
15+
- RPC - `0.5.1 <https://github.com/starkware-libs/starknet-specs/releases/tag/v0.5.1>`_
16+
17+
18+
0.18.3 Breaking changes
19+
-----------------------
20+
21+
1. Support for ``TESTNET2`` network has been removed.
22+
23+
.. currentmodule:: starknet_py.net.client
24+
25+
2. :meth:`FullNodeClient.get_pending_transactions` method has been removed. It is advised to use :meth:`FullNodeClient.get_block` method with ``block_number="pending"`` argument.
26+
27+
.. currentmodule:: starknet_py.net.client_models
28+
29+
3. :class:`PendingStarknetBlock` field ``parent_hash`` is now named ``parent_block_hash``.
30+
4. :class:`FunctionInvocation` fields ``events`` and ``messages`` have been changed from ``List[Event]`` and ``List[L2toL1Message]`` to ``List[OrderedEvent]`` and ``List[OrderedMessage]`` respectively.
31+
5. ``cairo_version`` parameter in :meth:`Account.sign_invoke_transaction` and :meth:`Account.execute` has been removed.
32+
33+
0.18.3 Minor changes
34+
--------------------
35+
36+
1. :class:`StarknetBlock`, :class:`StarknetBlockWithTxHashes`, :class:`PendingStarknetBlock` and :class:`PendingStarknetBlockWithTxHashes` now have two additional fields: ``starknet_version`` and ``l1_gas_price``.
37+
2. :class:`PendingStarknetBlock` and :class:`PendingStarknetBlockWithTxHashes` fields ``timestamp``, ``sequencer_address`` and ``parent_block_hash`` are now required, not optional.
38+
3. :class:`TransactionReceipt` now has an additional field - ``message_hash`` (for ``L1_HANDLER_TXN_RECEIPT``).
39+
4. Most fields in ``TransactionTrace`` classes are now optional.
40+
5. :class:`InvokeTransactionTrace`, :class:`DeclareTransactionTrace`, :class:`DeployAccountTransactionTrace` and :class:`L1HandlerTransactionTrace` classes now have an additional field - ``state_diff``.
41+
42+
43+
|
44+
45+
.. raw:: html
46+
47+
<hr>
48+
49+
|
50+
451
**********************
552
0.18.2 Migration guide
653
**********************

pyproject.toml

Lines changed: 30 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "starknet-py"
3-
version = "0.18.2"
3+
version = "0.18.3"
44
description = "A python SDK for Starknet"
55
authors = ["Tomasz Rejowski <tomasz.rejowski@swmansion.com>", "Jakub Ptak <jakub.ptak@swmansion.com>"]
66
include = ["starknet_py", "starknet_py/utils/crypto/libcrypto_c_exports.*"]
@@ -54,31 +54,37 @@ cairo-lang = {version = "0.12.2", python = ">=3.9, <3.10"}
5454
starknet-devnet = {version = "0.6.2", python = ">=3.9, <3.10"}
5555

5656
[tool.poe.tasks]
57-
test.shell = "pytest -n auto -v --reruns 10 --only-rerun aiohttp.client_exceptions.ClientConnectorError --cov=starknet_py starknet_py"
57+
test = [
58+
"clean_coverage",
59+
"test_ci_gateway_v1 --disable-warnings -qq",
60+
"test_ci_full_node_v1 --disable-warnings -qq",
61+
"test_ci_gateway_v2 --disable-warnings -qq",
62+
"test_ci_full_node_v2 --disable-warnings -qq",
63+
"test_ci_on_networks_gateway --disable-warnings -qq",
64+
"test_ci_on_networks_full_node --disable-warnings -qq",
65+
"test_report --skip-covered"
66+
]
5867

5968
test_ci = ["test_ci_gateway_v1", "test_ci_full_node_v1", "test_ci_gateway_v2", "test_ci_full_node_v2"]
60-
test_ci_gateway_v1.shell = "coverage run -m pytest --client=gateway --contract_dir=v1 -v --reruns 5 --only-rerun aiohttp.client_exceptions.ClientConnectorError starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks"
61-
test_ci_full_node_v1.shell = "coverage run -m pytest --client=full_node --contract_dir=v1 -v --reruns 5 --only-rerun aiohttp.client_exceptions.ClientConnectorError starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks"
62-
test_ci_gateway_v2.shell = "coverage run -m pytest --client=gateway --contract_dir=v2 -v --reruns 5 --only-rerun aiohttp.client_exceptions.ClientConnectorError starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks"
63-
test_ci_full_node_v2.shell = "coverage run -m pytest --client=full_node --contract_dir=v2 -v --reruns 5 --only-rerun aiohttp.client_exceptions.ClientConnectorError starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks"
69+
test_ci_gateway_v1 = "coverage run -a -m pytest --client=gateway --contract_dir=v1 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks"
70+
test_ci_full_node_v1 = "coverage run -a -m pytest --client=full_node --contract_dir=v1 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks"
71+
test_ci_gateway_v2 = "coverage run -a -m pytest --client=gateway --contract_dir=v2 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks"
72+
test_ci_full_node_v2 = "coverage run -a -m pytest --client=full_node --contract_dir=v2 starknet_py --ignore=starknet_py/tests/e2e/docs --ignore=starknet_py/tests/e2e/core --ignore=starknet_py/tests/e2e/tests_on_networks"
6473

6574
# order of tests below is important, explanation in /tests_on_networks/client_test.py above 'test_wait_for_tx_reverted_full_node'
6675
test_ci_on_networks = ["test_ci_on_networks_full_node", "test_ci_on_networks_gateway"]
67-
test_ci_on_networks_gateway = "coverage run -m pytest --client=gateway -v --reruns 5 --only-rerun aiohttp.client_exceptions.ClientConnectorError starknet_py/tests/e2e/tests_on_networks"
68-
test_ci_on_networks_full_node = "coverage run -m pytest --client=full_node -v --reruns 5 --only-rerun aiohttp.client_exceptions.ClientConnectorError starknet_py/tests/e2e/tests_on_networks"
76+
test_ci_on_networks_gateway = "coverage run -a -m pytest --client=gateway starknet_py/tests/e2e/tests_on_networks"
77+
test_ci_on_networks_full_node = "coverage run -a -m pytest --client=full_node starknet_py/tests/e2e/tests_on_networks"
6978

7079
test_ci_docs = ["test_ci_docs_gateway_v1", "test_ci_docs_full_node_v1", "test_ci_docs_gateway_v2", "test_ci_docs_full_node_v2"]
71-
test_ci_docs_gateway_v1.shell = "coverage run -m pytest --client=gateway --contract_dir=v1 -v --reruns 5 --only-rerun aiohttp.client_exceptions.ClientConnectorError starknet_py/tests/e2e/docs"
72-
test_ci_docs_full_node_v1.shell = "coverage run -m pytest --client=full_node --contract_dir=v1 -v --reruns 5 --only-rerun aiohttp.client_exceptions.ClientConnectorError starknet_py/tests/e2e/docs"
73-
test_ci_docs_gateway_v2.shell = "coverage run -m pytest --client=gateway --contract_dir=v2 -v --reruns 5 --only-rerun aiohttp.client_exceptions.ClientConnectorError starknet_py/tests/e2e/docs"
74-
test_ci_docs_full_node_v2.shell = "coverage run -m pytest --client=full_node --contract_dir=v2 -v --reruns 5 --only-rerun aiohttp.client_exceptions.ClientConnectorError starknet_py/tests/e2e/docs"
75-
76-
test_unit.shell = "pytest -n auto -v starknet_py --ignore=starknet_py/tests/e2e"
77-
test_e2e.shell = "pytest -n auto -v starknet_py/tests/e2e --ignore=starknet_py/tests/e2e/docs"
78-
test_docs.shell = "pytest -n auto -v starknet_py/tests/e2e/docs"
79-
test_core.shell = "pytest -v starknet_py/tests/e2e/core --net=integration"
80-
test_report.shell = "coverage report"
80+
test_ci_docs_gateway_v1 = "coverage run -a -m pytest --client=gateway --contract_dir=v1 starknet_py/tests/e2e/docs"
81+
test_ci_docs_full_node_v1 = "coverage run -a -m pytest --client=full_node --contract_dir=v1 starknet_py/tests/e2e/docs"
82+
test_ci_docs_gateway_v2 = "coverage run -a -m pytest --client=gateway --contract_dir=v2 starknet_py/tests/e2e/docs"
83+
test_ci_docs_full_node_v2 = "coverage run -a -m pytest --client=full_node --contract_dir=v2 starknet_py/tests/e2e/docs"
84+
85+
test_report = "coverage report -m"
8186
test_html.shell = "coverage html && open ./htmlcov/index.html"
87+
clean_coverage = "coverage erase"
8288
docs_create = { shell = "make -C docs html" }
8389
docs_open = { shell = "open docs/_build/html/index.html" }
8490
lint = "pylint starknet_py"
@@ -102,7 +108,7 @@ source = ["starknet_py"]
102108

103109

104110
[tool.coverage.report]
105-
omit = ["*_test.py", "starknet_py/tests/e2e/*", "starknet_py/utils/docs.py"]
111+
omit = ["*_test.py", "test_*.py", "starknet_py/tests/*"]
106112
skip_empty = true
107113

108114

@@ -128,6 +134,11 @@ profile = "black"
128134
skip_gitignore = true
129135

130136
[tool.pytest.ini_options]
137+
addopts = [
138+
"-v",
139+
"--reruns=5",
140+
"--only-rerun=aiohttp.client_exceptions.ClientConnectorError"
141+
]
131142
markers = [
132143
"run_on_testnet: marks test that will only run on testnet (when --net=testnet)",
133144
"run_on_devnet: marks test that will only run on devnet (when --net=devnet)"

starknet_py/hash/address.py

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
from typing import Sequence
22

33
from starknet_py.constants import CONTRACT_ADDRESS_PREFIX, L2_ADDRESS_UPPER_BOUND
4-
from starknet_py.hash.utils import compute_hash_on_elements
4+
from starknet_py.hash.utils import (
5+
HEX_PREFIX,
6+
_starknet_keccak,
7+
compute_hash_on_elements,
8+
encode_uint,
9+
get_bytes_length,
10+
)
511

612

713
def compute_address(
@@ -33,3 +39,41 @@ def compute_address(
3339
)
3440

3541
return raw_address % L2_ADDRESS_UPPER_BOUND
42+
43+
44+
def get_checksum_address(address: str) -> str:
45+
"""
46+
Outputs formatted checksum address.
47+
48+
Follows implementation of starknet.js. It is not compatible with EIP55 as it treats hex string as encoded number,
49+
instead of encoding it as ASCII string.
50+
51+
:param address: Address to encode
52+
:return: Checksum address
53+
"""
54+
if not address.lower().startswith(HEX_PREFIX):
55+
raise ValueError(f"{address} is not a valid hexadecimal address.")
56+
57+
int_address = int(address, 16)
58+
string_address = address[2:].zfill(64)
59+
60+
address_in_bytes = encode_uint(int_address, get_bytes_length(int_address))
61+
address_hash = _starknet_keccak(address_in_bytes)
62+
63+
result = "".join(
64+
(
65+
char.upper()
66+
if char.isalpha() and (address_hash >> 256 - 4 * i - 1) & 1
67+
else char
68+
)
69+
for i, char in enumerate(string_address)
70+
)
71+
72+
return f"{HEX_PREFIX}{result}"
73+
74+
75+
def is_checksum_address(address: str) -> bool:
76+
"""
77+
Checks if provided string is in a checksum address format.
78+
"""
79+
return get_checksum_address(address) == address

starknet_py/hash/address_test.py

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
1-
from starknet_py.hash.address import compute_address
1+
import pytest
2+
3+
from starknet_py.hash.address import (
4+
compute_address,
5+
get_checksum_address,
6+
is_checksum_address,
7+
)
28

39

410
def test_compute_address():
@@ -22,3 +28,46 @@ def test_compute_address_with_deployer_address():
2228
)
2329
== 3179899882984850239687045389724311807765146621017486664543269641150383510696
2430
)
31+
32+
33+
@pytest.mark.parametrize(
34+
"address, checksum_address",
35+
[
36+
(
37+
"0x2fd23d9182193775423497fc0c472e156c57c69e4089a1967fb288a2d84e914",
38+
"0x02Fd23d9182193775423497fc0c472E156C57C69E4089A1967fb288A2d84e914",
39+
),
40+
(
41+
"0x00abcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefabcdefab",
42+
"0x00AbcDefaBcdefabCDEfAbCDEfAbcdEFAbCDEfabCDefaBCdEFaBcDeFaBcDefAb",
43+
),
44+
(
45+
"0xfedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafedcbafe",
46+
"0x00fEdCBafEdcbafEDCbAFedCBAFeDCbafEdCBAfeDcbaFeDCbAfEDCbAfeDcbAFE",
47+
),
48+
("0xa", "0x000000000000000000000000000000000000000000000000000000000000000A"),
49+
(
50+
"0x0",
51+
"0x0000000000000000000000000000000000000000000000000000000000000000",
52+
),
53+
],
54+
)
55+
def test_get_checksum_address(address, checksum_address):
56+
assert get_checksum_address(address) == checksum_address
57+
58+
59+
@pytest.mark.parametrize("address", ["", "0xx", "0123"])
60+
def test_get_checksum_address_raises_on_invalid_address(address):
61+
with pytest.raises(ValueError):
62+
get_checksum_address(address)
63+
64+
65+
@pytest.mark.parametrize(
66+
"address, is_checksum",
67+
[
68+
("0x02Fd23d9182193775423497fc0c472E156C57C69E4089A1967fb288A2d84e914", True),
69+
("0x000000000000000000000000000000000000000000000000000000000000000a", False),
70+
],
71+
)
72+
def test_is_checksum_address(address, is_checksum):
73+
assert is_checksum_address(address) == is_checksum

starknet_py/hash/utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from starknet_py.constants import EC_ORDER
1515

1616
MASK_250 = 2**250 - 1
17+
HEX_PREFIX = "0x"
1718

1819

1920
def _starknet_keccak(data: bytes) -> int:
@@ -25,6 +26,12 @@ def _starknet_keccak(data: bytes) -> int:
2526
return int_from_bytes(k.digest()) & MASK_250
2627

2728

29+
def keccak256(data: bytes) -> int:
30+
k = keccak.new(digest_bits=256)
31+
k.update(data)
32+
return int_from_bytes(k.digest())
33+
34+
2835
def pedersen_hash(left: int, right: int) -> int:
2936
"""
3037
One of two hash functions (along with _starknet_keccak) used throughout Starknet.
@@ -70,3 +77,15 @@ def private_to_stark_key(priv_key: int) -> int:
7077
Deduces the public key given a private key.
7178
"""
7279
return cpp_get_public_key(priv_key)
80+
81+
82+
def encode_uint(value: int, bytes_length: int = 32) -> bytes:
83+
return value.to_bytes(bytes_length, byteorder="big")
84+
85+
86+
def encode_uint_list(data: List[int]) -> bytes:
87+
return b"".join(encode_uint(x) for x in data)
88+
89+
90+
def get_bytes_length(value: int) -> int:
91+
return (value.bit_length() + 7) // 8

starknet_py/hash/utils_test.py

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,13 @@
22
# fmt: off
33
import pytest
44

5-
from starknet_py.hash.utils import compute_hash_on_elements, pedersen_hash
5+
from starknet_py.hash.utils import (
6+
compute_hash_on_elements,
7+
encode_uint,
8+
encode_uint_list,
9+
keccak256,
10+
pedersen_hash,
11+
)
612

713

814
@pytest.mark.parametrize(
@@ -33,3 +39,49 @@ def test_compute_hash_on_elements(data, calculated_hash):
3339
)
3440
def test_pedersen_hash(first, second, hash_):
3541
assert pedersen_hash(first, second) == hash_
42+
43+
44+
@pytest.mark.parametrize(
45+
"value, expected_encoded",
46+
[
47+
(0, b"\x00" * 32),
48+
(1, b"\x00" * 31 + b"\x01"),
49+
(123456789, b"\x00" * 28 + b"\x07\x5b\xcd\x15")
50+
]
51+
)
52+
def test_encode_uint(value, expected_encoded):
53+
assert encode_uint(value) == expected_encoded
54+
55+
56+
@pytest.mark.parametrize(
57+
"value, expected_encoded",
58+
[
59+
([], b""),
60+
([1, 2, 3], b"\x00" * 31 + b"\x01" + b"\x00" * 31 + b"\x02" + b"\x00" * 31 + b"\x03"),
61+
]
62+
)
63+
def test_encode_uint_list(value, expected_encoded):
64+
assert encode_uint_list(value) == expected_encoded
65+
66+
67+
@pytest.mark.parametrize(
68+
"string, expected_hash",
69+
[
70+
("", 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470),
71+
("test", 0x9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658),
72+
("longer test string", 0x47bed17bfbbc08d6b5a0f603eff1b3e932c37c10b865847a7bc73d55b260f32a)
73+
]
74+
)
75+
def test_keccak256_strings(string, expected_hash):
76+
assert keccak256(string.encode("utf-8")) == expected_hash
77+
78+
79+
@pytest.mark.parametrize(
80+
"value, expected_hash",
81+
[
82+
(4, 0x8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b),
83+
(5, 0x036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0)
84+
]
85+
)
86+
def test_keccak256_ints(value, expected_hash):
87+
assert keccak256(encode_uint(value)) == expected_hash

0 commit comments

Comments
 (0)