-
Notifications
You must be signed in to change notification settings - Fork 68
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
add support for prank(sender, origin) and startPrank(sender, origin) cheatcodes #336
Changes from 5 commits
f1e803b
022e2df
c1d5f9e
15ffcdb
73f4478
d62c1eb
1c3373b
9939ca5
3ec4901
324f77d
b6c1e48
c051be4
8bb0d6f
707b9e1
656c359
de64d95
09b6438
1689032
09b8066
df2c7a3
1ea690c
c1c64e6
17eba86
24f1493
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -630,6 +630,7 @@ class Exec: # an execution path | |||||||||||||||||||||||||||
jumpis: Dict[str, Dict[bool, int]] # for loop detection | ||||||||||||||||||||||||||||
symbolic: bool # symbolic or concrete storage | ||||||||||||||||||||||||||||
prank: Prank | ||||||||||||||||||||||||||||
origin: Address | ||||||||||||||||||||||||||||
karmacoma-eth marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||
addresses_to_delete: Set[Address] | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
# path | ||||||||||||||||||||||||||||
|
@@ -662,6 +663,7 @@ def __init__(self, **kwargs) -> None: | |||||||||||||||||||||||||||
self.jumpis = kwargs["jumpis"] | ||||||||||||||||||||||||||||
self.symbolic = kwargs["symbolic"] | ||||||||||||||||||||||||||||
self.prank = kwargs["prank"] | ||||||||||||||||||||||||||||
self.origin = kwargs["origin"] | ||||||||||||||||||||||||||||
self.addresses_to_delete = kwargs.get("addresses_to_delete") or set() | ||||||||||||||||||||||||||||
# | ||||||||||||||||||||||||||||
self.path = kwargs["path"] | ||||||||||||||||||||||||||||
|
@@ -724,6 +726,13 @@ def current_opcode(self) -> UnionType[int, BitVecRef]: | |||||||||||||||||||||||||||
def current_instruction(self) -> Instruction: | ||||||||||||||||||||||||||||
return self.pgm.decode_instruction(self.pc) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
def resolve_prank(self, to: Address) -> Tuple[Address, Address]: | ||||||||||||||||||||||||||||
# this potentially "consumes" the active prank | ||||||||||||||||||||||||||||
prank_result = self.prank.lookup(to) | ||||||||||||||||||||||||||||
caller = self.this if prank_result.sender is None else prank_result.sender | ||||||||||||||||||||||||||||
origin = f_origin() if prank_result.origin is None else prank_result.origin | ||||||||||||||||||||||||||||
karmacoma-eth marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||
return caller, origin | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
def set_code(self, who: Address, code: UnionType[ByteVec, Contract]) -> None: | ||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||
Sets the code at a given address. | ||||||||||||||||||||||||||||
|
@@ -1558,14 +1567,14 @@ def call( | |||||||||||||||||||||||||||
if not ret_size >= 0: | ||||||||||||||||||||||||||||
raise ValueError(ret_size) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
caller = ex.prank.lookup(ex.this, to) | ||||||||||||||||||||||||||||
pranked_caller, pranked_origin = ex.resolve_prank(to) | ||||||||||||||||||||||||||||
arg = ex.st.memory.slice(arg_loc, arg_loc + arg_size) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
def send_callvalue(condition=None) -> None: | ||||||||||||||||||||||||||||
# no balance update for CALLCODE which transfers to itself | ||||||||||||||||||||||||||||
if op == EVM.CALL: | ||||||||||||||||||||||||||||
# TODO: revert if context is static | ||||||||||||||||||||||||||||
self.transfer_value(ex, caller, to, fund, condition) | ||||||||||||||||||||||||||||
self.transfer_value(ex, pranked_caller, to, fund, condition) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
def call_known(to: Address) -> None: | ||||||||||||||||||||||||||||
# backup current state | ||||||||||||||||||||||||||||
|
@@ -1578,7 +1587,7 @@ def call_known(to: Address) -> None: | |||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
message = Message( | ||||||||||||||||||||||||||||
target=to if op in [EVM.CALL, EVM.STATICCALL] else ex.this, | ||||||||||||||||||||||||||||
caller=caller if op != EVM.DELEGATECALL else ex.caller(), | ||||||||||||||||||||||||||||
caller=pranked_caller if op != EVM.DELEGATECALL else ex.caller(), | ||||||||||||||||||||||||||||
value=fund if op != EVM.DELEGATECALL else ex.callvalue(), | ||||||||||||||||||||||||||||
data=arg, | ||||||||||||||||||||||||||||
is_static=(ex.context.message.is_static or op == EVM.STATICCALL), | ||||||||||||||||||||||||||||
|
@@ -1610,6 +1619,7 @@ def callback(new_ex: Exec, stack, step_id): | |||||||||||||||||||||||||||
new_ex.jumpis = deepcopy(ex.jumpis) | ||||||||||||||||||||||||||||
new_ex.symbolic = ex.symbolic | ||||||||||||||||||||||||||||
new_ex.prank = deepcopy(ex.prank) | ||||||||||||||||||||||||||||
new_ex.origin = ex.origin | ||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice to restore it here, but don't forget it in other places that restore vm states:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably we need more tests to cover these cases:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. tests added in 1ea690c |
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
# set return data (in memory) | ||||||||||||||||||||||||||||
effective_ret_size = min(ret_size, new_ex.returndatasize()) | ||||||||||||||||||||||||||||
|
@@ -1652,6 +1662,7 @@ def callback(new_ex: Exec, stack, step_id): | |||||||||||||||||||||||||||
jumpis={}, | ||||||||||||||||||||||||||||
symbolic=ex.symbolic, | ||||||||||||||||||||||||||||
prank=Prank(), | ||||||||||||||||||||||||||||
origin=pranked_origin, | ||||||||||||||||||||||||||||
# | ||||||||||||||||||||||||||||
path=ex.path, | ||||||||||||||||||||||||||||
alias=ex.alias, | ||||||||||||||||||||||||||||
|
@@ -1775,7 +1786,7 @@ def call_unknown() -> None: | |||||||||||||||||||||||||||
CallContext( | ||||||||||||||||||||||||||||
message=Message( | ||||||||||||||||||||||||||||
target=to, | ||||||||||||||||||||||||||||
caller=caller, | ||||||||||||||||||||||||||||
caller=pranked_caller, | ||||||||||||||||||||||||||||
value=fund, | ||||||||||||||||||||||||||||
data=ex.st.memory.slice(arg_loc, arg_loc + arg_size), | ||||||||||||||||||||||||||||
call_scheme=op, | ||||||||||||||||||||||||||||
|
@@ -1847,8 +1858,8 @@ def create( | |||||||||||||||||||||||||||
if op == EVM.CREATE2: | ||||||||||||||||||||||||||||
salt = ex.st.pop() | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
# lookup prank | ||||||||||||||||||||||||||||
caller = ex.prank.lookup(ex.this, con_addr(0)) | ||||||||||||||||||||||||||||
# check if there is an active prank | ||||||||||||||||||||||||||||
pranked_caller, pranked_origin = ex.resolve_prank(address(ex.this)) | ||||||||||||||||||||||||||||
karmacoma-eth marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
# contract creation code | ||||||||||||||||||||||||||||
create_hexcode = ex.st.memory.slice(loc, loc + size) | ||||||||||||||||||||||||||||
|
@@ -1867,14 +1878,16 @@ def create( | |||||||||||||||||||||||||||
create_hexcode = bytes_to_bv_value(create_hexcode) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
code_hash = ex.sha3_data(create_hexcode) | ||||||||||||||||||||||||||||
hash_data = simplify(Concat(con(0xFF, 8), uint160(caller), salt, code_hash)) | ||||||||||||||||||||||||||||
hash_data = simplify( | ||||||||||||||||||||||||||||
Concat(con(0xFF, 8), uint160(pranked_caller), salt, code_hash) | ||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||
new_addr = uint160(ex.sha3_data(hash_data)) | ||||||||||||||||||||||||||||
else: | ||||||||||||||||||||||||||||
raise HalmosException(f"Unknown CREATE opcode: {op}") | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
message = Message( | ||||||||||||||||||||||||||||
target=new_addr, | ||||||||||||||||||||||||||||
caller=caller, | ||||||||||||||||||||||||||||
caller=pranked_caller, | ||||||||||||||||||||||||||||
value=value, | ||||||||||||||||||||||||||||
data=create_hexcode, | ||||||||||||||||||||||||||||
is_static=False, | ||||||||||||||||||||||||||||
|
@@ -1908,7 +1921,7 @@ def create( | |||||||||||||||||||||||||||
ex.storage[new_addr] = {} # existing storage may not be empty and reset here | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
# transfer value | ||||||||||||||||||||||||||||
self.transfer_value(ex, caller, new_addr, value) | ||||||||||||||||||||||||||||
self.transfer_value(ex, pranked_caller, new_addr, value) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
def callback(new_ex, stack, step_id): | ||||||||||||||||||||||||||||
subcall = new_ex.context | ||||||||||||||||||||||||||||
|
@@ -1972,6 +1985,7 @@ def callback(new_ex, stack, step_id): | |||||||||||||||||||||||||||
jumpis={}, | ||||||||||||||||||||||||||||
symbolic=False, | ||||||||||||||||||||||||||||
prank=Prank(), | ||||||||||||||||||||||||||||
origin=pranked_origin, | ||||||||||||||||||||||||||||
# | ||||||||||||||||||||||||||||
path=ex.path, | ||||||||||||||||||||||||||||
alias=ex.alias, | ||||||||||||||||||||||||||||
|
@@ -2092,6 +2106,7 @@ def create_branch(self, ex: Exec, cond: BitVecRef, target: int) -> Exec: | |||||||||||||||||||||||||||
jumpis=deepcopy(ex.jumpis), | ||||||||||||||||||||||||||||
symbolic=ex.symbolic, | ||||||||||||||||||||||||||||
prank=deepcopy(ex.prank), | ||||||||||||||||||||||||||||
origin=ex.origin, | ||||||||||||||||||||||||||||
# | ||||||||||||||||||||||||||||
path=new_path, | ||||||||||||||||||||||||||||
alias=ex.alias.copy(), | ||||||||||||||||||||||||||||
|
@@ -2291,7 +2306,7 @@ def finalize(ex: Exec): | |||||||||||||||||||||||||||
ex.st.push(uint256(ex.caller())) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
elif opcode == EVM.ORIGIN: | ||||||||||||||||||||||||||||
ex.st.push(uint256(f_origin())) | ||||||||||||||||||||||||||||
ex.st.push(uint256(ex.origin)) | ||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||
elif opcode == EVM.ADDRESS: | ||||||||||||||||||||||||||||
ex.st.push(uint256(ex.this)) | ||||||||||||||||||||||||||||
|
@@ -2626,6 +2641,7 @@ def mk_exec( | |||||||||||||||||||||||||||
jumpis={}, | ||||||||||||||||||||||||||||
symbolic=symbolic, | ||||||||||||||||||||||||||||
prank=Prank(), | ||||||||||||||||||||||||||||
origin=f_origin(), | ||||||||||||||||||||||||||||
# | ||||||||||||||||||||||||||||
path=path, | ||||||||||||||||||||||||||||
alias={}, | ||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is accurate but a bit convoluted. For better code readability, I'd suggest having
prank()
take an additional argument (defaulting toFalse
if not provided) that is assigned toself.keep
. Then, havestartPrank()
callprank(..., keep=True)
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah you're right, in 17eba86