-
Notifications
You must be signed in to change notification settings - Fork 175
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #143 from ralexstokes/migrate-purity-checker
Port `check_for_impurity.se` to Vyper LLL
- Loading branch information
Showing
4 changed files
with
397 additions
and
57 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
## This Python source code hosts the Vyper LLL used for the purity checker. | ||
## The purity checker scans a contract's bytecode to see if it uses any operations that rely on (external) mutable state. | ||
## This code was ported from an original written in the deprecated Serpent: https://github.com/ethereum/research/blob/master/impurity/check_for_impurity.se | ||
|
||
## The following are memory maps for each function: | ||
# MEMORY MAP for `submit` method | ||
# [320, 351]: addr, the input address, 32 bytes | ||
# [352, 352+_EXTCODESIZE-1]: bytecode at addr, _EXTCODESIZE bytes | ||
# [352+_EXTCODESIZE, 352+33*_EXTCODESIZE-32]: ops, array to hold processed opcodes, 32*_EXTCODESIZE bytes | ||
# [352+33*_EXTCODESIZE, 352+65*_EXTCODESIZE-32]: pushargs, array to hold processed push arguments, 32*_EXTCODESIZE bytes | ||
# [352+65*_EXTCODESIZE, 383+65*_EXTCODESIZE]: i, loop counter, 32 bytes | ||
|
||
# MEMORY MAP for `check` method | ||
# [320, 351]: addr, the input address, 32 bytes | ||
|
||
from vyper import compile_lll, optimizer | ||
from vyper.parser.parser import LLLnode | ||
|
||
invalid_if_banned = ["if", | ||
# sum([2**x for x in [0x31, 0x32, 0x33, 0x3a, 0x3b, 0x3c, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x54, 0x55, 0xf0, 0xff]]) | ||
["and", 57897811465722876096115075801844696845150819816717216876035649536196444422144, | ||
["exp", 2, "_c"]], | ||
"invalid"] | ||
|
||
is_push = ["and", ["le", 0x60, "_c"], ["le", "_c", 0x7f]] | ||
|
||
def index_pushargs(index): | ||
return ["add", ["add", 352, ["mul", 33, "_EXTCODESIZE"]], ["mul", 32, index]] | ||
|
||
handle_push = ["seq", | ||
["mstore", index_pushargs("_op"), ["div", ["mload", ["add", ["add", 352, ["mload", "_i"]], 1]], ["exp", 256, ["sub", 0x7f, "_c"]]]], | ||
["mstore", "_i", ["add", ["sub", "_c", 0x5f], ["mload", "_i"]]]] # there is an extra -1 in here to account for the increment of the repeat loop; -0x5e ~> -0x5f from the serpent code | ||
|
||
is_some_call = ["or", ["eq", "_c", 0xf1], | ||
["or", ["eq", "_c", 0xf2], ["eq", "_c", 0xf4]]] | ||
|
||
def index_ops(index): | ||
return ["add", ["add", 352, "_EXTCODESIZE"], ["mul", 32, index]] | ||
|
||
find_address = ["if", ["and", ["ge", "_op", 2], | ||
["and", ["ge", ["mload", index_ops(["sub", "_op", 1])], 0x60], | ||
["le", ["mload", index_ops(["sub", "_op", 1])], 0x7f]]], | ||
["set", "_address_entry", ["sub", "_op", 2]], | ||
["if", | ||
["and", ["ge", "_op", 4], | ||
["and", ["eq", ["mload", index_ops(["sub", "_op", 1])], 0x03], | ||
["and", ["eq", | ||
["mload", index_ops(["sub", "_op", 2])], 0x5a], | ||
["and", ["ge", | ||
["mload", index_ops(["sub", "_op", 3])], 0x60], | ||
["le", | ||
["mload", index_ops(["sub", "_op", 3])], 0x7f]]]]], | ||
["set", "_address_entry", ["sub", "_op", 4]], | ||
["if", ["and", ["ge", "_op", 2], | ||
["eq", | ||
["mload", index_ops(["sub", "_op", 1])], 0x5a]], | ||
["set", "_address_entry", ["sub", "_op", 2]], | ||
["if", ["and", ["ge", "_op", 2], | ||
["eq", | ||
["mload", index_ops(["sub", "_op", 1])], 0x90]], | ||
["set", "_address_entry", ["sub", "_op", 2]], | ||
["if", ["and", ["ge", "_op", 2], | ||
["and", ["ge", | ||
["mload", index_ops(["sub", "_op", 1])], 0x80], | ||
["lt", | ||
["mload", index_ops(["sub", "_op", 1])], 0x90]]], | ||
["set", "_address_entry", ["sub", "_op", 2]], | ||
"invalid"]]]]] | ||
|
||
filter_address_usage = ["if", ["sload", ["add", ["sha3_32", 0], # self.approved_addrs | ||
["mload", index_pushargs("_address_entry")]]], | ||
["seq"], | ||
["if", ["eq", | ||
["mload", index_ops("_address_entry")], 0x30], | ||
["seq"], | ||
["if", ["eq", | ||
["mload", index_ops("_address_entry")], 0x60], | ||
["seq"], | ||
"invalid"]]] | ||
|
||
handle_some_call = ["with", "_address_entry", 0, | ||
["seq", | ||
find_address, | ||
filter_address_usage]] | ||
|
||
dispatch_compound_sequences = ["if", is_push, | ||
handle_push, | ||
["if", is_some_call, | ||
handle_some_call]] | ||
|
||
process_byte = ["seq", | ||
invalid_if_banned, | ||
dispatch_compound_sequences, | ||
["mstore", ["add", ["add", 352, "_EXTCODESIZE"], ["mul", 32, "_op"]], "_c"], | ||
["set", "_op", ["add", "_op", 1]]] | ||
|
||
loop_body = ["if", | ||
["ge", ["mload", "_i"], "_EXTCODESIZE"], | ||
"break", | ||
["with", "_c", ["mod", ["mload", ["add", 352, ["sub", ["mload", "_i"], 31]]], 256], | ||
process_byte]] | ||
|
||
purity_checker_lll = LLLnode.from_list( | ||
["seq", | ||
["return", | ||
0, | ||
["lll", | ||
["seq", | ||
["mstore", 28, ["calldataload", 0]], | ||
["mstore", 32, 1461501637330902918203684832716283019655932542976], | ||
["mstore", 64, 170141183460469231731687303715884105727], | ||
["mstore", 96, -170141183460469231731687303715884105728], | ||
["mstore", 128, 1701411834604692317316873037158841057270000000000], | ||
["mstore", 160, -1701411834604692317316873037158841057280000000000], | ||
["if", | ||
["eq", ["mload", 0], 2710585003], # submit | ||
["seq", | ||
["calldatacopy", 320, 4, 32], | ||
["assert", ["iszero", "callvalue"]], | ||
["uclamplt", ["calldataload", 4], ["mload", 32]], # checking address input | ||
# scan bytecode at address input | ||
["with", "_EXTCODESIZE", ["extcodesize", ["mload", 320]], # addr | ||
["if", ["eq", "_EXTCODESIZE", 0], | ||
"invalid", # ban accounts with no code | ||
["seq", | ||
["extcodecopy", ["mload", 320], 352, 0, "_EXTCODESIZE"], | ||
["with", "_i", ["add", 352, ["mul", 65, "_EXTCODESIZE"]], | ||
["with", "_op", 0, | ||
["repeat", "_i", 0, | ||
115792089237316195423570985008687907853269984665640564039457584007913129639935, | ||
loop_body]]]]]], | ||
# approve the address `addr` | ||
["sstore", ["add", ["sha3_32", 0], ["mload", 320]], 1], | ||
["mstore", 0, 1], | ||
["return", 0, 32], | ||
"stop"]], | ||
["if", | ||
["eq", ["mload", 0], 3258357672], # check | ||
["seq", | ||
["calldatacopy", 320, 4, 32], | ||
["assert", ["iszero", "callvalue"]], | ||
["uclamplt", ["calldataload", 4], ["mload", 32]], # checking address input | ||
["mstore", 0, ["sload", ["add", ["sha3_32", 0], ["mload", 320]]]], | ||
["return", 0, 32], | ||
"stop"]]], | ||
0]]]) | ||
|
||
def lll_to_evm(lll): | ||
return compile_lll.assembly_to_evm(compile_lll.compile_to_assembly(optimizer.optimize(lll))) | ||
|
||
def purity_checker_data(): | ||
return lll_to_evm(purity_checker_lll) | ||
|
||
def purity_checker_data_hex(): | ||
return '0x' + purity_checker_data().hex() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.