def fill_state_test(filler: Dict[str, Any]) -> Dict[str, Dict[str, Any]]: """ Filler function for filling state tests. """ test_name = get_test_name(filler) test = filler[test_name] environment = normalize_environment(test["env"]) pre_state = normalize_state(test["pre"]) transaction_group = normalize_transaction_group(test["transaction"]) post = defaultdict(list) # type: Dict[int, List[Dict[str, str]]] for expect in test["expect"]: indexes = expect["indexes"] networks = normalize_networks(expect["networks"]) result = normalize_state(expect["result"]) post_state = deep_merge(pre_state, result) for network in networks: state_class = STATE_CLASSES[network] post_state_root = calc_state_root(post_state, state_class) post[network].append({ "hash": encode_hex(post_state_root), "indexes": indexes, }) return { test_name: { "env": environment, "pre": pre_state, "transaction": transaction_group, "post": post } }
def execution(execution, filler): execution = normalize_execution(execution or {}) # user caller as origin if not explicitly given if "caller" in execution and "origin" not in execution: execution = assoc(execution, "origin", execution["caller"]) if "vyperLLLCode" in execution: code = compile_vyper_lll(execution["vyperLLLCode"]) if "code" in execution: if code != execution["code"]: raise ValueError("Compiled Vyper LLL code does not match") execution = assoc(execution, "code", code) execution = merge(DEFAULT_EXECUTION, execution) test_name = get_test_name(filler) return deep_merge( filler, { test_name: { "exec": execution, } } )
def fill_test(filler: Dict[str, Any], info: Dict[str, Any] = None, apply_formatter: bool = True, **kwargs: Any) -> Dict[str, Any]: test_name = get_test_name(filler) test = filler[test_name] if "transaction" in test: filled = fill_state_test(filler) formatter = filled_state_test_formatter elif "exec" in test: filled = fill_vm_test(filler, **kwargs) formatter = filled_vm_test_formatter else: raise ValueError( "Given filler does not appear to be for VM or state test") info = merge( { "filledwith": FILLED_WITH_TEMPLATE.format(version=get_version_from_git()) }, info if info else {}) filled = assoc_in(filled, [test_name, "_info"], info) if apply_formatter: return formatter(filled) else: return filled
def _expect(post_state: Dict[str, Any], networks: Any, transaction: TransactionDict, filler: Dict[str, Any]) -> Dict[str, Any]: test_name = get_test_name(filler) test = filler[test_name] test_update = {test_name: {}} # type: Dict[str, Dict[Any, Any]] pre_state = test.get("pre", {}) post_state = normalize_state(post_state or {}) defaults = { address: { "balance": 0, "nonce": 0, "code": b"", "storage": {}, } for address in post_state } result = deep_merge(defaults, pre_state, normalize_state(post_state)) new_expect = {"result": result} if transaction is not None: transaction = normalize_transaction( merge(get_default_transaction(networks), transaction)) if "transaction" not in test: transaction_group = apply_formatters_to_dict( { "data": wrap_in_list, "gasLimit": wrap_in_list, "value": wrap_in_list, }, transaction) indexes = { index_key: 0 for transaction_key, index_key in [ ("gasLimit", "gas"), ("value", "value"), ("data", "data"), ] if transaction_key in transaction_group } else: transaction_group, indexes = add_transaction_to_group( test["transaction"], transaction) new_expect = assoc(new_expect, "indexes", indexes) test_update = assoc_in(test_update, [test_name, "transaction"], transaction_group) if networks is not None: networks = normalize_networks(networks) new_expect = assoc(new_expect, "networks", networks) existing_expects = test.get("expect", []) expect = existing_expects + [new_expect] test_update = assoc_in(test_update, [test_name, "expect"], expect) return deep_merge(filler, test_update)
def execution(execution: Dict[str, Any], filler: Dict[str, Any]) -> Dict[str, Any]: """ For VM tests, specify the code that is being run as well as the current state of the EVM. State tests don't support this object. The parameter is a dictionary specifying some or all of the following keys: +--------------------+------------------------------------------------------------+ | key | description | +====================+============================================================+ | ``"address"`` | the address of the account executing the code | +--------------------+------------------------------------------------------------+ | ``"caller"`` | the caller address | +--------------------+------------------------------------------------------------+ | ``"origin"`` | the origin address (defaulting to the caller address) | +--------------------+------------------------------------------------------------+ | ``"value"`` | the value of the call | +--------------------+------------------------------------------------------------+ | ``"data"`` | the data passed with the call | +--------------------+------------------------------------------------------------+ | ``"gasPrice"`` | the gas price of the call | +--------------------+------------------------------------------------------------+ | ``"gas"`` | the amount of gas allocated for the call | +--------------------+------------------------------------------------------------+ | ``"code"`` | the bytecode to execute | +--------------------+------------------------------------------------------------+ | ``"vyperLLLCode"`` | the code in Vyper LLL (compiled to bytecode automatically) | +--------------------+------------------------------------------------------------+ """ execution = normalize_execution(execution or {}) # user caller as origin if not explicitly given if "caller" in execution and "origin" not in execution: execution = assoc(execution, "origin", execution["caller"]) if "vyperLLLCode" in execution: code = compile_vyper_lll(execution["vyperLLLCode"]) if "code" in execution: if code != execution["code"]: raise ValueError("Compiled Vyper LLL code does not match") execution = assoc(execution, "code", code) execution = merge(DEFAULT_EXECUTION, execution) test_name = get_test_name(filler) return deep_merge( filler, { test_name: { "exec": execution, } } )
def _pre_state(filler: Dict[str, Any]) -> Dict[str, Any]: test_name = get_test_name(filler) old_pre_state = filler[test_name].get("pre_state", {}) pre_state = normalize_state(raw_state) defaults = {address: { "balance": 0, "nonce": 0, "code": b"", "storage": {}, } for address in pre_state} new_pre_state = deep_merge(defaults, old_pre_state, pre_state) return assoc_in(filler, [test_name, "pre"], new_pre_state)
def fill_vm_test( filler: Dict[str, Any], *, call_creates: Any=None, gas_price: Union[int, str]=None, gas_remaining: Union[int, str]=0, logs: Iterable[Tuple[bytes, List[int], bytes]]=None, output: bytes=b"") -> Dict[str, Dict[str, Any]]: test_name = get_test_name(filler) test = filler[test_name] environment = normalize_environment(test["env"]) pre_state = normalize_state(test["pre"]) execution = normalize_execution(test["exec"]) assert len(test["expect"]) == 1 expect = test["expect"][0] assert "network" not in test assert "indexes" not in test result = normalize_state(expect["result"]) post_state = deep_merge(pre_state, result) call_creates = normalize_call_creates(call_creates or []) gas_remaining = normalize_int(gas_remaining) output = normalize_bytes(output) logs = normalize_logs(logs or []) log_hash = hash_log_entries(logs) return { test_name: { "env": environment, "pre": pre_state, "exec": execution, "post": post_state, "callcreates": call_creates, "gas": gas_remaining, "output": output, "logs": log_hash, } }
PARENT_DIR = os.path.dirname(os.path.abspath(__file__)) OUTPUT_DIR = os.path.join(PARENT_DIR, "json") FILLER_PARENT_DIR = os.path.join(OUTPUT_DIR, "fillers") TEST_PARENT_DIR = os.path.join(OUTPUT_DIR, "tests") DIR_STRUCTURE = { } if __name__ == "__main__": for (filler_dir, test_dir), test_groups in DIR_STRUCTURE.items(): for test_group, tests in test_groups.items(): for filler, filler_kwargs in tests: test_name = get_test_name(filler) filename = test_name + ".json" filler_src_path = os.path.join(filler_dir, test_group, filename) filler_path = os.path.join(FILLER_PARENT_DIR, filler_src_path) test_path = os.path.join(TEST_PARENT_DIR, test_dir, test_group, filename) for path in [filler_path, test_path]: os.makedirs(os.path.dirname(path), exist_ok=True) formatted_filler = filler_formatter(filler) filler_string = json.dumps(formatted_filler, indent=4, sort_keys=True) with open(filler_path, "w") as filler_file: filler_file.write(filler_string) filler_hash = keccak(filler_string.encode("ascii"))