Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Verify types in TypedData in line with SNIP-12 #1369

Merged
merged 129 commits into from
Jun 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
129 commits
Select commit Hold shift + click to select a range
95fe8a8
Differentiate typed data files by revision
franciszekjob Jun 14, 2024
139f970
Add `HashMethod`
franciszekjob Jun 14, 2024
1317848
Add `Revision` enum; Rename `StarkNetDomain` to `Domain`
franciszekjob Jun 14, 2024
2cf0aaa
Add tests to revision 1
franciszekjob Jun 14, 2024
9d9fda3
Remove prints
franciszekjob Jun 14, 2024
f67cdb7
Add more rev 1 test cases to `test_type_hash`
franciszekjob Jun 14, 2024
5469dd3
Change values param to `List[int]` in `HashMethod.hash()`
franciszekjob Jun 16, 2024
1be0060
Add `DomainSchema`; Add `RevisionField` and `ChainIdField`
franciszekjob Jun 17, 2024
9781a21
Move `HashMethod` to separate file
franciszekjob Jun 17, 2024
a7e7f89
Refactor `Domain.to_dict()`
franciszekjob Jun 17, 2024
57c16ec
Update comment
franciszekjob Jun 17, 2024
c648f84
Add `TypedData._hash_method` field
franciszekjob Jun 17, 2024
74cf615
Remove `TypedData._hash_method` field
franciszekjob Jun 17, 2024
549e143
Update docs
franciszekjob Jun 17, 2024
9c67d18
Update docs
franciszekjob Jun 17, 2024
be1e1ec
Merge branch 'franciszekjob/1353-snip-12' of https://github.com/softw…
franciszekjob Jun 17, 2024
23562a4
Update comment
franciszekjob Jun 17, 2024
1c4cde6
Reduce `Domain._verify_types()`
franciszekjob Jun 18, 2024
e158122
Remove unnecessary variable duplication
franciszekjob Jun 18, 2024
57ed19f
Remove unused functions
franciszekjob Jun 18, 2024
64a7a6a
Add merkle tree type data jsons
franciszekjob Jun 20, 2024
599f6bd
Add `HashMethod.hash_many()`
franciszekjob Jun 20, 2024
8b6e28d
Add `MerkleTree` dataclass; Add merkle tree tests
franciszekjob Jun 20, 2024
d7540eb
Add `Parameter.contains` to handle merkle parameter
franciszekjob Jun 20, 2024
9290b5e
Introduce merkle trees in `TypedData`; Add merkle tree cases in typed…
franciszekjob Jun 20, 2024
3d92324
Convert `TypedData._hash_method` to `@property`
franciszekjob Jun 20, 2024
0c651d4
Change `Domain.revision` type to `Revision`
franciszekjob Jun 20, 2024
961e489
Use equality comparison for `resolved_revision`
franciszekjob Jun 20, 2024
e98ed80
Merge branch 'franciszekjob/1353-snip-12' of https://github.com/softw…
franciszekjob Jun 20, 2024
59a7b2e
Set `revision` in `Domain` typed dict to be `Optional`
franciszekjob Jun 20, 2024
80e9b87
Update import
franciszekjob Jun 20, 2024
9335160
Refactor `Domain.to_dixt()`
franciszekjob Jun 20, 2024
ff8431a
Resolve conflicts with upstream branch
franciszekjob Jun 20, 2024
f189b04
Format
franciszekjob Jun 20, 2024
8d31b56
Fix doc examples for `sign_message` and `verify_message`; Update func…
franciszekjob Jun 20, 2024
493f4fa
Remove unused import
franciszekjob Jun 20, 2024
186df9d
Remove `test_sign_message_rev_v0` and `test_verify_message_rev_v0`
franciszekjob Jun 20, 2024
ed0f402
Change `sign_message()` and `verify_message()` to accept `TypedData` …
franciszekjob Jun 20, 2024
73680cb
Update migration guide
franciszekjob Jun 20, 2024
1131277
Resolve conflicts
franciszekjob Jun 20, 2024
256b334
Update `test_sign_offchain_message()` to use `TypedData` class instance
franciszekjob Jun 20, 2024
6b52ea5
Update previous changes in `sign_message()` and `verify_message()`
franciszekjob Jun 20, 2024
0d3b592
Update `sign_message()` and `verify_message()` description comments
franciszekjob Jun 20, 2024
0c80872
Minor `DomainSchema.make_dataclass()` refactor
franciszekjob Jun 20, 2024
35ddc45
Merge branch 'franciszekjob/1353-snip-12' of https://github.com/softw…
franciszekjob Jun 20, 2024
10d9a1c
Minor refactor of `HashMethod`
franciszekjob Jun 20, 2024
48d90a8
Merge branch 'development' of https://github.com/software-mansion/sta…
franciszekjob Jun 20, 2024
46b8a48
Resolve conflicts with upstream branch
franciszekjob Jun 21, 2024
29e540d
Restore retrieving `revision` using `data.get()`
franciszekjob Jun 21, 2024
d0602af
Resolve conflicts with upstream branch
franciszekjob Jun 21, 2024
5c9c931
Add pylint directives to disable specific warnings
franciszekjob Jun 21, 2024
deef39d
Format
franciszekjob Jun 21, 2024
5ff98ed
Restore `**kwargs` in `make_dataclass()` methods
franciszekjob Jun 21, 2024
e163d23
Rename `is_pointer()` to `is_array()`; Refactor `is_array()`
franciszekjob Jun 21, 2024
3d35717
Check for domain separator name in `TypedData.types`
franciszekjob Jun 21, 2024
0a3a45c
Extend typed data types verification
franciszekjob Jun 21, 2024
555040d
Add custom error message on missing value in `BasicType` enum
franciszekjob Jun 21, 2024
75b9fdb
Extend typed data tests for types verification
franciszekjob Jun 21, 2024
9858c43
Merge branch 'franciszekjob/1353-2-merkletree' of https://github.com/…
franciszekjob Jun 21, 2024
c4c9379
Add missing type annotation in `test_invalid_types()`
franciszekjob Jun 21, 2024
ff1d408
Merge branch 'franciszekjob/1353-snip-12' of https://github.com/softw…
franciszekjob Jun 21, 2024
487ecc1
Refactor checking error message in `test_missing_dependency()`
franciszekjob Jun 21, 2024
39982e3
Update `RevisionField._deserialize()` return type to `Revision`
franciszekjob Jun 21, 2024
dfec5e9
Add custom error message on missing value in `Revision` enum
franciszekjob Jun 21, 2024
3ae8bd7
Add module in migration guide to enable hyperlinks
franciszekjob Jun 21, 2024
09ad981
Check error message in `test_invalid_types`
franciszekjob Jun 21, 2024
e5240c0
Add `Parameter.to_dict()` and `TypedData.to_dict()`
franciszekjob Jun 21, 2024
27c24a1
Move `Revision` to common schemas
franciszekjob Jun 21, 2024
e6a2f26
Format
franciszekjob Jun 21, 2024
f604dbb
Set `ChainIdField._deserialize()` return type to `str`; Change 'Domai…
franciszekjob Jun 21, 2024
5505baa
Remove unnecessary import
franciszekjob Jun 21, 2024
3e00175
Resolve conflicts with upstream branch
franciszekjob Jun 21, 2024
bd6fc8f
Restore previous import of `keccak256`
franciszekjob Jun 21, 2024
37b650b
Format
franciszekjob Jun 21, 2024
7e1fcd1
Add newline in typed data rev 1 example json
franciszekjob Jun 21, 2024
1383a00
Merge branch 'franciszekjob/1353-snip-12' of https://github.com/softw…
franciszekjob Jun 21, 2024
1477a3d
Add newlines in typed data example jsons
franciszekjob Jun 21, 2024
f355eca
Update starknet_py/utils/merkle_tree.py
franciszekjob Jun 21, 2024
65f3af9
Merge branch 'franciszekjob/1353-2-merkletree' of https://github.com/…
franciszekjob Jun 21, 2024
30f8bc9
Use `str` instead of `int` in `MerkleTree`
franciszekjob Jun 21, 2024
ecb4c35
Inline `BasicType._missing()` in `TypedData.verify_types()`
franciszekjob Jun 24, 2024
2ae6ff6
Format
franciszekjob Jun 24, 2024
59ed61d
Format
franciszekjob Jun 24, 2024
f7ab76c
Resolve conflicts with upstream branch
franciszekjob Jun 24, 2024
9492df3
Match error message in tests
franciszekjob Jun 24, 2024
2ae86bd
Rename `is_array()` to `is_pointer()`
franciszekjob Jun 24, 2024
f4eeb4f
Update raised error message in `TypedData._encode_value()`
franciszekjob Jun 24, 2024
36a6cb4
Remove unnecessary `ChainIdField`
franciszekjob Jun 24, 2024
c057544
Change chainId from `int` to `str` in all examples
franciszekjob Jun 24, 2024
2da5a66
Refactor `TypedData.to_dict()`
franciszekjob Jun 24, 2024
2b5a44d
Resolve conflicts with upstream branch
franciszekjob Jun 24, 2024
4d6d05f
Format
franciszekjob Jun 24, 2024
04a8d14
Rename `branches` to `levels` in `MerkleTree`
franciszekjob Jun 24, 2024
d429b4e
Rename variables in `MerkleTree.build()`
franciszekjob Jun 24, 2024
65e79fc
Remove `Parameter.to_dict()`; Add `RevisionField._serialize()`
franciszekjob Jun 24, 2024
f64ff2e
Merge branch 'franciszekjob/1353-snip-12' of https://github.com/softw…
franciszekjob Jun 24, 2024
3574b8c
Update starknet_py/net/schemas/common.py
franciszekjob Jun 24, 2024
0412e27
Update starknet_py/net/schemas/common.py
franciszekjob Jun 24, 2024
6d59203
Merge branch 'franciszekjob/1353-snip-12' of https://github.com/softw…
franciszekjob Jun 24, 2024
59dd892
Merge branch 'franciszekjob/1353-2-merkletree' of https://github.com/…
franciszekjob Jun 24, 2024
58b590b
Change string to raw string literal
franciszekjob Jun 24, 2024
91ef12c
Remove revision check in `get_hex()`
franciszekjob Jun 24, 2024
98cd0db
Refactor `MerkleTree` implementation; Update merkle tree tests
franciszekjob Jun 25, 2024
eccf2c9
Format
franciszekjob Jun 25, 2024
dc957f0
Merge branch 'franciszekjob/1353-2-merkletree' of https://github.com/…
franciszekjob Jun 25, 2024
643d387
Remove unused `domain_object_v1`
franciszekjob Jun 25, 2024
1f8ddb6
Merge branch 'franciszekjob/1353-2-merkletree' of https://github.com/…
franciszekjob Jun 25, 2024
4552e81
Update example domain types in typed data tests
franciszekjob Jun 25, 2024
a59c8a2
Format
franciszekjob Jun 25, 2024
ecbed7b
Merge branch 'franciszekjob/1353-2-merkletree' of https://github.com/…
franciszekjob Jun 25, 2024
c490faf
Add leaves and root hash to returned merkle tree levels
franciszekjob Jun 25, 2024
faee6d4
Add check for merkle tree levels count
franciszekjob Jun 25, 2024
ac26be1
Merge branch 'franciszekjob/1353-2-merkletree' of https://github.com/…
franciszekjob Jun 25, 2024
5c3b4f4
Refactor `TypedData._get_merkle_tree_leaves_type()`
franciszekjob Jun 26, 2024
c606c30
Merge branch 'franciszekjob/1353-2-merkletree' of https://github.com/…
franciszekjob Jun 26, 2024
f1dda04
Update starknet_py/utils/typed_data.py
franciszekjob Jun 26, 2024
68d6217
Update starknet_py/utils/typed_data.py
franciszekjob Jun 26, 2024
1dfa542
Merge branch 'franciszekjob/1353-2-merkletree' of https://github.com/…
franciszekjob Jun 26, 2024
5334c2c
Format
franciszekjob Jun 27, 2024
6b9b745
Add `_get_basic_types_values()`
franciszekjob Jun 27, 2024
5afd511
Refactor retrieving `referenced_types`
franciszekjob Jun 27, 2024
6c4e4f6
Remove `_get_basic_types_values()`; Add `_get_basic_types()` with `re…
franciszekjob Jun 27, 2024
199d290
Rename `_get_basic_types_values()` to `_get_basic_types()`
franciszekjob Jun 27, 2024
0a8d2b4
Revert "Rename `_get_basic_types_values()` to `_get_basic_types()`"
franciszekjob Jun 27, 2024
165f5b8
Rename `_get_basic_types_values()` to `_get_basic_types()`; Add `Basi…
franciszekjob Jun 27, 2024
c694407
Format
franciszekjob Jun 27, 2024
51a865c
Rename `_get_basic_types_values()` to `_get_basic_type_names()`
franciszekjob Jun 27, 2024
c66466b
Refactor `test_types_redefinition()`
franciszekjob Jun 28, 2024
17225ab
Merge branch 'development' into franciszekjob/1353-3-verify-types
franciszekjob Jun 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 45 additions & 3 deletions starknet_py/utils/typed_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ def _encode_value(
hashes = [self._encode_value(type_name, val) for val in value]
return compute_hash_on_elements(hashes)

if type_name not in _get_basic_type_names(self.domain.resolved_revision):
raise ValueError(f"Type [{type_name}] is not defined in types.")

basic_type = BasicType(type_name)

if basic_type == BasicType.MERKLE_TREE and isinstance(value, list):
Expand Down Expand Up @@ -164,12 +167,34 @@ def _encode_data(self, type_name: str, data: dict) -> List[int]:
return values

def _verify_types(self):
reserved_type_names = ["felt", "felt*", "string", "selector", "merkletree"]
if self.domain.separator_name not in self.types:
raise ValueError(f"Types must contain '{self.domain.separator_name}'.")

basic_type_names = _get_basic_type_names(self.domain.resolved_revision)

for type_name in reserved_type_names:
for type_name in basic_type_names:
if type_name in self.types:
raise ValueError(f"Reserved type name: {type_name}")

referenced_types = {
ref_type.contains
if ref_type.contains is not None
else strip_pointer(ref_type.type)
for type_name in self.types
for ref_type in self.types[type_name]
}
referenced_types.update([self.domain.separator_name, self.primary_type])

for type_name in self.types:
if not type_name:
raise ValueError("Type names cannot be empty.")
if is_pointer(type_name):
raise ValueError(f"Type names cannot end in *. {type_name} was found.")
if type_name not in referenced_types:
raise ValueError(
f"Dangling types are not allowed. Unreferenced type {type_name} was found."
franciszekjob marked this conversation as resolved.
Show resolved Hide resolved
)

def _get_dependencies(self, type_name: str) -> List[str]:
if type_name not in self.types:
# type_name is a primitive type, has no dependencies
Expand Down Expand Up @@ -280,7 +305,7 @@ def get_hex(value: Union[int, str]) -> str:


def is_pointer(value: str) -> bool:
return len(value) > 0 and value[-1] == "*"
return value.endswith("*")


def strip_pointer(value: str) -> str:
Expand All @@ -306,9 +331,26 @@ class BasicType(Enum):
FELT = "felt"
SELECTOR = "selector"
MERKLE_TREE = "merkletree"
STRING = "string"
SHORT_STRING = "shortstring"


def _get_basic_type_names(revision: Revision) -> List[str]:
basic_types_v0 = [
BasicType.FELT,
BasicType.SELECTOR,
BasicType.MERKLE_TREE,
BasicType.STRING,
]

basic_types_v1 = basic_types_v0 + [
BasicType.SHORT_STRING,
]

basic_types = basic_types_v0 if revision == Revision.V0 else basic_types_v1
return [basic_type.value for basic_type in basic_types]


# pylint: disable=unused-argument
# pylint: disable=no-self-use

Expand Down
89 changes: 80 additions & 9 deletions starknet_py/utils/typed_data_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,13 @@

from starknet_py.net.models.typed_data import Revision
from starknet_py.tests.e2e.fixtures.constants import TYPED_DATA_DIR
from starknet_py.utils.typed_data import Domain, Parameter, TypedData, get_hex
from starknet_py.utils.typed_data import (
BasicType,
Domain,
Parameter,
TypedData,
get_hex,
)


class CasesRev0(Enum):
Expand Down Expand Up @@ -197,15 +203,80 @@ def _make_typed_data(included_type: str, revision: Revision):


@pytest.mark.parametrize(
"included_type",
"included_type, revision",
[
("", Revision.V1),
("myType*", Revision.V1)
],
)
def test_invalid_type_names(included_type: str, revision: Revision):
with pytest.raises(ValueError):
_make_typed_data(included_type, revision)


@pytest.mark.parametrize(
"included_type, revision",
[
"felt",
"felt*",
"string",
"selector",
"merkletree"
(BasicType.FELT.value, Revision.V0),
(BasicType.STRING.value, Revision.V0),
(BasicType.SELECTOR.value, Revision.V0),
(BasicType.MERKLE_TREE.value, Revision.V0),
(BasicType.FELT.value, Revision.V1),
(BasicType.STRING.value, Revision.V1),
(BasicType.SELECTOR.value, Revision.V1),
(BasicType.MERKLE_TREE.value, Revision.V1),
(BasicType.SHORT_STRING.value, Revision.V1),
],
)
def test_invalid_types(included_type: str):
def test_types_redefinition(included_type: str, revision: Revision):
with pytest.raises(ValueError, match=f"Reserved type name: {included_type}"):
_make_typed_data(included_type, Revision.V1)
_make_typed_data(included_type, revision)


def test_custom_type_definition():
_make_typed_data("myType", Revision.V0)
franciszekjob marked this conversation as resolved.
Show resolved Hide resolved


@pytest.mark.parametrize(
"revision",
list(Revision),
)
def test_missing_domain_type(revision: Revision):
domain = domain_v0 if revision == Revision.V0 else domain_v1

with pytest.raises(ValueError, match=f"Types must contain '{domain.separator_name}'."):
TypedData(
types={},
primary_type="felt",
domain=domain,
message={},
)


def test_dangling_type():
with pytest.raises(ValueError, match="Dangling types are not allowed. Unreferenced type dangling was found."):
TypedData(
types={
**domain_type_v1,
"dangling": [],
"mytype": []
},
primary_type="mytype",
domain=domain_v1,
message={"mytype": 1},
)


def test_missing_dependency():
typed_data = TypedData(
types={
**domain_type_v1,
"house": [Parameter(name="fridge", type="ice cream")]
},
primary_type="house",
domain=domain_v1,
message={"fridge": 1},
)

with pytest.raises(ValueError, match=r"Type \[ice cream\] is not defined in types."):
typed_data.struct_hash("house", {"fridge": 1})
Loading