def constancy_checks(node, context, stmt): if node.location == 'storage' and context.is_constant(): raise ConstancyViolation( f"Cannot modify storage inside {context.pp_constancy()}: {node.annotation}", stmt, ) if not node.mutable: raise ConstancyViolation( f"Cannot modify function argument: {node.annotation}", stmt, )
def build_LLL(self, expr, args, kwargs, context): value = kwargs['value'] if context.is_constant(): raise ConstancyViolation( f"Cannot make calls from {context.pp_constancy()}", expr, ) placeholder = context.new_placeholder(ByteArrayType(96)) kode = get_create_forwarder_to_bytecode() high = bytes_to_int(kode[:32]) low = bytes_to_int((kode + b'\x00' * 32)[47:79]) return LLLnode.from_list( [ 'seq', ['mstore', placeholder, high], ['mstore', ['add', placeholder, 27], ['mul', args[0], 2**96]], ['mstore', ['add', placeholder, 47], low], ['clamp_nonzero', ['create', value, placeholder, 96]], ], typ=BaseType('address'), pos=getpos(expr), add_gas_estimate=11000, )
def build_LLL(self, expr, args, kwargs, context): if context.is_constant(): raise ConstancyViolation( f"Cannot {expr.func.id} inside {context.pp_constancy()}!", expr.func, ) return LLLnode.from_list(['selfdestruct', args[0]], typ=None, pos=getpos(expr))
def raw_call(expr, args, kwargs, context): to, data = args gas, value, outsize, delegate_call = ( kwargs['gas'], kwargs['value'], kwargs['outsize'], kwargs['is_delegate_call'], ) if delegate_call.typ.is_literal is False: raise TypeMismatch( 'The delegate_call parameter has to be a static/literal boolean value.' ) if context.is_constant(): raise ConstancyViolation( f"Cannot make calls from {context.pp_constancy()}", expr, ) placeholder = context.new_placeholder(data.typ) placeholder_node = LLLnode.from_list(placeholder, typ=data.typ, location='memory') copier = make_byte_array_copier(placeholder_node, data, pos=getpos(expr)) output_placeholder = context.new_placeholder(ByteArrayType(outsize)) output_node = LLLnode.from_list( output_placeholder, typ=ByteArrayType(outsize), location='memory', ) # build LLL for call or delegatecall common_call_lll = [ ['add', placeholder_node, 32], ['mload', placeholder_node], # if there is no return value, the return offset can be 0 ['add', output_node, 32] if outsize else 0, outsize ] if delegate_call.value == 1: call_lll = ['delegatecall', gas, to] + common_call_lll else: call_lll = ['call', gas, to, value] + common_call_lll # build sequence LLL if outsize: # only copy the return value to memory if outsize > 0 seq = [ 'seq', copier, ['assert', call_lll], ['mstore', output_node, outsize], output_node ] typ = ByteArrayType(outsize) else: seq = ['seq', copier, ['assert', call_lll]] typ = None return LLLnode.from_list(seq, typ=typ, location="memory", pos=getpos(expr))
def build_LLL(self, expr, args, kwargs, context): to, value = args if context.is_constant(): raise ConstancyViolation( f"Cannot send ether inside {context.pp_constancy()}!", expr, ) return LLLnode.from_list( ['assert', ['call', 0, to, value, 0, 0, 0, 0]], typ=None, pos=getpos(expr), )
def send(expr, args, kwargs, context): to, value = args if context.is_constant(): raise ConstancyViolation( f"Cannot send ether inside {context.pp_constancy()}!", expr, ) enforce_units(value.typ, expr.args[1], BaseType('uint256', {'wei': 1})) return LLLnode.from_list( ['assert', ['call', 0, to, value, 0, 0, 0, 0]], typ=None, pos=getpos(expr), )
def make_call(stmt_expr, context): method_name, _, sig = call_lookup_specs(stmt_expr, context) if context.is_constant() and not sig.const: raise ConstancyViolation( f"May not call non-constant function '{method_name}' within {context.pp_constancy()}.", getpos(stmt_expr) ) if not sig.private: raise StructureException("Cannot call public functions via 'self'", stmt_expr) return call_self_private(stmt_expr, context, sig)
def make_setter(left, right, location, pos, in_function_call=False): # Basic types if isinstance(left.typ, BaseType): right = base_type_conversion( right, right.typ, left.typ, pos, in_function_call=in_function_call, ) if location == 'storage': return LLLnode.from_list(['sstore', left, right], typ=None) elif location == 'memory': return LLLnode.from_list(['mstore', left, right], typ=None) # Byte arrays elif isinstance(left.typ, ByteArrayLike): return make_byte_array_copier(left, right, pos) # Can't copy mappings elif isinstance(left.typ, MappingType): raise TypeMismatch( "Cannot copy mappings; can only copy individual elements", pos) # Arrays elif isinstance(left.typ, ListType): # Cannot do something like [a, b, c] = [1, 2, 3] if left.value == "multi": raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, (ListType, NullType)): raise TypeMismatch( f"Setter type mismatch: left side is array, right side is {right.typ}", pos) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" # Type checks if not isinstance(right.typ, NullType): if not isinstance(right.typ, ListType): raise TypeMismatch("Left side is array, right side is not", pos) if left.typ.count != right.typ.count: raise TypeMismatch("Mismatched number of elements", pos) # If the right side is a literal if right.value == "multi": if len(right.args) != left.typ.count: raise TypeMismatch("Mismatched number of elements", pos) subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, array_bounds_check=False, ), right.args[i], location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null # CC 20190619 probably not needed as of #1106 elif isinstance(right.typ, NullType): subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, array_bounds_check=False, ), LLLnode.from_list(None, typ=NullType()), location, pos=pos)) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a variable else: right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) subs = [] for i in range(left.typ.count): subs.append( make_setter(add_variable_offset( left_token, LLLnode.from_list(i, typ='int128'), pos=pos, array_bounds_check=False, ), add_variable_offset( right_token, LLLnode.from_list(i, typ='int128'), pos=pos, array_bounds_check=False, ), location, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None) # Structs elif isinstance(left.typ, TupleLike): if left.value == "multi" and isinstance(left.typ, StructType): raise Exception("Target of set statement must be a single item") if not isinstance(right.typ, NullType): if not isinstance(right.typ, left.typ.__class__): raise TypeMismatch( f"Setter type mismatch: left side is {left.typ}, right side is {right.typ}", pos, ) if isinstance(left.typ, StructType): for k in right.args: if k.value is None: raise InvalidLiteral( 'Setting struct value to None is not allowed, use a default value.', pos, ) for k in left.typ.members: if k not in right.typ.members: raise TypeMismatch( f"Keys don't match for structs, missing {k}", pos, ) for k in right.typ.members: if k not in left.typ.members: raise TypeMismatch( f"Keys don't match for structs, extra {k}", pos, ) if left.typ.name != right.typ.name: raise TypeMismatch(f"Expected {left.typ}, got {right.typ}", pos) else: if len(left.typ.members) != len(right.typ.members): raise TypeMismatch( "Tuple lengths don't match, " f"{len(left.typ.members)} vs {len(right.typ.members)}", pos, ) left_token = LLLnode.from_list('_L', typ=left.typ, location=left.location) if left.location == "storage": left = LLLnode.from_list(['sha3_32', left], typ=left.typ, location="storage_prehashed") left_token.location = "storage_prehashed" keyz = left.typ.tuple_keys() # If the left side is a literal if left.value == 'multi': locations = [arg.location for arg in left.args] else: locations = [location for _ in keyz] # If the right side is a literal if right.value == "multi": if len(right.args) != len(keyz): raise TypeMismatch("Mismatched number of elements", pos) # get the RHS arguments into a dict because # they are not guaranteed to be in the same order # the LHS keys. right_args = dict(zip(right.typ.tuple_keys(), right.args)) subs = [] for (key, loc) in zip(keyz, locations): subs.append( make_setter( add_variable_offset(left_token, key, pos=pos), right_args[key], loc, pos=pos, )) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If the right side is a null elif isinstance(right.typ, NullType): subs = [] for typ, loc in zip(keyz, locations): subs.append( make_setter( add_variable_offset(left_token, typ, pos=pos), LLLnode.from_list(None, typ=NullType()), loc, pos=pos, )) return LLLnode.from_list(['with', '_L', left, ['seq'] + subs], typ=None) # If tuple assign. elif isinstance(left.typ, TupleType) and isinstance( right.typ, TupleType): subs = [] static_offset_counter = 0 zipped_components = zip(left.args, right.typ.members, locations) for var_arg in left.args: if var_arg.location == 'calldata': raise ConstancyViolation( f"Cannot modify function argument: {var_arg.annotation}", pos) for left_arg, right_arg, loc in zipped_components: if isinstance(right_arg, ByteArrayLike): RType = ByteArrayType if isinstance( right_arg, ByteArrayType) else StringType offset = LLLnode.from_list([ 'add', '_R', ['mload', ['add', '_R', static_offset_counter]] ], typ=RType(right_arg.maxlen), location='memory', pos=pos) static_offset_counter += 32 else: offset = LLLnode.from_list( ['mload', ['add', '_R', static_offset_counter]], typ=right_arg.typ, pos=pos, ) static_offset_counter += get_size_of_type(right_arg) * 32 subs.append(make_setter(left_arg, offset, loc, pos=pos)) return LLLnode.from_list( ['with', '_R', right, ['seq'] + subs], typ=None, annotation='Tuple assignment', ) # If the right side is a variable else: subs = [] right_token = LLLnode.from_list('_R', typ=right.typ, location=right.location) for typ, loc in zip(keyz, locations): subs.append( make_setter(add_variable_offset(left_token, typ, pos=pos), add_variable_offset(right_token, typ, pos=pos), loc, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None, ) else: raise Exception("Invalid type for setters")
def raw_call(expr, args, kwargs, context): to, data = args gas, value, outsize, delegate_call = ( kwargs['gas'], kwargs['value'], kwargs['outsize'], kwargs['delegate_call'], ) if delegate_call.typ.is_literal is False: raise TypeMismatch( 'The delegate_call parameter has to be a static/literal boolean value.' ) if context.is_constant(): raise ConstancyViolation( f"Cannot make calls from {context.pp_constancy()}", expr, ) if value != zero_value: enforce_units( value.typ, get_keyword(expr, 'value'), BaseType('uint256', {'wei': 1}), ) placeholder = context.new_placeholder(data.typ) placeholder_node = LLLnode.from_list(placeholder, typ=data.typ, location='memory') copier = make_byte_array_copier(placeholder_node, data, pos=getpos(expr)) output_placeholder = context.new_placeholder(ByteArrayType(outsize)) output_node = LLLnode.from_list( output_placeholder, typ=ByteArrayType(outsize), location='memory', ) if delegate_call.value == 1: z = LLLnode.from_list( [ 'seq', copier, [ 'assert', [ 'delegatecall', gas, to, ['add', placeholder_node, 32], ['mload', placeholder_node], ['add', output_node, 32], outsize, ], ], ['mstore', output_node, outsize], output_node, ], typ=ByteArrayType(outsize), location='memory', pos=getpos(expr), ) else: z = LLLnode.from_list([ 'seq', copier, [ 'assert', [ 'call', gas, to, value, ['add', placeholder_node, 32], ['mload', placeholder_node], ['add', output_node, 32], outsize, ], ], ['mstore', output_node, outsize], output_node, ], typ=ByteArrayType(outsize), location='memory', pos=getpos(expr)) return z
def external_contract_call(node, context, contract_name, contract_address, pos, value=None, gas=None): from vyper.parser.expr import ( Expr, ) if value is None: value = 0 if gas is None: gas = 'gas' if not contract_name: raise StructureException( f'Invalid external contract call "{node.func.attr}".', node) if contract_name not in context.sigs: raise VariableDeclarationException( f'Contract "{contract_name}" not declared yet', node) if contract_address.value == "address": raise StructureException(f"External calls to self are not permitted.", node) method_name = node.func.attr if method_name not in context.sigs[contract_name]: raise FunctionDeclarationException(( f"Function not declared yet: {method_name} (reminder: " "function must be declared in the correct contract)" f"The available methods are: {','.join(context.sigs[contract_name].keys())}" ), node.func) sig = context.sigs[contract_name][method_name] inargs, inargsize, _ = pack_arguments( sig, [Expr(arg, context).lll_node for arg in node.args], context, node.func, ) output_placeholder, output_size, returner = get_external_contract_call_output( sig, context) sub = [ 'seq', ['assert', ['extcodesize', contract_address]], ['assert', ['ne', 'address', contract_address]], ] if context.is_constant() and not sig.const: raise ConstancyViolation( f"May not call non-constant function '{method_name}' within {context.pp_constancy()}." " For asserting the result of modifiable contract calls, try assert_modifiable.", node) if context.is_constant() or sig.const: sub.append([ 'assert', [ 'staticcall', gas, contract_address, inargs, inargsize, output_placeholder, output_size, ] ]) else: sub.append([ 'assert', [ 'call', gas, contract_address, value, inargs, inargsize, output_placeholder, output_size, ] ]) sub.extend(returner) o = LLLnode.from_list(sub, typ=sig.output_type, location='memory', pos=getpos(node)) return o