Skip to content

Commit

Permalink
Merge pull request #143 from ralexstokes/migrate-purity-checker
Browse files Browse the repository at this point in the history
Port `check_for_impurity.se` to Vyper LLL
  • Loading branch information
djrtwo committed Jun 14, 2018
2 parents 6feca0b + 19930fd commit 89dd98f
Show file tree
Hide file tree
Showing 4 changed files with 397 additions and 57 deletions.
155 changes: 155 additions & 0 deletions casper/contracts/purity_checker.py
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()
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@
VYPER_RLP_DECODER_TX_SENDER = "0x39ba083c30fCe59883775Fc729bBE1f9dE4DEe11"
MSG_HASHER_TX_HEX = "0xf9016d808506fc23ac0083026a508080b9015a6101488061000e6000396101565660007f01000000000000000000000000000000000000000000000000000000000000006000350460f8811215610038576001915061003f565b60f6810391505b508060005b368312156100c8577f01000000000000000000000000000000000000000000000000000000000000008335048391506080811215610087576001840193506100c2565b60b881121561009d57607f8103840193506100c1565b60c08112156100c05760b68103600185013560b783036020035260005101840193505b5b5b50610044565b81810360388112156100f4578060c00160005380836001378060010160002060e052602060e0f3610143565b61010081121561010557600161011b565b6201000081121561011757600261011a565b60035b5b8160005280601f038160f701815382856020378282600101018120610140526020610140f350505b505050505b6000f31b2d4f" # NOQA
MSG_HASHER_TX_SENDER = "0xD7a3BD6C9eA32efF147d067f907AE6b22d436F91"
PURITY_CHECKER_TX_HEX = "0xf90467808506fc23ac00830583c88080b904546104428061000e60003961045056600061033f537c0100000000000000000000000000000000000000000000000000000000600035047f80010000000000000000000000000000000000000030ffff1c0e00000000000060205263a1903eab8114156103f7573659905901600090523660048237600435608052506080513b806020015990590160009052818152602081019050905060a0526080513b600060a0516080513c6080513b8060200260200159905901600090528181526020810190509050610100526080513b806020026020015990590160009052818152602081019050905061016052600060005b602060a05103518212156103c957610100601f8360a051010351066020518160020a161561010a57fe5b80606013151561011e57607f811315610121565b60005b1561014f5780607f036101000a60018460a0510101510482602002610160510152605e8103830192506103b2565b60f18114801561015f5780610164565b60f282145b905080156101725780610177565b60f482145b9050156103aa5760028212151561019e5760606001830360200261010051015112156101a1565b60005b156101bc57607f6001830360200261010051015113156101bf565b60005b156101d157600282036102605261031e565b6004821215156101f057600360018303602002610100510151146101f3565b60005b1561020d57605a6002830360200261010051015114610210565b60005b1561022b57606060038303602002610100510151121561022e565b60005b1561024957607f60038303602002610100510151131561024c565b60005b1561025e57600482036102605261031d565b60028212151561027d57605a6001830360200261010051015114610280565b60005b1561029257600282036102605261031c565b6002821215156102b157609060018303602002610100510151146102b4565b60005b156102c657600282036102605261031b565b6002821215156102e65760806001830360200261010051015112156102e9565b60005b156103035760906001830360200261010051015112610306565b60005b1561031857600282036102605261031a565bfe5b5b5b5b5b604060405990590160009052600081526102605160200261016051015181602001528090502054156103555760016102a052610393565b60306102605160200261010051015114156103755760016102a052610392565b60606102605160200261010051015114156103915760016102a0525b5b5b6102a051151561039f57fe5b6001830192506103b1565b6001830192505b5b8082602002610100510152600182019150506100e0565b50506001604060405990590160009052600081526080518160200152809050205560016102e05260206102e0f35b63c23697a8811415610440573659905901600090523660048237600435608052506040604059905901600090526000815260805181602001528090502054610300526020610300f35b505b6000f31b2d4f" # NOQA
PURITY_CHECKER_TX_SENDER = "0xeA0f0D55EE82Edf248eD648A9A8d213FBa8b5081"
PURITY_CHECKER_TX_HEX = "0xf90403808506fc23ac0083061a808080b903f06103d856600035601c52740100000000000000000000000000000000000000006020526f7fffffffffffffffffffffffffffffff6040527fffffffffffffffffffffffffffffffff8000000000000000000000000000000060605274012a05f1fffffffffffffffffffffffffdabf41c006080527ffffffffffffffffffffffffed5fa0e000000000000000000000000000000000060a05263a1903eab600051141561038657602060046101403734156100b457600080fd5b60043560205181106100c557600080fd5b50610140513b60008114156100da57fe610367565b806000610160610140513c806041026101600160008160007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818352015b84845110151561012b5761036256610351565b610100601f8551036101600151068060020a7f80010000000000000000000000000000000000000030ffff1c0e000000000000161561016657fe5b607f811115816060111516156101a55780607f036101000a600186516101600101510484602002876021026101600101528451605f820301855261033d565b60f4811460f282141760f18214171561033c576000607f6001860360200288610160010151111560606001870360200289610160010151101516600286101516156101f5576002850390506102e2565b607f6003860360200288610160010151111560606003870360200289610160010151101516605a6002870360200289610160010151141660036001870360200289610160010151141660048610151615610254576004850390506102e1565b605a6001860360200288610160010151146002861015161561027b576002850390506102e0565b6090600186036020028861016001015114600286101516156102a2576002850390506102df565b609060018603602002886101600101511060806001870360200289610160010151101516600286101516156102dc576002850390506102de565bfe5b5b5b5b5b8060200287602102610160010151600060c052602060c0200154156103065761033a565b60308160200288610160010151141561031e57610339565b60608160200288610160010151141561033657610338565bfe5b5b5b505b5b808460200287610160010152600184019350505b5b8151600101808352811415610118575b505050505b50600161014051600060c052602060c0200155600160005260206000f3005b63c23697a860005114156103d357602060046101403734156103a757600080fd5b60043560205181106103b857600080fd5b5061014051600060c052602060c020015460005260206000f3005b5b6100046103d8036100046000396100046103d8036000f31b2d4f" # NOQA
PURITY_CHECKER_TX_SENDER = "0xBbED7124dB2543ac17c2096961a59a9C0095FA78"
PURITY_CHECKER_ABI = [{'name': 'check', 'type': 'function', 'constant': True, 'inputs': [{'name': 'addr', 'type': 'address'}], 'outputs': [{'name': 'out', 'type': 'bool'}]}, {'name': 'submit', 'type': 'function', 'constant': False, 'inputs': [{'name': 'addr', 'type': 'address'}], 'outputs': [{'name': 'out', 'type': 'bool'}]}] # NOQA

EPOCH_LENGTH = 10
Expand Down
Loading

0 comments on commit 89dd98f

Please sign in to comment.