Skip to content

Commit cf2e629

Browse files
authored
feat(verified_contracts): enhance transformations constraints (#27)
* test(compiled_contracts): add tests for invalid array item types for 'creation_code_artifacts.linkReferences' and 'runtime_code_artifacts.linkReferences' constraints * test(verified_contracts): refactor 'creation_values.constructorArguments' tests * feat(verified_contracts): added check that the key is not empty in 'values_libraries' check * feat(verified_contracts): add check that the key is not empty in 'values_immutables' check * feat(verified_contracts): add check that the key is not empty in 'values_cbor_auxdata' check * fix(verified_contracts): make callProtection transformation to always have offset 1
1 parent fc41980 commit cf2e629

4 files changed

Lines changed: 102 additions & 66 deletions

database.sql

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -684,14 +684,16 @@ BEGIN
684684
RETURN true;
685685
END IF;
686686

687-
-- we have to use IF, so that internal select subquery is executed only when `obj -> 'libraries'` is an object
688-
IF is_jsonb_object(obj -> 'libraries') THEN
689-
RETURN bool_and(are_valid_values)
690-
FROM (SELECT is_jsonb_string(value) AND is_valid_hex(value ->> 0, '{20}') as are_valid_values
691-
FROM jsonb_each(obj -> 'libraries')) as subquery;
692-
ELSE
687+
IF NOT is_jsonb_object(obj -> 'libraries') THEN
693688
RETURN false;
694689
END IF;
690+
691+
RETURN bool_and(
692+
length(key) > 0 AND
693+
is_jsonb_string(value) AND
694+
is_valid_hex(value ->> 0, '{20}')
695+
)
696+
FROM jsonb_each(obj -> 'libraries');
695697
END;
696698
$$ LANGUAGE plpgsql;
697699

@@ -704,13 +706,16 @@ BEGIN
704706
RETURN true;
705707
END IF;
706708

707-
IF is_jsonb_object(obj -> 'immutables') THEN
708-
RETURN bool_and(are_valid_values)
709-
FROM (SELECT is_jsonb_string(value) AND is_valid_hex(value ->> 0, '+') as are_valid_values
710-
FROM jsonb_each(obj -> 'immutables')) as subquery;
711-
ELSE
709+
IF NOT is_jsonb_object(obj -> 'immutables') THEN
712710
RETURN false;
713711
END IF;
712+
713+
RETURN bool_and(
714+
length(key) > 0 AND
715+
is_jsonb_string(value) AND
716+
is_valid_hex(value ->> 0, '+')
717+
)
718+
FROM jsonb_each(obj -> 'immutables');
714719
END;
715720
$$ LANGUAGE plpgsql;
716721

@@ -723,13 +728,16 @@ BEGIN
723728
RETURN true;
724729
END IF;
725730

726-
IF is_jsonb_object(obj -> 'cborAuxdata') THEN
727-
RETURN bool_and(are_valid_values)
728-
FROM (SELECT is_jsonb_string(value) AND is_valid_hex(value ->> 0, '+') as are_valid_values
729-
FROM jsonb_each(obj -> 'cborAuxdata')) as subquery;
730-
ELSE
731+
IF NOT is_jsonb_object(obj -> 'cborAuxdata') THEN
731732
RETURN false;
732733
END IF;
734+
735+
RETURN bool_and(
736+
length(key) > 0 AND
737+
is_jsonb_string(value) AND
738+
is_valid_hex(value ->> 0, '+')
739+
)
740+
FROM jsonb_each(obj -> 'cborAuxdata');
733741
END;
734742
$$ LANGUAGE plpgsql;
735743

@@ -846,7 +854,9 @@ CREATE OR REPLACE FUNCTION validate_transformations_call_protection(object jsonb
846854
RETURNS boolean AS
847855
$$
848856
BEGIN
849-
RETURN validate_transformation_key_type(object, 'replace') AND validate_transformation_key_offset(object);
857+
RETURN validate_transformation_key_type(object, 'replace')
858+
-- 'callProtection' value is always located at offset 1
859+
AND validate_transformation_key_offset(object) AND (object ->> 'offset')::integer = 1;
850860
END;
851861
$$ LANGUAGE plpgsql;
852862

tests/test_constraint_creation_values_json_schema.py

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def test_valid_field_value(self, connection, dummy_code, dummy_contract, dummy_c
6464
dummy_verified_contract.insert(
6565
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)
6666

67-
@pytest.mark.parametrize("value", [None, [], dict()], ids=["null", "array", "object"])
67+
@pytest.mark.parametrize("value", [None, 0, [], dict()], ids=["null", "number", "array", "object"])
6868
def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
6969
dummy_verified_contract.creation_values = dict({
7070
"constructorArguments": value
@@ -74,36 +74,11 @@ def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract,
7474
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
7575
"creation_values_json_schema")
7676

77-
def test_without_0x_prefix_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
77+
@pytest.mark.parametrize("value", ["0x", "1234", "0xqwer", "0x123"],
78+
ids=["empty_hex", "without_0x_prefix", "not_hex", "odd_length"])
79+
def test_invalid_value_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
7880
dummy_verified_contract.creation_values = dict({
79-
"constructorArguments": "1234"
80-
})
81-
check_constraint_fails(
82-
lambda: dummy_verified_contract.insert(
83-
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
84-
"creation_values_json_schema")
85-
86-
def test_not_valid_hex_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
87-
dummy_verified_contract.creation_values = dict({
88-
"constructorArguments": "0xqwer"
89-
})
90-
check_constraint_fails(
91-
lambda: dummy_verified_contract.insert(
92-
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
93-
"creation_values_json_schema")
94-
95-
def test_empty_hex_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
96-
dummy_verified_contract.creation_values = dict({
97-
"constructorArguments": "0x"
98-
})
99-
check_constraint_fails(
100-
lambda: dummy_verified_contract.insert(
101-
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
102-
"creation_values_json_schema")
103-
104-
def test_odd_length_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
105-
dummy_verified_contract.creation_values = dict({
106-
"constructorArguments": "0x123"
81+
"constructorArguments": value
10782
})
10883
check_constraint_fails(
10984
lambda: dummy_verified_contract.insert(
@@ -131,7 +106,7 @@ def test_empty_object(self, connection, dummy_code, dummy_contract,
131106
dummy_verified_contract.insert(
132107
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)
133108

134-
@pytest.mark.parametrize("value", [None, [], ""], ids=["null", "array", "string"])
109+
@pytest.mark.parametrize("value", [None, 0, [], ""], ids=["null", "number", "array", "string"])
135110
def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract,
136111
dummy_contract_deployment, dummy_compiled_contract,
137112
dummy_verified_contract):
@@ -143,7 +118,17 @@ def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract,
143118
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
144119
"creation_values_json_schema")
145120

146-
@pytest.mark.parametrize("value", [None, [], dict({})], ids=["null", "array", "object"])
121+
def test_empty_key_name_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment,
122+
dummy_compiled_contract, dummy_verified_contract):
123+
dummy_verified_contract.creation_values = dict({
124+
"libraries": {"": "0x4000000000000000000000000000000000000000"}
125+
})
126+
check_constraint_fails(
127+
lambda: dummy_verified_contract.insert(
128+
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
129+
"creation_values_json_schema")
130+
131+
@pytest.mark.parametrize("value", [None, 0, [], dict({})], ids=["null", "number", "array", "object"])
147132
def test_additional_properties_with_invalid_type_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment,
148133
dummy_compiled_contract, dummy_verified_contract):
149134
dummy_verified_contract.creation_values = dict({
@@ -188,8 +173,8 @@ def test_values_one_fail_all_fails(self, connection, dummy_code, dummy_contract,
188173
dummy_compiled_contract, dummy_verified_contract):
189174
dummy_verified_contract.creation_values = dict({
190175
"libraries": {
191-
"file1:lib1": "4000000000000000000000000000000000000000000000000",
192-
"file2:lib2": "0x4000000000000000000000000000000000000000000000000"
176+
"file1:lib1": "0x4000000000000000000000000000000000000000000000000",
177+
"file2:lib2": "4000000000000000000000000000000000000000000000000"
193178
}
194179
})
195180
check_constraint_fails(
@@ -218,7 +203,7 @@ def test_empty_object(self, connection, dummy_code, dummy_contract,
218203
dummy_verified_contract.insert(
219204
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)
220205

221-
@pytest.mark.parametrize("value", [None, [], ""], ids=["null", "array", "string"])
206+
@pytest.mark.parametrize("value", [None, 0, [], ""], ids=["null", "number", "array", "string"])
222207
def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract,
223208
dummy_contract_deployment, dummy_compiled_contract,
224209
dummy_verified_contract):
@@ -230,7 +215,17 @@ def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract,
230215
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
231216
"creation_values_json_schema")
232217

233-
@pytest.mark.parametrize("value", [None, [], dict({})], ids=["null", "array", "object"])
218+
def test_empty_key_name_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment,
219+
dummy_compiled_contract, dummy_verified_contract):
220+
dummy_verified_contract.creation_values = dict({
221+
"cborAuxdata": {"": "0x00000000000000000000000000000000000000000000000000000000000000000000000000"}
222+
})
223+
check_constraint_fails(
224+
lambda: dummy_verified_contract.insert(
225+
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
226+
"creation_values_json_schema")
227+
228+
@pytest.mark.parametrize("value", [None, 0, [], dict({})], ids=["null", "number", "array", "object"])
234229
def test_additional_properties_with_invalid_type_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment,
235230
dummy_compiled_contract, dummy_verified_contract):
236231
dummy_verified_contract.creation_values = dict({

tests/test_constraint_runtime_transformations_json_schema.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -379,14 +379,14 @@ def test_invalid_key_id_value_fails(self, connection, dummy_code, dummy_contract
379379
class TestCallProtection:
380380
def test_valid_value(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
381381
dummy_verified_contract.runtime_transformations = [
382-
{"reason": "callProtection", "type": "replace", "offset": 0}
382+
{"reason": "callProtection", "type": "replace", "offset": 1}
383383
]
384384
dummy_verified_contract.insert(
385385
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)
386386

387387
def test_missing_key_type_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
388388
dummy_verified_contract.runtime_transformations = [
389-
{"reason": "callProtection", "offset": 0}
389+
{"reason": "callProtection", "offset": 1}
390390
]
391391
check_constraint_fails(
392392
lambda: dummy_verified_contract.insert(
@@ -396,7 +396,7 @@ def test_missing_key_type_fails(self, connection, dummy_code, dummy_contract, du
396396
@pytest.mark.parametrize("value", [None, 0, [], dict()], ids=["null", "number", "array", "object"])
397397
def test_invalid_key_type_type_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
398398
dummy_verified_contract.runtime_transformations = [
399-
{"reason": "callProtection", "type": value, "offset": 0}
399+
{"reason": "callProtection", "type": value, "offset": 1}
400400
]
401401
check_constraint_fails(
402402
lambda: dummy_verified_contract.insert(
@@ -405,7 +405,7 @@ def test_invalid_key_type_type_fails(self, value, connection, dummy_code, dummy_
405405

406406
def test_invalid_key_type_value_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
407407
dummy_verified_contract.runtime_transformations = [
408-
{"reason": "callProtection", "type": "insert", "offset": 0}
408+
{"reason": "callProtection", "type": "insert", "offset": 1}
409409
]
410410
check_constraint_fails(
411411
lambda: dummy_verified_contract.insert(
@@ -431,11 +431,12 @@ def test_invalid_key_offset_type_fails(self, value, connection, dummy_code, dumm
431431
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
432432
"runtime_transformations_json_schema")
433433

434-
def test_invalid_key_offset_value_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment,
434+
@pytest.mark.parametrize("value", [-1, 0, 2], ids=["negative", "zero", "positive_not_1"])
435+
def test_invalid_key_offset_value_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment,
435436
dummy_compiled_contract, dummy_verified_contract):
436-
# The offset must be >= 0
437+
# The offset must always be equal to 1
437438
dummy_verified_contract.runtime_transformations = [
438-
{"reason": "callProtection", "type": "replace", "offset": -1}
439+
{"reason": "callProtection", "type": "replace", "offset": value}
439440
]
440441
check_constraint_fails(
441442
lambda: dummy_verified_contract.insert(

tests/test_constraint_runtime_values_json_schema.py

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def test_empty_object(self, connection, dummy_code, dummy_contract,
7070
dummy_verified_contract.insert(
7171
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)
7272

73-
@pytest.mark.parametrize("value", [None, [], ""], ids=["null", "array", "string"])
73+
@pytest.mark.parametrize("value", [None, 0, [], ""], ids=["null", "number", "array", "string"])
7474
def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract,
7575
dummy_contract_deployment, dummy_compiled_contract,
7676
dummy_verified_contract):
@@ -82,7 +82,17 @@ def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract,
8282
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
8383
"runtime_values_json_schema")
8484

85-
@pytest.mark.parametrize("value", [None, [], dict({})], ids=["null", "array", "object"])
85+
def test_empty_key_name_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment,
86+
dummy_compiled_contract, dummy_verified_contract):
87+
dummy_verified_contract.runtime_values = dict({
88+
"libraries": {"": "0x4000000000000000000000000000000000000000"}
89+
})
90+
check_constraint_fails(
91+
lambda: dummy_verified_contract.insert(
92+
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
93+
"runtime_values_json_schema")
94+
95+
@pytest.mark.parametrize("value", [None, 0, [], dict({})], ids=["null", "number", "array", "object"])
8696
def test_additional_properties_with_invalid_type_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment,
8797
dummy_compiled_contract, dummy_verified_contract):
8898
dummy_verified_contract.runtime_values = dict({
@@ -157,7 +167,7 @@ def test_empty_object(self, connection, dummy_code, dummy_contract,
157167
dummy_verified_contract.insert(
158168
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)
159169

160-
@pytest.mark.parametrize("value", [None, [], ""], ids=["null", "array", "string"])
170+
@pytest.mark.parametrize("value", [None, 0, [], ""], ids=["null", "number", "array", "string"])
161171
def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract,
162172
dummy_contract_deployment, dummy_compiled_contract,
163173
dummy_verified_contract):
@@ -169,7 +179,17 @@ def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract,
169179
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
170180
"runtime_values_json_schema")
171181

172-
@pytest.mark.parametrize("value", [None, [], dict({})], ids=["null", "array", "object"])
182+
def test_empty_key_name_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment,
183+
dummy_compiled_contract, dummy_verified_contract):
184+
dummy_verified_contract.runtime_values = dict({
185+
"immutables": {"": "0x6400000000000000000000000000000000000000000000000000000000000000"}
186+
})
187+
check_constraint_fails(
188+
lambda: dummy_verified_contract.insert(
189+
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
190+
"runtime_values_json_schema")
191+
192+
@pytest.mark.parametrize("value", [None, 0, [], dict({})], ids=["null", "number", "array", "object"])
173193
def test_additional_properties_with_invalid_type_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment,
174194
dummy_compiled_contract, dummy_verified_contract):
175195
dummy_verified_contract.runtime_values = dict({
@@ -244,7 +264,7 @@ def test_empty_object(self, connection, dummy_code, dummy_contract,
244264
dummy_verified_contract.insert(
245265
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)
246266

247-
@pytest.mark.parametrize("value", [None, [], ""], ids=["null", "array", "string"])
267+
@pytest.mark.parametrize("value", [None, 0, [], ""], ids=["null", "number", "array", "string"])
248268
def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract,
249269
dummy_contract_deployment, dummy_compiled_contract,
250270
dummy_verified_contract):
@@ -256,7 +276,17 @@ def test_invalid_type_fails(self, value, connection, dummy_code, dummy_contract,
256276
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
257277
"runtime_values_json_schema")
258278

259-
@pytest.mark.parametrize("value", [None, [], dict({})], ids=["null", "array", "object"])
279+
def test_empty_key_name_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment,
280+
dummy_compiled_contract, dummy_verified_contract):
281+
dummy_verified_contract.runtime_values = dict({
282+
"cborAuxdata": {"": "0x00000000000000000000000000000000000000000000000000000000000000000000000000"}
283+
})
284+
check_constraint_fails(
285+
lambda: dummy_verified_contract.insert(
286+
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
287+
"runtime_values_json_schema")
288+
289+
@pytest.mark.parametrize("value", [None, 0, [], dict({})], ids=["null", "number", "array", "object"])
260290
def test_additional_properties_with_invalid_type_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment,
261291
dummy_compiled_contract, dummy_verified_contract):
262292
dummy_verified_contract.runtime_values = dict({

0 commit comments

Comments
 (0)