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

[WIP] Add manual symbolic calldata support #223

Closed
wants to merge 74 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
93fd80f
Add `symbolicCallData`, `assumeBytesLength` cheats
palinatolmach Dec 1, 2023
f8fa53f
Set Version: 0.1.78
Dec 1, 2023
14db31f
WIP: use dynamic array in ABI calldata for `bytes`
palinatolmach Dec 1, 2023
56ce5e8
Merge branch 'master' into symbolic-calldata
PetarMax Dec 5, 2023
dba45bf
Set Version: 0.1.83
Dec 5, 2023
6c81fea
WIP: use `abi_symbolic_calldata` in calldata cell
palinatolmach Dec 6, 2023
bd58294
Set Version: 0.1.84
Dec 6, 2023
9825ae7
Experimental: `bytes` assumptions
palinatolmach Dec 6, 2023
c6e43fe
Merge branch 'master' into symbolic-calldata
palinatolmach Dec 6, 2023
ae3740e
Set Version: 0.1.84
Dec 6, 2023
2ddb50f
Merge branch 'master' into symbolic-calldata
palinatolmach Dec 7, 2023
c4b37d9
Set Version: 0.1.86
Dec 7, 2023
a425c9a
Fix in escaping in dynamic array names
palinatolmach Dec 7, 2023
bfc32b8
WIP: make calldata symbolic iff it has dyn types
palinatolmach Dec 8, 2023
07eb6b7
Merge branch 'master' into symbolic-calldata
palinatolmach Dec 8, 2023
91fecd3
Set Version: 0.1.88
Dec 8, 2023
6a5785b
Merge branch 'master' into symbolic-calldata
palinatolmach Dec 8, 2023
61e5cad
Set Version: 0.1.89
Dec 8, 2023
c8d09b8
Make CD symbolic if any input has dynamic types
palinatolmach Dec 8, 2023
1c13c20
Handle dynamic arrays of types other than `bytes`
palinatolmach Dec 10, 2023
1441750
Merge branch 'master' into symbolic-calldata
palinatolmach Dec 10, 2023
35a89fa
Set Version: 0.1.91
Dec 10, 2023
765e815
WIP: temp poetry change, manual assumptions on CD
palinatolmach Dec 11, 2023
8531aa3
Set Version: 0.1.92
Dec 11, 2023
fe7d23d
Merge branch 'master' into symbolic-calldata
palinatolmach Dec 11, 2023
ad30492
Format assumptions on CD
palinatolmach Dec 11, 2023
b8c68fe
add suport for kevm nogas
anvacaru Dec 12, 2023
0ba7b82
hardcode use-gas for profiling
anvacaru Dec 12, 2023
edae5e6
test
anvacaru Dec 12, 2023
a590d05
WIP: hardcode constraints for symbolic CD test
palinatolmach Dec 14, 2023
a82ac3e
WIP: hardcode the first offset to `224`
palinatolmach Dec 14, 2023
5fc1f89
renaming use-gas related variables
PetarMax Dec 15, 2023
377ba45
Set Version: 0.1.100
Dec 15, 2023
dc486c2
merge with master
PetarMax Dec 17, 2023
40d31dd
Set Version: 0.1.101
Dec 17, 2023
b10c434
Merge branch 'master' into symbolic-calldata
palinatolmach Dec 18, 2023
7242b4a
Set Version: 0.1.101
Dec 18, 2023
942cf05
WIP: hardcode calldata length constraints
palinatolmach Dec 19, 2023
89b6518
set kevm version tag
anvacaru Dec 20, 2023
9210417
remove space
anvacaru Dec 20, 2023
4214f09
set use_gas to false by default
anvacaru Dec 20, 2023
06f1f79
Revert "hardcode use-gas for profiling"
anvacaru Dec 20, 2023
ec3fc71
WIP: hardcode range constraints for test arguments
palinatolmach Dec 21, 2023
77816c0
Set Version: 0.1.102
Dec 21, 2023
3114a8e
REVERT: use `symbolic-calldata` branch in KEVM
palinatolmach Dec 21, 2023
d88bb39
Merge branch 'master' into symbolic-calldata
palinatolmach Dec 21, 2023
c4ab4bf
Set Version: 0.1.102
Dec 21, 2023
9f32027
Merge branch 'master' into kevm-no-gas
anvacaru Dec 21, 2023
3757229
Set Version: 0.1.103
Dec 21, 2023
92909c1
remove duplicated substitution
anvacaru Dec 21, 2023
9712d1b
update expected output
anvacaru Dec 21, 2023
23ca4c3
use gas for GasTests
anvacaru Dec 22, 2023
7d93549
Merge branch 'kevm-no-gas' into symbolic-calldata
palinatolmach Dec 22, 2023
420ac95
Move manual assumption building to functions, fmt
palinatolmach Dec 22, 2023
cfd42ac
Add assumptions for symbolic `bytes data` length
palinatolmach Dec 22, 2023
de1484f
Use assumptions for structured calldata by default
palinatolmach Dec 22, 2023
8c6f880
Update dependency: deps/kevm_release (#256)
rv-jenkins Dec 22, 2023
ddd45ea
Update dependency: deps/kevm_release (#256)
rv-jenkins Dec 22, 2023
5020a34
merge with master
PetarMax Dec 27, 2023
20adf54
Set Version: 0.1.104
Dec 27, 2023
c377393
adding constraint on bytes length
PetarMax Dec 30, 2023
bcd315e
setting length to 1Gb
PetarMax Dec 30, 2023
81c7036
Update `poetry.lock`
palinatolmach Jan 10, 2024
e6752db
adding definedness preservation
PetarMax Jan 14, 2024
08dceed
preserving definedness
PetarMax Jan 14, 2024
fe2ee25
Merge branch 'master' into symbolic-calldata
palinatolmach Jan 15, 2024
64f7a64
Set Version: 0.1.112
Jan 15, 2024
8066e5e
merge with master
PetarMax Jan 16, 2024
703c1f5
Set Version: 0.1.118
Jan 16, 2024
9d4e395
Merge branch 'master' into symbolic-calldata
palinatolmach Jan 17, 2024
0266425
Set Version: 0.1.119
Jan 17, 2024
2d7838d
Merge branch 'master' into symbolic-calldata
palinatolmach Jan 20, 2024
f3710be
Set Version: 0.1.123
Jan 20, 2024
7ce1022
Minor cleanup: removed unused assumptions, cheatcodes
palinatolmach Jan 20, 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
2 changes: 1 addition & 1 deletion package/version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.1.122
0.1.123
10 changes: 5 additions & 5 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@ build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "kontrol"
version = "0.1.122"
version = "0.1.123"
description = "Foundry integration for KEVM"
authors = [
"Runtime Verification, Inc. <[email protected]>",
]

[tool.poetry.dependencies]
python = "^3.10"
kevm-pyk = { git = "https://github.com/runtimeverification/evm-semantics.git", tag = "v1.0.422", subdirectory = "kevm-pyk" }
kevm-pyk = { git = "https://github.com/runtimeverification/evm-semantics.git", branch = "symbolic-calldata", subdirectory = "kevm-pyk" }

[tool.poetry.group.dev.dependencies]
autoflake = "*"
Expand Down
2 changes: 1 addition & 1 deletion src/kontrol/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@
if TYPE_CHECKING:
from typing import Final

VERSION: Final = '0.1.122'
VERSION: Final = '0.1.123'
4 changes: 4 additions & 0 deletions src/kontrol/kdist/cheatcodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ The `<output>` cell will be updated with the value of the address generated from
<k> #call_foundry SELECTOR ARGS => . ... </k>
<output> _ => #bufStrict(32, #addrFromPrivateKey(#unparseDataBytes(ARGS))) </output>
requires SELECTOR ==Int selector ( "addr(uint256)" )
[preserves-definedness]
```

#### `load` - Loads a storage slot from an address.
Expand Down Expand Up @@ -896,6 +897,7 @@ The `ECDSASign` function returns the signed data in [r,s,v] form, which we conve
<k> #call_foundry SELECTOR ARGS => . ... </k>
<output> _ => #sign(#range(ARGS, 32, 32),#range(ARGS,0,32)) </output>
requires SELECTOR ==Int selector ( "sign(uint256,bytes32)" )
[preserves-definedness]
```

Otherwise, throw an error for any other call to the Foundry contract.
Expand Down Expand Up @@ -943,6 +945,7 @@ Utils
<code> _ => #if #asWord(CODE) ==Int 0 #then .Bytes #else CODE #fi </code>
...
</account>
[preserves-definedness]
```

- `#returnNonce ACCTID` takes the nonce of a given account and places it on the `<output>` cell.
Expand Down Expand Up @@ -997,6 +1000,7 @@ Utils
<storage> STORAGE => STORAGE [ LOC <- VALUE ] </storage>
...
</account>
[preserves-definedness]
```

`#setSymbolicStorage ACCTID` takes a given account and makes its storage fully symbolic.
Expand Down
64 changes: 62 additions & 2 deletions src/kontrol/prove.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from pyk.prelude.collections import map_empty, map_of
from pyk.prelude.k import GENERATED_TOP_CELL
from pyk.prelude.kbool import FALSE, TRUE, notBool
from pyk.prelude.kint import intToken
from pyk.prelude.kint import eqInt, intToken, leInt
from pyk.prelude.ml import mlEqualsTrue
from pyk.prelude.string import stringToken
from pyk.proof.proof import Proof
Expand Down Expand Up @@ -368,7 +368,7 @@ def _method_to_cfg(
use_init_code = True

elif isinstance(method, Contract.Method):
calldata = method.calldata_cell(contract)
calldata, is_calldata_symbolic = method.calldata_cell(contract)
callvalue = method.callvalue_cell
program = KEVM.bin_runtime(KApply(f'contract_{contract.name_with_path}'))
use_init_code = False
Expand All @@ -378,6 +378,7 @@ def _method_to_cfg(
contract.name_with_path,
program=program,
calldata=calldata,
is_calldata_symbolic=is_calldata_symbolic,
callvalue=callvalue,
use_gas=use_gas,
summary_entries=summary_entries,
Expand Down Expand Up @@ -468,6 +469,10 @@ def _update_cterm_from_node(cterm: CTerm, node: KCFG.Node, contract_name: str) -
new_init_cterm = CTerm(set_cell(new_init_cterm.config, 'GAS_CELL', gas_cell), [])
new_init_cterm = CTerm(set_cell(new_init_cterm.config, 'CALLGAS_CELL', callgas_cell), [])

# adding constraints from the initial cterm
for constraint in cterm.constraints:
new_init_cterm = new_init_cterm.add_constraint(constraint)

return new_init_cterm


Expand Down Expand Up @@ -528,6 +533,7 @@ def _init_cterm(
*,
setup_cterm: CTerm | None = None,
calldata: KInner | None = None,
is_calldata_symbolic: bool = False,
callvalue: KInner | None = None,
summary_entries: Iterable[SummaryEntry] | None = None,
) -> CTerm:
Expand Down Expand Up @@ -594,6 +600,14 @@ def _init_cterm(

if calldata is not None:
init_subst['CALLDATA_CELL'] = calldata
constraints = []

if is_calldata_symbolic:
# TODO(palina): EXPERIMENTAL: if calldata is symbolic,
# add assumptions that correspond to the test w/`BYTES_DATA` having symbolic length, and `bytes[]` containing
# 10 elements, each 600 bytes long
structured_calldata_symbolic_data_constraints = structured_calldata_symbolic_data_assumptions()
constraints.extend(structured_calldata_symbolic_data_constraints)

if callvalue is not None:
init_subst['CALLVALUE_CELL'] = callvalue
Expand All @@ -614,6 +628,52 @@ def _init_cterm(
return init_cterm


def structured_calldata_symbolic_data_assumptions() -> list[KApply]:
constraints = []

target_constraint = mlEqualsTrue(KEVM.range_address(KVariable('target', 'Int')))
sender_constraint = mlEqualsTrue(KEVM.range_address(KVariable('sender', 'Int')))
l2_output_constraint = mlEqualsTrue(KEVM.range_uint('256', KVariable('l2OutputIndex', 'Int')))
constraints.append(l2_output_constraint)

constraints.append(target_constraint)
constraints.append(sender_constraint)

length_bytes_data_equality = mlEqualsTrue(
eqInt(KEVM.size_bytes(KVariable('BYTES_DATA', 'Bytes')), KVariable('BYTES_SIZE', 'Int'))
)

# Size of the symbolic bytes is limited to 1Gb
length_bytes_data_constraint = mlEqualsTrue(leInt(KVariable('BYTES_SIZE', 'Int'), intToken("1073741824")))

constraints.append(length_bytes_data_equality)
constraints.append(length_bytes_data_constraint)

length_bytes_arr_constraint_1 = mlEqualsTrue(eqInt(KEVM.size_bytes(KVariable('BYTES_1', 'Bytes')), intToken("600")))
constraints.append(length_bytes_arr_constraint_1)
length_bytes_arr_constraint_2 = mlEqualsTrue(eqInt(KEVM.size_bytes(KVariable('BYTES_2', 'Bytes')), intToken("600")))
constraints.append(length_bytes_arr_constraint_2)
length_bytes_arr_constraint_3 = mlEqualsTrue(eqInt(KEVM.size_bytes(KVariable('BYTES_3', 'Bytes')), intToken("600")))
constraints.append(length_bytes_arr_constraint_3)
length_bytes_arr_constraint_4 = mlEqualsTrue(eqInt(KEVM.size_bytes(KVariable('BYTES_4', 'Bytes')), intToken("600")))
constraints.append(length_bytes_arr_constraint_4)
length_bytes_arr_constraint_5 = mlEqualsTrue(eqInt(KEVM.size_bytes(KVariable('BYTES_5', 'Bytes')), intToken("600")))
constraints.append(length_bytes_arr_constraint_5)
length_bytes_arr_constraint_6 = mlEqualsTrue(eqInt(KEVM.size_bytes(KVariable('BYTES_6', 'Bytes')), intToken("600")))
constraints.append(length_bytes_arr_constraint_6)
length_bytes_arr_constraint_7 = mlEqualsTrue(eqInt(KEVM.size_bytes(KVariable('BYTES_7', 'Bytes')), intToken("600")))
constraints.append(length_bytes_arr_constraint_7)
length_bytes_arr_constraint_8 = mlEqualsTrue(eqInt(KEVM.size_bytes(KVariable('BYTES_8', 'Bytes')), intToken("600")))
constraints.append(length_bytes_arr_constraint_8)
length_bytes_arr_constraint_9 = mlEqualsTrue(eqInt(KEVM.size_bytes(KVariable('BYTES_9', 'Bytes')), intToken("600")))
constraints.append(length_bytes_arr_constraint_9)
length_bytes_arr_constraint_10 = mlEqualsTrue(
eqInt(KEVM.size_bytes(KVariable('BYTES_10', 'Bytes')), intToken("600"))
)
constraints.append(length_bytes_arr_constraint_10)
return constraints


def _final_cterm(
empty_config: KInner, contract_name: str, *, failing: bool, is_test: bool = True, use_init_code: bool = False
) -> CTerm:
Expand Down
63 changes: 46 additions & 17 deletions src/kontrol/solc_to_k.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ class Input:
components: tuple[Input, ...] = ()
idx: int = 0

@cached_property
def arg_name(self) -> str:
return f'V{self.idx}_{self.name.replace("-", "_")}'

@staticmethod
def from_dict(input: dict, idx: int = 0) -> Input:
name = input['name']
Expand All @@ -82,30 +86,39 @@ def from_dict(input: dict, idx: int = 0) -> Input:
return Input(name, type, idx=idx)

@staticmethod
def arg_name(input: Input) -> str:
return f'V{input.idx}_{input.name.replace("-", "_")}'

@staticmethod
def _make_single_type(input: Input) -> KApply:
input_name = Input.arg_name(input)
input_type = input.type
return KEVM.abi_type(input_type, KVariable(input_name))
def _make_single_type(input: Input) -> (KApply, bool):
if input.type == 'bytes':
# getting the type
element_type = KEVM.abi_type(input.type, KVariable(input.arg_name))
return (KEVM.abi_dynamic_bytes_array(element_type), True)
elif input.type.endswith('[]'):
element_type = KEVM.abi_type(input.type[0 : input.type.index('[')], KVariable(input.arg_name))
if element_type == 'bytes':
return (KEVM.abi_dynamic_bytes_array(element_type), True)
else:
return (KEVM.abi_dynamic_array(element_type), True)
else:
input_name = input.arg_name
input_type = input.type
return (KEVM.abi_type(input_type, KVariable(input_name)), False)

@staticmethod
def _make_complex_type(components: Iterable[Input]) -> KApply:
def _make_complex_type(components: Iterable[Input]) -> (KApply, bool):
# TODO(palina): add support for static arrays
"""
recursively unwrap components in arguments of complex types and convert them to KEVM types
"""
has_dynamic_type = False
abi_types: list[KInner] = []
for comp in components:
# nested tuple, unwrap its components
if comp.type == 'tuple':
tuple = Input._make_complex_type(comp.components)
tuple, has_dynamic_type = Input._make_complex_type(comp.components)
abi_type = tuple
else:
abi_type = Input._make_single_type(comp)
abi_type, has_dynamic_type = Input._make_single_type(comp)
abi_types.append(abi_type)
return KEVM.abi_tuple(abi_types)
return (KEVM.abi_tuple(abi_types), has_dynamic_type)

@staticmethod
def _unwrap_components(components: dict, i: int = 0) -> list[Input]:
Expand All @@ -124,7 +137,7 @@ def _unwrap_components(components: dict, i: int = 0) -> list[Input]:
i += 1
return comps

def to_abi(self) -> KApply:
def to_abi(self) -> (KApply, bool):
if self.type == 'tuple':
return Input._make_complex_type(self.components)
else:
Expand Down Expand Up @@ -282,7 +295,7 @@ def flat_inputs(self) -> tuple[Input, ...]:

@cached_property
def arg_names(self) -> Iterable[str]:
return tuple(Input.arg_name(input) for input in self.flat_inputs)
return tuple(input.arg_name for input in self.flat_inputs)

@cached_property
def arg_types(self) -> Iterable[str]:
Expand Down Expand Up @@ -351,7 +364,7 @@ def rule(self, contract: KInner, application_label: KLabel, contract_name: str)
args: list[KInner] = []
conjuncts: list[KInner] = []
for input in self.inputs:
abi_type = input.to_abi()
abi_type, _ = input.to_abi()
args.append(abi_type)
rps = _range_predicates(abi_type)
for rp in rps:
Expand All @@ -374,8 +387,20 @@ def callvalue_cell(self) -> KInner:
else abstract_term_safely(KVariable('_###CALLVALUE###_'), base_name='CALLVALUE')
)

def calldata_cell(self, contract: Contract) -> KInner:
return KApply(contract.klabel_method, [KApply(contract.klabel), self.application])
def calldata_cell(self, contract: Contract) -> (KInner, bool):
has_dynamic_types = False
args: list[KInner] = []

for input in self.inputs:
abi_type, input_has_dynamic_types = input.to_abi()
# if any of the inputs contain dynamic types, make it symbolic
has_dynamic_types = has_dynamic_types or input_has_dynamic_types
args.append(abi_type)

if has_dynamic_types:
return (KEVM.abi_symbolic_calldata(self.name, args), True)
else:
return (KApply(contract.klabel_method, [KApply(contract.klabel), self.application]), False)

@cached_property
def application(self) -> KInner:
Expand Down Expand Up @@ -827,6 +852,10 @@ def _range_predicates(abi: KApply) -> list[KInner | None]:
if abi.label.name == 'abi_type_tuple':
if type(abi.args[0]) is KApply:
rp += _range_collection_predicates(abi.args[0])
elif abi.label.name == 'abi_dynamic_array':
# TODO(palina): support other arrays
type_label = "bytes"
rp.append(_range_predicate(single(abi.args), type_label))
else:
type_label = abi.label.name.removeprefix('abi_type_')
rp.append(_range_predicate(single(abi.args), type_label))
Expand Down
Loading
Loading