def _assert_reason(self, test_expr, msg): if isinstance(msg, sri_ast.Name) and msg.id == 'UNREACHABLE': return self._assert_unreachable(test_expr, msg) if not isinstance(msg, sri_ast.Str): raise StructureException( 'Reason parameter of assert needs to be a literal string ' '(or UNREACHABLE constant).', msg) if len(msg.s.strip()) == 0: raise StructureException('Empty reason string not allowed.', self.stmt) reason_str = msg.s.strip() sig_placeholder = self.context.new_placeholder(BaseType(32)) arg_placeholder = self.context.new_placeholder(BaseType(32)) reason_str_type = ByteArrayType(len(reason_str)) placeholder_bytes = Expr(msg, self.context).lll_node method_id = fourbytes_to_int(keccak256(b"Error(string)")[:4]) assert_reason = [ 'seq', ['mstore', sig_placeholder, method_id], ['mstore', arg_placeholder, 32], placeholder_bytes, [ 'assert_reason', test_expr, int(sig_placeholder + 28), int(4 + get_size_of_type(reason_str_type) * 32), ], ] return LLLnode.from_list(assert_reason, typ=None, pos=getpos(self.stmt))
def keccak256_helper(expr, args, kwargs, context): sub = args[0] # Can hash literals if isinstance(sub, bytes): return LLLnode.from_list(bytes_to_int(keccak256(sub)), typ=BaseType('bytes32'), pos=getpos(expr)) # Can hash bytes32 objects if is_base_type(sub.typ, 'bytes32'): return LLLnode.from_list( [ 'seq', ['mstore', MemoryPositions.FREE_VAR_SPACE, sub], ['sha3', MemoryPositions.FREE_VAR_SPACE, 32] ], typ=BaseType('bytes32'), pos=getpos(expr), ) # Copy the data to an in-memory array if sub.location == "memory": # If we are hashing a value in memory, no need to copy it, just hash in-place return LLLnode.from_list( [ 'with', '_sub', sub, ['sha3', ['add', '_sub', 32], ['mload', '_sub']] ], typ=BaseType('bytes32'), pos=getpos(expr), ) elif sub.location == "storage": lengetter = LLLnode.from_list(['sload', ['sha3_32', '_sub']], typ=BaseType('int128')) else: # This should never happen, but just left here for future compiler-writers. raise Exception( f"Unsupported location: {sub.location}") # pragma: no test placeholder = context.new_placeholder(sub.typ) placeholder_node = LLLnode.from_list(placeholder, typ=sub.typ, location='memory') copier = make_byte_array_copier( placeholder_node, LLLnode.from_list('_sub', typ=sub.typ, location=sub.location), ) return LLLnode.from_list([ 'with', '_sub', sub, ['seq', copier, ['sha3', ['add', placeholder, 32], lengetter]], ], typ=BaseType('bytes32'), pos=getpos(expr))
def test_get_extcodehash(get_contract, evm_version): code = """ a: address @public def __init__(): self.a = self @public def foo(x: address) -> bytes32: return x.codehash @public def foo2(x: address) -> bytes32: b: address = x return b.codehash @public def foo3() -> bytes32: return self.codehash @public def foo4() -> bytes32: return self.a.codehash """ if evm_version in ("byzantium", "atlantis"): with pytest.raises(EvmVersionException): compile_code(code, evm_version=evm_version) return compiled = compile_code(code, ['bytecode_runtime'], evm_version=evm_version) bytecode = bytes.fromhex(compiled['bytecode_runtime'][2:]) hash_ = keccak256(bytecode) c = get_contract(code, evm_version=evm_version) assert c.foo(c.address) == hash_ assert not int(c.foo("0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF").hex(), 16) assert c.foo2(c.address) == hash_ assert not int(c.foo2("0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF").hex(), 16) assert c.foo3() == hash_ assert c.foo4() == hash_
def test_contracts_keccak(): hash_ = keccak256(FOO_CODE.encode()).hex() input_json = { 'sources': { 'foo.sri': { 'content': FOO_CODE, 'keccak256': hash_ } } } get_input_dict_contracts(input_json) input_json['sources']['foo.sri']['keccak256'] = "0x" + hash_ get_input_dict_contracts(input_json) input_json['sources']['foo.sri']['keccak256'] = "0x1234567890" with pytest.raises(JSONError): get_input_dict_contracts(input_json)
def get_input_dict_contracts(input_dict: Dict) -> ContractCodes: contract_sources: ContractCodes = {} for path, value in input_dict['sources'].items(): if 'urls' in value: raise JSONError(f"{path} - 'urls' is not a supported field, use 'content' instead") if 'content' not in value: raise JSONError(f"{path} missing required field - 'content'") if 'keccak256' in value: hash_ = value['keccak256'].lower() if hash_.startswith('0x'): hash_ = hash_[2:] if hash_ != keccak256(value['content'].encode('utf-8')).hex(): raise JSONError( f"Calculated keccak of '{path}' does not match keccak given in input JSON" ) key = _standardize_path(path) if key in contract_sources: raise JSONError(f"Contract namespace collision: {key}") contract_sources[key] = value['content'] return contract_sources
def test_raw_call_bytes32_data(w3, tester, get_contract_with_gas_estimation): code = """ b: uint256 @public def foo(): a: uint256 = 1234 self.b = 4321 raw_log([], convert(a, bytes32)) raw_log([], convert(self.b, bytes32)) raw_log([], convert(b"testmessage", bytes32)) raw_log([], keccak256(b"")) """ c = get_contract_with_gas_estimation(code) tx_hash = c.foo(transact={}) receipt = tester.get_transaction_receipt(tx_hash.hex()) logs = receipt['logs'] assert logs[0]['data'] == w3.toHex((1234).to_bytes(32, 'big')) assert logs[1]['data'] == w3.toHex((4321).to_bytes(32, 'big')) assert logs[2]['data'] == w3.toHex(b"testmessage").ljust(32*2 + 2, '0') assert logs[3]['data'] == w3.toHex(keccak256(b""))
def from_declaration(cls, code, global_ctx): name = code.target.id pos = 0 check_valid_varname(name, global_ctx._structs, global_ctx._constants, pos=code, error_prefix="Event name invalid. ", exc=EventDeclarationException) # Determine the arguments, expects something of the form def foo(arg1: num, arg2: num ... args = [] indexed_list = [] topics_count = 1 if code.annotation.args: keys = code.annotation.args[0].keys values = code.annotation.args[0].values for i in range(len(keys)): typ = values[i] if not isinstance(keys[i], sri_ast.Name): raise EventDeclarationException( 'Invalid key type, expected a valid name.', keys[i], ) if not isinstance( typ, (sri_ast.Name, sri_ast.Call, sri_ast.Subscript)): raise EventDeclarationException( 'Invalid event argument type.', typ) if isinstance(typ, sri_ast.Call) and not isinstance( typ.func, sri_ast.Name): raise EventDeclarationException( 'Invalid event argument type', typ) arg = keys[i].id arg_item = keys[i] is_indexed = False # Check to see if argument is a topic if isinstance(typ, sri_ast.Call) and typ.func.id == 'indexed': typ = values[i].args[0] indexed_list.append(True) topics_count += 1 is_indexed = True else: indexed_list.append(False) if isinstance(typ, sri_ast.Subscript) and getattr( typ.value, 'id', None ) == 'bytes' and typ.slice.value.n > 32 and is_indexed: # noqa: E501 raise EventDeclarationException( "Indexed arguments are limited to 32 bytes") if topics_count > 4: raise EventDeclarationException( f"Maximum of 3 topics {topics_count - 1} given", arg, ) if not isinstance(arg, str): raise VariableDeclarationException("Argument name invalid", arg) if not typ: raise InvalidType("Argument must have type", arg) check_valid_varname( arg, global_ctx._structs, global_ctx._constants, pos=arg_item, error_prefix="Event argument name invalid or reserved.", ) if arg in (x.name for x in args): raise VariableDeclarationException( "Duplicate function argument name: " + arg, arg_item, ) # Can struct be logged? parsed_type = global_ctx.parse_type(typ, None) args.append(VariableRecord(arg, pos, parsed_type, False)) if isinstance(parsed_type, ByteArrayType): pos += ceil32(typ.slice.value.n) else: pos += get_size_of_type(parsed_type) * 32 sig = name + '(' + ','.join([ canonicalize_type(arg.typ, indexed_list[pos]) for pos, arg in enumerate(args) ]) + ')' # noqa F812 event_id = bytes_to_int(keccak256(bytes(sig, 'utf-8'))) return cls(name, args, indexed_list, event_id, sig)
def from_definition(cls, code, sigs=None, custom_structs=None, contract_def=False, constants=None, constant_override=False): if not custom_structs: custom_structs = {} name = code.name mem_pos = 0 valid_name, msg = is_varname_valid(name, custom_structs, constants) if not valid_name and (not name.lower() in FUNCTION_WHITELIST): raise FunctionDeclarationException("Function name invalid. " + msg, code) # Validate default values. for default_value in getattr(code.args, 'defaults', []): validate_default_values(default_value) # Determine the arguments, expects something of the form def foo(arg1: # int128, arg2: int128 ... args = [] for arg in code.args.args: # Each arg needs a type specified. typ = arg.annotation if not typ: raise InvalidType("Argument must have type", arg) # Validate arg name. check_valid_varname( arg.arg, custom_structs, constants, arg, "Argument name invalid or reserved. ", FunctionDeclarationException, ) # Check for duplicate arg name. if arg.arg in (x.name for x in args): raise FunctionDeclarationException( "Duplicate function argument name: " + arg.arg, arg, ) parsed_type = parse_type( typ, None, sigs, custom_structs=custom_structs, constants=constants, ) args.append( VariableRecord( arg.arg, mem_pos, parsed_type, False, defined_at=getpos(arg), )) if isinstance(parsed_type, ByteArrayLike): mem_pos += 32 else: mem_pos += get_size_of_type(parsed_type) * 32 const = constant_override payable = False private = False public = False nonreentrant_key = '' # Update function properties from decorators for dec in code.decorator_list: if isinstance(dec, sri_ast.Name) and dec.id == "constant": const = True elif isinstance(dec, sri_ast.Name) and dec.id == "payable": payable = True elif isinstance(dec, sri_ast.Name) and dec.id == "private": private = True elif isinstance(dec, sri_ast.Name) and dec.id == "public": public = True elif isinstance(dec, sri_ast.Call) and dec.func.id == "nonreentrant": if nonreentrant_key: raise StructureException( "Only one @nonreentrant decorator allowed per function", dec) if dec.args and len(dec.args) == 1 and isinstance( dec.args[0], sri_ast.Str) and dec.args[0].s: # noqa: E501 nonreentrant_key = dec.args[0].s else: raise StructureException( "@nonreentrant decorator requires a non-empty string to use as a key.", dec) else: raise StructureException("Bad decorator", dec) if public and private: raise StructureException( f"Cannot use public and private decorators on the same function: {name}" ) if payable and const: raise StructureException( f"Function {name} cannot be both constant and payable.") if payable and private: raise StructureException( f"Function {name} cannot be both private and payable.") if (not public and not private) and not contract_def: raise StructureException( "Function visibility must be declared (@public or @private)", code, ) if const and nonreentrant_key: raise StructureException( "@nonreentrant makes no sense on a @constant function.", code) # Determine the return type and whether or not it's constant. Expects something # of the form: # def foo(): ... # def foo() -> int128: ... # If there is no return type, ie. it's of the form def foo(): ... # and NOT def foo() -> type: ..., then it's null if not code.returns: output_type = None elif isinstance(code.returns, (sri_ast.Name, sri_ast.Compare, sri_ast.Subscript, sri_ast.Call, sri_ast.Tuple)): output_type = parse_type( code.returns, None, sigs, custom_structs=custom_structs, constants=constants, ) else: raise InvalidType( f"Output type invalid or unsupported: {parse_type(code.returns, None)}", code.returns, ) # Output type must be canonicalizable if output_type is not None: assert isinstance(output_type, TupleType) or canonicalize_type(output_type) # Get the canonical function signature sig = cls.get_full_sig(name, code.args.args, sigs, custom_structs, constants) # Take the first 4 bytes of the hash of the sig to get the method ID method_id = fourbytes_to_int(keccak256(bytes(sig, 'utf-8'))[:4]) return cls(name, args, output_type, const, payable, private, nonreentrant_key, sig, method_id, code)