Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
38 changes: 36 additions & 2 deletions database.sql
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,32 @@ END;
$$;


--
-- Name: validate_transformation_key_length(jsonb); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.validate_transformation_key_length(object jsonb) RETURNS boolean
LANGUAGE plpgsql
AS $$
BEGIN
RETURN object ? 'length' AND is_jsonb_number(object -> 'length') AND (object ->> 'length')::integer > 0;
END;
$$;

--
-- Name: validate_transformation_key_length_optional(jsonb); Type: FUNCTION; Schema: public; Owner: -
--

CREATE FUNCTION public.validate_transformation_key_length_optional(object jsonb) RETURNS boolean
LANGUAGE plpgsql
AS $$
BEGIN
RETURN NOT object ? 'length'
OR (is_jsonb_number(object -> 'length') AND (object ->> 'length')::integer > 0);
END;
$$;


--
-- Name: validate_transformation_key_type(jsonb, text); Type: FUNCTION; Schema: public; Owner: -
--
Expand Down Expand Up @@ -691,8 +717,16 @@ CREATE FUNCTION public.validate_transformations_cbor_auxdata(object jsonb) RETUR
LANGUAGE plpgsql
AS $$
BEGIN
RETURN validate_transformation_key_type(object, 'replace') AND validate_transformation_key_offset(object)
AND validate_transformation_key_id(object);
RETURN (
validate_transformation_key_type(object, 'replace')
AND validate_transformation_key_offset(object)
AND validate_transformation_key_length_optional(object)
AND validate_transformation_key_id(object)
) OR (
validate_transformation_key_type(object, 'delete')
AND validate_transformation_key_offset(object)
AND validate_transformation_key_length(object)
);
END;
$$;

Expand Down
22 changes: 19 additions & 3 deletions json-schemas/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Apart from the specifications in each section below, the following rules apply t

- All hexadecimal value strings must be prefixed with `0x` such as addresses, constructor arguments etc.
- `offset` values correspond to bytes in the bytecode and not string indexes. So `offset: 1` for the bytecode "0xab46fd" is the first byte in the bytecode corresponds to start from `46`
- `length` values (for delete transformations) are in number of bytes.

## Transformations

Expand Down Expand Up @@ -82,7 +83,8 @@ This object contains the transformation that will be applied to the creation byt
The creation transformation can only contain these as `"reason"`s and `"type"`s:

- `{ "reason": "constructorArguments", "type": "insert", "offset": 999 }`
- `{ "reason": "cborAuxdata", "type": "replace", "offset": 123, id: "0" }` Needs an `id` since there can be multiple auxdata transformations e.g. factories.
- `{ "reason": "cborAuxdata", "type": "replace", "offset": 123, id: "0" }` Needs an `id` since there can be multiple auxdata transformations e.g. factories. Can also include optional `"length"`, if included it must be used instead of the length of the auxdata in the transformations.
Comment thread
marcocastignoli marked this conversation as resolved.
- `{ "reason": "cborAuxdata", "type": "delete", "offset": 123, "length": 45 }` Can also have `"type": "delete"` which means the onchain bytecode does not have the cborAuxdata while the recompiled bytecode has it.
- `{ "reason": "library", "type": "replace", "offset": 123, id: "sources/lib/MyLib.sol:MyLib" }`

Example:
Expand All @@ -101,6 +103,12 @@ Example:
"offset": 1269,
"reason": "cborAuxdata"
},
{
"type": "delete",
"offset": 1295,
"length": 47,
"reason": "cborAuxdata"
},
{
"type": "insert",
"offset": 1322,
Expand Down Expand Up @@ -133,7 +141,8 @@ Similar to `creation_transformation`. But runtime code does not contain construc

The runtime transformations can only contain these as `"reason"`s and `"type"`s:

- `{ "reason": "cborAuxdata", "type": "replace", "offset": 123, id: "0" }` Needs an `id` since there can be multiple auxdata transformations e.g. factories.
- `{ "reason": "cborAuxdata", "type": "replace", "offset": 123, id: "0" }` Needs an `id` since there can be multiple auxdata transformations e.g. factories. Can also include optional `"length"`, if included it must be used instead of the length of the auxdata in the transformations.
- `{ "reason": "cborAuxdata", "type": "delete", "offset": 123, "length": 45 }` Can also have `"type": "delete"` which means the onchain bytecode does not have the cborAuxdata while the recompiled bytecode has it.
- `{ "reason": "library", "type": "replace", "offset": 123, id: "contracts/order/OrderUtils.sol:OrderUtilsLib" }`
- `{ "reason": "immutable", "type": "replace", "offset": 999, id: "2473" }` Needs an `id` for referencing multiple times and there can be multiple immutable transformations. Solidity contracts have `"replace"` type, while Vyper ones have `"insert"` because they are appended to the runtime bytecode.
- `{ "reason": "callProtection", "type": "replace", "offset": 1 }`
Expand Down Expand Up @@ -220,7 +229,14 @@ Compiler settings as passed to the compiler in JSON format
},
"outputSelection": {
"*": {
"*": ["evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi"]
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
},
"contracts/order/OrderUtils.sol": {
"OrderUtils": ["*"]
Expand Down
12 changes: 9 additions & 3 deletions json-schemas/verified_contracts-transformations.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,16 @@
"required": [
"type",
"reason",
"offset",
"id"
"offset"
],
"additionalProperties": false,
"properties": {
"type": {
"type": "string",
"value": "replace"
"enum": [
"replace",
"delete"
]
},
"reason": {
"type": "string",
Expand All @@ -54,6 +56,10 @@
"id": {
"type": "string",
"minLength": 1
},
"length": {
"type": "number",
"minimum": 1
}
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
-- migrate:up
CREATE OR REPLACE FUNCTION validate_transformation_key_length(object jsonb)
RETURNS boolean AS
$$
BEGIN
RETURN object ? 'length' AND is_jsonb_number(object -> 'length') AND (object ->> 'length')::integer > 0;
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION validate_transformation_key_length_optional(object jsonb)
RETURNS boolean AS
$$
BEGIN
RETURN NOT object ? 'length'
OR (is_jsonb_number(object -> 'length') AND (object ->> 'length')::integer > 0);
END;
$$ LANGUAGE plpgsql;

CREATE OR REPLACE FUNCTION validate_transformations_cbor_auxdata(object jsonb)
RETURNS boolean AS
$$
BEGIN
RETURN (
validate_transformation_key_type(object, 'replace')
AND validate_transformation_key_offset(object)
AND validate_transformation_key_length_optional(object)
AND validate_transformation_key_id(object)
Comment thread
manuelwedler marked this conversation as resolved.
) OR (
validate_transformation_key_type(object, 'delete')
AND validate_transformation_key_offset(object)
AND validate_transformation_key_length(object)
);
END;
$$ LANGUAGE plpgsql;

-- migrate:down
DROP FUNCTION IF EXISTS validate_transformation_key_length_optional(jsonb);
DROP FUNCTION IF EXISTS validate_transformation_key_length(jsonb);

CREATE OR REPLACE FUNCTION validate_transformations_cbor_auxdata(object jsonb)
RETURNS boolean AS
$$
BEGIN
RETURN validate_transformation_key_type(object, 'replace') AND validate_transformation_key_offset(object)
AND validate_transformation_key_id(object);
END;
$$ LANGUAGE plpgsql;
56 changes: 55 additions & 1 deletion tests/test_constraint_creation_transformations_json_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ def test_expected_type_values(self, connection, dummy_code, dummy_contract, dumm
{"reason": "constructorArguments", "type": "insert", "offset": 0},
{"reason": "library", "type": "replace",
"offset": 0, "id": "file1:lib1"},
{"reason": "cborAuxdata", "type": "replace", "offset": 0, "id": "0"}
{"reason": "cborAuxdata", "type": "replace", "offset": 0, "id": "0"},
{"reason": "cborAuxdata", "type": "delete", "offset": 2, "length": 4}
]
dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)
Expand Down Expand Up @@ -267,6 +268,20 @@ def test_valid_value(self, connection, dummy_code, dummy_contract, dummy_contrac
dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)

def test_valid_value_with_length(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.creation_transformations = [
{"reason": "cborAuxdata", "type": "replace", "offset": 6, "id": "0", "length": 12}
]
dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)

def test_valid_value_delete(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.creation_transformations = [
{"reason": "cborAuxdata", "type": "delete", "offset": 6, "length": 12}
]
dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)

def test_missing_key_type_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.creation_transformations = [
{"reason": "cborAuxdata", "offset": 0, "id": "0"}
Expand Down Expand Up @@ -325,6 +340,45 @@ def test_invalid_key_offset_value_fails(self, connection, dummy_code, dummy_cont
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
"creation_transformations_json_schema")

def test_missing_key_length_for_delete_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.creation_transformations = [
{"reason": "cborAuxdata", "type": "delete", "offset": 6}
]
check_constraint_fails(
lambda: dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
"creation_transformations_json_schema")

@pytest.mark.parametrize("value", [None, "", [], dict()], ids=["null", "string", "array", "object"])
def test_invalid_key_length_type_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.creation_transformations = [
{"reason": "cborAuxdata", "type": "replace", "offset": 6, "id": "0", "length": value}
]
check_constraint_fails(
lambda: dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
"creation_transformations_json_schema")

@pytest.mark.parametrize("value", [None, "", [], dict()], ids=["null", "string", "array", "object"])
def test_invalid_key_length_type_for_delete_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.creation_transformations = [
{"reason": "cborAuxdata", "type": "delete", "offset": 0, "length": value}
]
check_constraint_fails(
lambda: dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
"creation_transformations_json_schema")

@pytest.mark.parametrize("value", [0, -1], ids=["zero", "negative"])
def test_invalid_key_length_value_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.creation_transformations = [
{"reason": "cborAuxdata", "type": "delete", "offset": 6, "length": value}
]
check_constraint_fails(
lambda: dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
"creation_transformations_json_schema")

def test_missing_key_id_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.creation_transformations = [
{"reason": "cborAuxdata", "type": "replace", "offset": 0}
Expand Down
54 changes: 54 additions & 0 deletions tests/test_constraint_runtime_transformations_json_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def test_expected_type_values(self, connection, dummy_code, dummy_contract, dumm
"offset": 0, "id": "file1:lib1"},
{"reason": "immutable", "type": "replace", "offset": 0, "id": "0"},
{"reason": "cborAuxdata", "type": "replace", "offset": 0, "id": "0"},
{"reason": "cborAuxdata", "type": "delete", "offset": 1, "length": 2},
{"reason": "callProtection", "type": "replace", "offset": 0}
]
dummy_verified_contract.insert(
Expand Down Expand Up @@ -287,6 +288,20 @@ def test_valid_value(self, connection, dummy_code, dummy_contract, dummy_contrac
dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)

def test_valid_value_with_length(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.runtime_transformations = [
{"reason": "cborAuxdata", "type": "replace", "offset": 0, "id": "0", "length": 12}
]
dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)

def test_valid_value_delete(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.runtime_transformations = [
{"reason": "cborAuxdata", "type": "delete", "offset": 0, "length": 12}
]
dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id)

def test_missing_key_type_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.runtime_transformations = [
{"reason": "cborAuxdata", "offset": 0, "id": "0"}
Expand Down Expand Up @@ -345,6 +360,45 @@ def test_invalid_key_offset_value_fails(self, connection, dummy_code, dummy_cont
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
"runtime_transformations_json_schema")

def test_missing_key_length_for_delete_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.runtime_transformations = [
{"reason": "cborAuxdata", "type": "delete", "offset": 0}
]
check_constraint_fails(
lambda: dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
"runtime_transformations_json_schema")

@pytest.mark.parametrize("value", [None, "", [], dict()], ids=["null", "string", "array", "object"])
def test_invalid_key_length_type_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.runtime_transformations = [
{"reason": "cborAuxdata", "type": "replace", "offset": 0, "id": "0", "length": value}
]
check_constraint_fails(
lambda: dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
"runtime_transformations_json_schema")

@pytest.mark.parametrize("value", [None, "", [], dict()], ids=["null", "string", "array", "object"])
def test_invalid_key_length_type_for_delete_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.runtime_transformations = [
{"reason": "cborAuxdata", "type": "delete", "offset": 0, "length": value}
]
check_constraint_fails(
lambda: dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
"runtime_transformations_json_schema")

@pytest.mark.parametrize("value", [0, -1], ids=["zero", "negative"])
def test_invalid_key_length_value_fails(self, value, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.runtime_transformations = [
{"reason": "cborAuxdata", "type": "delete", "offset": 0, "length": value}
]
check_constraint_fails(
lambda: dummy_verified_contract.insert(
connection, dummy_contract_deployment.id, dummy_compiled_contract.id),
"runtime_transformations_json_schema")

def test_missing_key_id_fails(self, connection, dummy_code, dummy_contract, dummy_contract_deployment, dummy_compiled_contract, dummy_verified_contract):
dummy_verified_contract.runtime_transformations = [
{"reason": "cborAuxdata", "type": "replace", "offset": 0}
Expand Down