def extract32(expr, args, kwargs, context): sub, index = args ret_type = kwargs['type'] # Get length and specific element if sub.location == "memory": lengetter = LLLnode.from_list(['mload', '_sub'], typ=BaseType('int128')) elementgetter = lambda index: LLLnode.from_list( ['mload', ['add', '_sub', ['add', 32, ['mul', 32, index]]]], typ=BaseType('int128')) elif sub.location == "storage": lengetter = LLLnode.from_list(['sload', ['sha3_32', '_sub']], typ=BaseType('int128')) elementgetter = lambda index: LLLnode.from_list( ['sload', ['add', ['sha3_32', '_sub'], ['add', 1, index]]], typ=BaseType('int128')) # Special case: index known to be a multiple of 32 if isinstance(index.value, int) and not index.value % 32: o = LLLnode.from_list([ 'with', '_sub', sub, elementgetter( ['div', ['clamp', 0, index, ['sub', lengetter, 32]], 32]) ], typ=BaseType(ret_type), annotation='extracting 32 bytes') # General case else: o = LLLnode.from_list([ 'with', '_sub', sub, [ 'with', '_len', lengetter, [ 'with', '_index', ['clamp', 0, index, ['sub', '_len', 32]], [ 'with', '_mi32', ['mod', '_index', 32], [ 'with', '_di32', ['div', '_index', 32], [ 'if', '_mi32', [ 'add', [ 'mul', elementgetter('_di32'), ['exp', 256, '_mi32'] ], [ 'div', elementgetter(['add', '_di32', 1]), ['exp', 256, ['sub', 32, '_mi32']] ] ], elementgetter('_di32') ] ] ] ] ] ], typ=BaseType(ret_type), pos=getpos(expr), annotation='extracting 32 bytes') if ret_type == 'int128': return LLLnode.from_list([ 'clamp', ['mload', MemoryPositions.MINNUM], o, ['mload', MemoryPositions.MAXNUM] ], typ=BaseType('int128'), pos=getpos(expr)) elif ret_type == 'address': return LLLnode.from_list( ['uclamplt', o, ['mload', MemoryPositions.ADDRSIZE]], typ=BaseType(ret_type), pos=getpos(expr)) else: return o
def parse_return(self): if self.context.return_type is None: if self.stmt.value: raise TypeMismatchException("Not expecting to return a value", self.stmt) return LLLnode.from_list( make_return_stmt(self.stmt, self.context, 0, 0), typ=None, pos=getpos(self.stmt), valency=0, ) if not self.stmt.value: raise TypeMismatchException("Expecting to return a value", self.stmt) sub = Expr(self.stmt.value, self.context).lll_node # Returning a value (most common case) if isinstance(sub.typ, BaseType): sub = unwrap_location(sub) if not isinstance(self.context.return_type, BaseType): raise TypeMismatchException( f"Return type units mismatch {sub.typ} {self.context.return_type}", self.stmt.value) elif self.context.return_type != sub.typ and not sub.typ.is_literal: raise TypeMismatchException( f"Trying to return base type {sub.typ}, output expecting " f"{self.context.return_type}", self.stmt.value, ) elif sub.typ.is_literal and ( self.context.return_type.typ == sub.typ or 'int' in self.context.return_type.typ and 'int' in sub.typ.typ): # noqa: E501 if not SizeLimits.in_bounds(self.context.return_type.typ, sub.value): raise InvalidLiteralException( "Number out of range: " + str(sub.value), self.stmt) else: return LLLnode.from_list( [ 'seq', ['mstore', 0, sub], make_return_stmt(self.stmt, self.context, 0, 32) ], typ=None, pos=getpos(self.stmt), valency=0, ) elif is_base_type(sub.typ, self.context.return_type.typ) or ( is_base_type(sub.typ, 'int128') and is_base_type( self.context.return_type, 'int256')): # noqa: E501 return LLLnode.from_list( [ 'seq', ['mstore', 0, sub], make_return_stmt(self.stmt, self.context, 0, 32) ], typ=None, pos=getpos(self.stmt), valency=0, ) else: raise TypeMismatchException( f"Unsupported type conversion: {sub.typ} to {self.context.return_type}", self.stmt.value, ) # Returning a byte array elif isinstance(sub.typ, ByteArrayLike): if not sub.typ.eq_base(self.context.return_type): raise TypeMismatchException( f"Trying to return base type {sub.typ}, output expecting " f"{self.context.return_type}", self.stmt.value, ) if sub.typ.maxlen > self.context.return_type.maxlen: raise TypeMismatchException( f"Cannot cast from greater max-length {sub.typ.maxlen} to shorter " f"max-length {self.context.return_type.maxlen}", self.stmt.value, ) # loop memory has to be allocated first. loop_memory_position = self.context.new_placeholder( typ=BaseType('uint256')) # len & bytez placeholder have to be declared after each other at all times. len_placeholder = self.context.new_placeholder( typ=BaseType('uint256')) bytez_placeholder = self.context.new_placeholder(typ=sub.typ) if sub.location in ('storage', 'memory'): return LLLnode.from_list([ 'seq', make_byte_array_copier(LLLnode( bytez_placeholder, location='memory', typ=sub.typ), sub, pos=getpos(self.stmt)), zero_pad(bytez_placeholder), ['mstore', len_placeholder, 32], make_return_stmt( self.stmt, self.context, len_placeholder, ['ceil32', ['add', ['mload', bytez_placeholder], 64]], loop_memory_position=loop_memory_position, ) ], typ=None, pos=getpos(self.stmt), valency=0) else: raise Exception(f"Invalid location: {sub.location}") elif isinstance(sub.typ, ListType): sub_base_type = re.split(r'\(|\[', str(sub.typ.subtype))[0] ret_base_type = re.split(r'\(|\[', str(self.context.return_type.subtype))[0] loop_memory_position = self.context.new_placeholder( typ=BaseType('uint256')) if sub_base_type != ret_base_type: raise TypeMismatchException( f"List return type {sub_base_type} does not match specified " f"return type, expecting {ret_base_type}", self.stmt) elif sub.location == "memory" and sub.value != "multi": return LLLnode.from_list( make_return_stmt( self.stmt, self.context, sub, get_size_of_type(self.context.return_type) * 32, loop_memory_position=loop_memory_position, ), typ=None, pos=getpos(self.stmt), valency=0, ) else: new_sub = LLLnode.from_list( self.context.new_placeholder(self.context.return_type), typ=self.context.return_type, location='memory', ) setter = make_setter(new_sub, sub, 'memory', pos=getpos(self.stmt)) return LLLnode.from_list([ 'seq', setter, make_return_stmt( self.stmt, self.context, new_sub, get_size_of_type(self.context.return_type) * 32, loop_memory_position=loop_memory_position, ) ], typ=None, pos=getpos(self.stmt)) # Returning a struct elif isinstance(sub.typ, StructType): retty = self.context.return_type if not isinstance(retty, StructType) or retty.name != sub.typ.name: raise TypeMismatchException( f"Trying to return {sub.typ}, output expecting {self.context.return_type}", self.stmt.value, ) return gen_tuple_return(self.stmt, self.context, sub) # Returning a tuple. elif isinstance(sub.typ, TupleType): if not isinstance(self.context.return_type, TupleType): raise TypeMismatchException( f"Trying to return tuple type {sub.typ}, output expecting " f"{self.context.return_type}", self.stmt.value, ) if len(self.context.return_type.members) != len(sub.typ.members): raise StructureException("Tuple lengths don't match!", self.stmt) # check return type matches, sub type. for i, ret_x in enumerate(self.context.return_type.members): s_member = sub.typ.members[i] sub_type = s_member if isinstance(s_member, NodeType) else s_member.typ if type(sub_type) is not type(ret_x): raise StructureException( "Tuple return type does not match annotated return. " f"{type(sub_type)} != {type(ret_x)}", self.stmt) return gen_tuple_return(self.stmt, self.context, sub) else: raise TypeMismatchException(f"Can't return type {sub.typ}", self.stmt)
def parse_name(self): if self.stmt.id == "vdb": return LLLnode('debugger', typ=None, pos=getpos(self.stmt)) else: raise StructureException( f"Unsupported statement type: {type(self.stmt)}", self.stmt)
def parse_for_list(self): with self.context.range_scope(): iter_list_node = Expr(self.stmt.iter, self.context).lll_node if not isinstance(iter_list_node.typ.subtype, BaseType): # Sanity check on list subtype. raise StructureException( 'For loops allowed only on basetype lists.', self.stmt.iter) iter_var_type = (self.context.vars.get(self.stmt.iter.id).typ if isinstance(self.stmt.iter, ast.Name) else None) subtype = iter_list_node.typ.subtype.typ varname = self.stmt.target.id value_pos = self.context.new_variable( varname, BaseType(subtype, unit=iter_list_node.typ.subtype.unit), ) i_pos_raw_name = '_index_for_' + varname i_pos = self.context.new_internal_variable( i_pos_raw_name, BaseType(subtype), ) self.context.forvars[varname] = True # Is a list that is already allocated to memory. if iter_var_type: list_name = self.stmt.iter.id # make sure list cannot be altered whilst iterating. with self.context.in_for_loop_scope(list_name): iter_var = self.context.vars.get(self.stmt.iter.id) if iter_var.location == 'calldata': fetcher = 'calldataload' elif iter_var.location == 'memory': fetcher = 'mload' else: raise CompilerPanic( 'List iteration only supported on in-memory types', self.expr) body = [ 'seq', [ 'mstore', value_pos, [ fetcher, [ 'add', iter_var.pos, ['mul', ['mload', i_pos], 32] ] ], ], parse_body(self.stmt.body, self.context) ] o = LLLnode.from_list( ['repeat', i_pos, 0, iter_var.size, body], typ=None, pos=getpos(self.stmt)) # List gets defined in the for statement. elif isinstance(self.stmt.iter, ast.List): # Allocate list to memory. count = iter_list_node.typ.count tmp_list = LLLnode.from_list(obj=self.context.new_placeholder( ListType(iter_list_node.typ.subtype, count)), typ=ListType( iter_list_node.typ.subtype, count), location='memory') setter = make_setter(tmp_list, iter_list_node, 'memory', pos=getpos(self.stmt)) body = [ 'seq', [ 'mstore', value_pos, [ 'mload', ['add', tmp_list, ['mul', ['mload', i_pos], 32]] ] ], parse_body(self.stmt.body, self.context) ] o = LLLnode.from_list( ['seq', setter, ['repeat', i_pos, 0, count, body]], typ=None, pos=getpos(self.stmt)) # List contained in storage. elif isinstance(self.stmt.iter, ast.Attribute): count = iter_list_node.typ.count list_name = iter_list_node.annotation # make sure list cannot be altered whilst iterating. with self.context.in_for_loop_scope(list_name): body = [ 'seq', [ 'mstore', value_pos, [ 'sload', [ 'add', ['sha3_32', iter_list_node], ['mload', i_pos] ] ] ], parse_body(self.stmt.body, self.context), ] o = LLLnode.from_list( ['seq', ['repeat', i_pos, 0, count, body]], typ=None, pos=getpos(self.stmt)) del self.context.vars[varname] # this kind of open access to the vars dict should be disallowed. # we should use member functions to provide an API for these kinds # of operations. del self.context.vars[self.context._mangle(i_pos_raw_name)] del self.context.forvars[varname] return o
def parse_continue(self): return LLLnode.from_list('continue', typ=None, pos=getpos(self.stmt))
def assign(self): # Assignment (e.g. x[4] = y) if len(self.stmt.targets) != 1: raise StructureException( "Assignment statement must have one target", self.stmt) with self.context.assignment_scope(): sub = Expr(self.stmt.value, self.context).lll_node # Disallow assignment to None if isinstance(sub.typ, NullType): raise InvalidLiteralException( ('Assignment to None is not allowed, use a default value ' 'or built-in `clear()`.'), self.stmt, ) is_valid_rlp_list_assign = (isinstance( self.stmt.value, ast.Call)) and getattr( self.stmt.value.func, 'id', '') == 'RLPList' # Determine if it's an RLPList assignment. if is_valid_rlp_list_assign: pos = self.context.new_variable(self.stmt.targets[0].id, sub.typ) variable_loc = LLLnode.from_list( pos, typ=sub.typ, location='memory', pos=getpos(self.stmt), annotation=self.stmt.targets[0].id, ) o = make_setter(variable_loc, sub, 'memory', pos=getpos(self.stmt)) else: # Error check when assigning to declared variable if isinstance(self.stmt.targets[0], ast.Name): # Do not allow assignment to undefined variables without annotation if self.stmt.targets[0].id not in self.context.vars: raise VariableDeclarationException( "Variable type not defined", self.stmt) # Check against implicit conversion self._check_implicit_conversion(self.stmt.targets[0].id, sub) is_valid_tuple_assign = (isinstance( self.stmt.targets[0], ast.Tuple)) and isinstance( self.stmt.value, ast.Tuple) # Do no allow tuple-to-tuple assignment if is_valid_tuple_assign: raise VariableDeclarationException( "Tuple to tuple assignment not supported", self.stmt, ) # Checks to see if assignment is valid target = self.get_target(self.stmt.targets[0]) if isinstance(target.typ, ContractType) and not isinstance( sub.typ, ContractType): raise TypeMismatchException( 'Contract assignment expects casted address: ' f'{target.typ.unit}(<address_var>)', self.stmt) o = make_setter(target, sub, target.location, pos=getpos(self.stmt)) o.pos = getpos(self.stmt) return o
def _assert_unreachable(test_expr, msg): return LLLnode.from_list(['assert_unreachable', test_expr], typ=None, pos=getpos(msg))
def _slice(expr, args, kwargs, context): sub, start, length = args[0], kwargs['start'], kwargs['len'] if not are_units_compatible(start.typ, BaseType('int128')): raise TypeMismatchException("Type for slice start index must be a unitless number", expr) # Expression representing the length of the slice if not are_units_compatible(length.typ, BaseType('int128')): raise TypeMismatchException("Type for slice length must be a unitless number", expr) if is_base_type(sub.typ, 'bytes32'): if (start.typ.is_literal and length.typ.is_literal) and \ not (0 <= start.value + length.value <= 32): raise InvalidLiteralException('Invalid start / length values needs to be between 0 and 32.', expr) sub_typ_maxlen = 32 else: sub_typ_maxlen = sub.typ.maxlen # Node representing the position of the output in memory np = context.new_placeholder(ByteArrayType(maxlen=sub_typ_maxlen + 32)) # Get returntype string or bytes if isinstance(args[0].typ, ByteArrayType) or is_base_type(sub.typ, 'bytes32'): ReturnType = ByteArrayType else: ReturnType = StringType # Node representing the position of the output in memory np = context.new_placeholder(ReturnType(maxlen=sub_typ_maxlen + 32)) placeholder_node = LLLnode.from_list(np, typ=sub.typ, location='memory') placeholder_plus_32_node = LLLnode.from_list(np + 32, typ=sub.typ, location='memory') # Copies over bytearray data if sub.location == 'storage': adj_sub = LLLnode.from_list( ['add', ['sha3_32', sub], ['add', ['div', '_start', 32], 1]], typ=sub.typ, location=sub.location ) else: adj_sub = LLLnode.from_list( ['add', sub, ['add', ['sub', '_start', ['mod', '_start', 32]], 32]], typ=sub.typ, location=sub.location ) if is_base_type(sub.typ, 'bytes32'): adj_sub = LLLnode.from_list( sub.args[0], typ=sub.typ, location="memory" ) copier = make_byte_slice_copier(placeholder_plus_32_node, adj_sub, ['add', '_length', 32], sub_typ_maxlen, pos=getpos(expr)) # New maximum length in the type of the result newmaxlen = length.value if not len(length.args) else sub_typ_maxlen if is_base_type(sub.typ, 'bytes32'): maxlen = 32 else: maxlen = ['mload', Expr(sub, context=context).lll_node] # Retrieve length of the bytes. out = ['with', '_start', start, ['with', '_length', length, ['with', '_opos', ['add', placeholder_node, ['mod', '_start', 32]], ['seq', ['assert', ['le', ['add', '_start', '_length'], maxlen]], copier, ['mstore', '_opos', '_length'], '_opos']]]] return LLLnode.from_list(out, typ=ReturnType(newmaxlen), location='memory', pos=getpos(expr))
def concat(expr, context): args = [Expr(arg, context).lll_node for arg in expr.args] if len(args) < 2: raise StructureException("Concat expects at least two arguments", expr) prev_type = '' for i, (expr_arg, arg) in enumerate(zip(expr.args, args)): if not isinstance(arg.typ, ByteArrayLike) and not is_base_type(arg.typ, 'bytes32'): raise TypeMismatchException("Concat expects string, bytes or bytes32 objects", expr_arg) current_type = 'bytes' if isinstance(arg.typ, ByteArrayType) or is_base_type(arg.typ, 'bytes32') else 'string' if prev_type and current_type != prev_type: raise TypeMismatchException("Concat expects consistant use of string or byte types, user either bytes or string.", expr_arg) prev_type = current_type if current_type == 'string': ReturnType = StringType else: ReturnType = ByteArrayType # Maximum length of the output total_maxlen = sum([arg.typ.maxlen if isinstance(arg.typ, ByteArrayType) else 32 for arg in args]) # Node representing the position of the output in memory placeholder = context.new_placeholder(ReturnType(total_maxlen)) # Object representing the output seq = [] # For each argument we are concatenating... for arg in args: # Start pasting into a position the starts at zero, and keeps # incrementing as we concatenate arguments placeholder_node = LLLnode.from_list(['add', placeholder, '_poz'], typ=ReturnType(total_maxlen), location='memory') placeholder_node_plus_32 = LLLnode.from_list(['add', ['add', placeholder, '_poz'], 32], typ=ReturnType(total_maxlen), location='memory') if isinstance(arg.typ, ReturnType): # Ignore empty strings if arg.typ.maxlen == 0: continue # Get the length of the current argument if arg.location == "memory": length = LLLnode.from_list(['mload', '_arg'], typ=BaseType('int128')) argstart = LLLnode.from_list(['add', '_arg', 32], typ=arg.typ, location=arg.location) elif arg.location == "storage": length = LLLnode.from_list(['sload', ['sha3_32', '_arg']], typ=BaseType('int128')) argstart = LLLnode.from_list(['add', ['sha3_32', '_arg'], 1], typ=arg.typ, location=arg.location) # Make a copier to copy over data from that argument seq.append(['with', '_arg', arg, ['seq', make_byte_slice_copier(placeholder_node_plus_32, argstart, length, arg.typ.maxlen, pos=getpos(expr)), # Change the position to start at the correct # place to paste the next value ['set', '_poz', ['add', '_poz', length]]]]) else: seq.append(['seq', ['mstore', ['add', placeholder_node, 32], unwrap_location(arg)], ['set', '_poz', ['add', '_poz', 32]]]) # The position, after all arguments are processing, equals the total # length. Paste this in to make the output a proper bytearray seq.append(['mstore', placeholder, '_poz']) # Memory location of the output seq.append(placeholder) return LLLnode.from_list( ['with', '_poz', 0, ['seq'] + seq], typ=ReturnType(total_maxlen), location='memory', pos=getpos(expr), annotation='concat' )
def to_decimal(expr, args, kwargs, context): input = args[0] return LLLnode.from_list(['mul', input, DECIMAL_DIVISOR], typ=BaseType('decimal', input.typ.unit, input.typ.positional), pos=getpos(expr))
if denomination % den: raise InvalidLiteralException("Too many decimal places: %s" % numstring, expr.args[0]) sub = num * denomination // den elif args[0].typ.is_literal: if args[0].value <= 0: raise InvalidLiteralException("Negative wei value not allowed", expr) sub = ['mul', args[0].value, denomination] elif args[0].typ.typ == 'uint256': sub = ['mul', args[0], denomination] else: sub = ['div', ['mul', args[0], denomination], DECIMAL_DIVISOR] return LLLnode.from_list(sub, typ=BaseType('uint256', {'wei': 1}), location=None, pos=getpos(expr)) zero_value = LLLnode.from_list(0, typ=BaseType('uint256', {'wei': 1})) false_value = LLLnode.from_list(0, typ=BaseType('bool', is_literal=True)) @signature('address', 'bytes', outsize='num_literal', gas='uint256', value=Optional('uint256', zero_value), delegate_call=Optional('bool', false_value)) 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 TypeMismatchException('The delegate_call parameter has to be a static/literal boolean value.') if context.is_constant(): raise ConstancyViolationException("Cannot make calls from %s" % 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)
def to_int128(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) _unit = in_arg.typ.unit if input_type in ('uint256', 'decimal') else None if input_type == 'num_literal': if isinstance(in_arg, int): if not SizeLimits.in_bounds('int128', in_arg): raise InvalidLiteralException(f"Number out of range: {in_arg}") return LLLnode.from_list( in_arg, typ=BaseType('int128', _unit), pos=getpos(expr) ) elif isinstance(in_arg, float): if not SizeLimits.in_bounds('int128', math.trunc(in_arg)): raise InvalidLiteralException(f"Number out of range: {math.trunc(in_arg)}") return LLLnode.from_list( math.trunc(in_arg), typ=BaseType('int128', _unit), pos=getpos(expr) ) else: raise InvalidLiteralException(f"Unknown numeric literal type: {in_arg}") elif input_type == 'bytes32': if in_arg.typ.is_literal: if not SizeLimits.in_bounds('int128', in_arg.value): raise InvalidLiteralException(f"Number out of range: {in_arg.value}", expr) else: return LLLnode.from_list( in_arg, typ=BaseType('int128', _unit), pos=getpos(expr) ) else: return LLLnode.from_list( [ 'clamp', ['mload', MemoryPositions.MINNUM], in_arg, ['mload', MemoryPositions.MAXNUM], ], typ=BaseType('int128', _unit), pos=getpos(expr) ) elif input_type == 'address': return LLLnode.from_list( [ 'signextend', 15, [ 'and', in_arg, (SizeLimits.ADDRSIZE - 1) ], ], typ=BaseType('int128', _unit), pos=getpos(expr) ) elif input_type in ('string', 'bytes'): if in_arg.typ.maxlen > 32: raise TypeMismatchException( f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to int128", expr, ) return byte_array_to_num(in_arg, expr, 'int128') elif input_type == 'uint256': if in_arg.typ.is_literal: if not SizeLimits.in_bounds('int128', in_arg.value): raise InvalidLiteralException(f"Number out of range: {in_arg.value}", expr) else: return LLLnode.from_list( in_arg, typ=BaseType('int128', _unit), pos=getpos(expr) ) else: return LLLnode.from_list( ['uclample', in_arg, ['mload', MemoryPositions.MAXNUM]], typ=BaseType('int128', _unit), pos=getpos(expr) ) elif input_type == 'decimal': return LLLnode.from_list( [ 'clamp', ['mload', MemoryPositions.MINNUM], ['sdiv', in_arg, DECIMAL_DIVISOR], ['mload', MemoryPositions.MAXNUM], ], typ=BaseType('int128', _unit), pos=getpos(expr) ) elif input_type == 'bool': return LLLnode.from_list( in_arg, typ=BaseType('int128', _unit), pos=getpos(expr) ) else: raise InvalidLiteralException(f"Invalid input for int128: {in_arg}", expr)
def to_decimal(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) if input_type == 'bytes': if in_arg.typ.maxlen > 32: raise TypeMismatchException( f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to decimal", expr, ) num = byte_array_to_num(in_arg, expr, 'int128') return LLLnode.from_list( ['mul', num, DECIMAL_DIVISOR], typ=BaseType('decimal'), pos=getpos(expr) ) else: _unit = in_arg.typ.unit _positional = in_arg.typ.positional if input_type == 'uint256': if in_arg.typ.is_literal: if not SizeLimits.in_bounds('int128', (in_arg.value * DECIMAL_DIVISOR)): raise InvalidLiteralException( f"Number out of range: {in_arg.value}", expr, ) else: return LLLnode.from_list( ['mul', in_arg, DECIMAL_DIVISOR], typ=BaseType('decimal', _unit, _positional), pos=getpos(expr) ) else: return LLLnode.from_list( [ 'uclample', ['mul', in_arg, DECIMAL_DIVISOR], ['mload', MemoryPositions.MAXDECIMAL] ], typ=BaseType('decimal', _unit, _positional), pos=getpos(expr) ) elif input_type == 'address': return LLLnode.from_list( [ 'mul', [ 'signextend', 15, [ 'and', in_arg, (SizeLimits.ADDRSIZE - 1) ], ], DECIMAL_DIVISOR ], typ=BaseType('decimal', _unit, _positional), pos=getpos(expr) ) elif input_type == 'bytes32': if in_arg.typ.is_literal: if not SizeLimits.in_bounds('int128', (in_arg.value * DECIMAL_DIVISOR)): raise InvalidLiteralException( f"Number out of range: {in_arg.value}", expr, ) else: return LLLnode.from_list( ['mul', in_arg, DECIMAL_DIVISOR], typ=BaseType('decimal', _unit, _positional), pos=getpos(expr) ) else: return LLLnode.from_list( [ 'clamp', ['mload', MemoryPositions.MINDECIMAL], ['mul', in_arg, DECIMAL_DIVISOR], ['mload', MemoryPositions.MAXDECIMAL], ], typ=BaseType('decimal', _unit, _positional), pos=getpos(expr) ) elif input_type in ('int128', 'bool'): return LLLnode.from_list( ['mul', in_arg, DECIMAL_DIVISOR], typ=BaseType('decimal', _unit, _positional), pos=getpos(expr) ) else: raise InvalidLiteralException(f"Invalid input for decimal: {in_arg}", expr)
def to_uint256(expr, args, kwargs, context): in_arg = args[0] input_type, _ = get_type(in_arg) _unit = in_arg.typ.unit if input_type in ('int128', 'decimal') else None if input_type == 'num_literal': if isinstance(in_arg, int): if not SizeLimits.in_bounds('uint256', in_arg): raise InvalidLiteralException(f"Number out of range: {in_arg}") return LLLnode.from_list( in_arg, typ=BaseType('uint256', _unit), pos=getpos(expr) ) elif isinstance(in_arg, float): if not SizeLimits.in_bounds('uint256', math.trunc(in_arg)): raise InvalidLiteralException(f"Number out of range: {math.trunc(in_arg)}") return LLLnode.from_list( math.trunc(in_arg), typ=BaseType('uint256', _unit), pos=getpos(expr) ) else: raise InvalidLiteralException(f"Unknown numeric literal type: {in_arg}") elif isinstance(in_arg, LLLnode) and input_type == 'int128': return LLLnode.from_list( ['clampge', in_arg, 0], typ=BaseType('uint256', _unit), pos=getpos(expr) ) elif isinstance(in_arg, LLLnode) and input_type == 'decimal': return LLLnode.from_list( ['div', ['clampge', in_arg, 0], DECIMAL_DIVISOR], typ=BaseType('uint256', _unit), pos=getpos(expr) ) elif isinstance(in_arg, LLLnode) and input_type == 'bool': return LLLnode.from_list( in_arg, typ=BaseType('uint256'), pos=getpos(expr) ) elif isinstance(in_arg, LLLnode) and input_type in ('bytes32', 'address'): return LLLnode( value=in_arg.value, args=in_arg.args, typ=BaseType('uint256'), pos=getpos(expr) ) elif isinstance(in_arg, LLLnode) and input_type == 'bytes': if in_arg.typ.maxlen > 32: raise InvalidLiteralException( f"Cannot convert bytes array of max length {in_arg.typ.maxlen} to uint256", expr, ) return byte_array_to_num(in_arg, expr, 'uint256') else: raise InvalidLiteralException(f"Invalid input for uint256: {in_arg}", expr)
def as_unitless_number(expr, args, kwargs, context): return LLLnode(value=args[0].value, args=args[0].args, typ=BaseType(args[0].typ.typ, {}), pos=getpos(expr))
def avo(arg, ind, pos): return unwrap_location(add_variable_offset(arg, LLLnode.from_list(ind, 'int128'), pos=pos))
def parse_docblock(self): if '"""' not in self.context.origcode.splitlines()[self.stmt.lineno - 1]: raise InvalidLiteralException('Only valid """ docblocks allowed', self.stmt) return LLLnode.from_list('pass', typ=None, pos=getpos(self.stmt))
def send(expr, args, kwargs, context): to, value = args if context.is_constant(): raise ConstancyViolationException("Cannot send ether inside %s!" % 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 call(self): is_self_function = (isinstance( self.stmt.func, ast.Attribute)) and isinstance( self.stmt.func.value, ast.Name) and self.stmt.func.value.id == "self" is_log_call = (isinstance( self.stmt.func, ast.Attribute)) and isinstance( self.stmt.func.value, ast.Name) and self.stmt.func.value.id == 'log' if isinstance(self.stmt.func, ast.Name): if self.stmt.func.id in stmt_dispatch_table: if self.stmt.func.id == 'clear': return self._clear() else: return stmt_dispatch_table[self.stmt.func.id](self.stmt, self.context) elif self.stmt.func.id in dispatch_table: raise StructureException( f"Function {self.stmt.func.id} can not be called without being used.", self.stmt, ) else: raise StructureException( f"Unknown function: '{self.stmt.func.id}'.", self.stmt, ) elif is_self_function: return self_call.make_call(self.stmt, self.context) elif is_log_call: if self.stmt.func.attr not in self.context.sigs['self']: raise EventDeclarationException( f"Event not declared yet: {self.stmt.func.attr}") event = self.context.sigs['self'][self.stmt.func.attr] if len(event.indexed_list) != len(self.stmt.args): raise EventDeclarationException( f"{event.name} received {len(self.stmt.args)} arguments but " f"expected {len(event.indexed_list)}") expected_topics, topics = [], [] expected_data, data = [], [] for pos, is_indexed in enumerate(event.indexed_list): if is_indexed: expected_topics.append(event.args[pos]) topics.append(self.stmt.args[pos]) else: expected_data.append(event.args[pos]) data.append(self.stmt.args[pos]) topics = pack_logging_topics( event.event_id, topics, expected_topics, self.context, pos=getpos(self.stmt), ) inargs, inargsize, inargsize_node, inarg_start = pack_logging_data( expected_data, data, self.context, pos=getpos(self.stmt), ) if inargsize_node is None: sz = inargsize else: sz = ['mload', inargsize_node] return LLLnode.from_list([ 'seq', inargs, LLLnode.from_list( ["log" + str(len(topics)), inarg_start, sz] + topics, add_gas_estimate=inargsize * 10, ) ], typ=None, pos=getpos(self.stmt)) else: return external_call.make_external_call(self.stmt, self.context)
def selfdestruct(expr, args, kwargs, context): if context.is_constant(): raise ConstancyViolationException("Cannot %s inside %s!" % (expr.func.id, context.pp_constancy()), expr.func) return LLLnode.from_list(['selfdestruct', args[0]], typ=None, pos=getpos(expr))
def parse_for(self): # from .parser import ( # parse_body, # ) # Type 0 for, e.g. for i in list(): ... if self._is_list_iter(): return self.parse_for_list() is_invalid_for_statement = any(( not isinstance(self.stmt.iter, ast.Call), not isinstance(self.stmt.iter.func, ast.Name), not isinstance(self.stmt.target, ast.Name), self.stmt.iter.func.id != "range", len(self.stmt.iter.args) not in {1, 2}, )) if is_invalid_for_statement: raise StructureException( ("For statements must be of the form `for i in range(rounds): " "..` or `for i in range(start, start + rounds): ..`"), self.stmt.iter) block_scope_id = id(self.stmt) with self.context.make_blockscope(block_scope_id): # Get arg0 arg0 = self.stmt.iter.args[0] num_of_args = len(self.stmt.iter.args) # Type 1 for, e.g. for i in range(10): ... if num_of_args == 1: arg0_val = self._get_range_const_value(arg0) start = LLLnode.from_list(0, typ='int128', pos=getpos(self.stmt)) rounds = arg0_val # Type 2 for, e.g. for i in range(100, 110): ... elif self._check_valid_range_constant(self.stmt.iter.args[1], raise_exception=False)[0]: arg0_val = self._get_range_const_value(arg0) arg1_val = self._get_range_const_value(self.stmt.iter.args[1]) start = LLLnode.from_list(arg0_val, typ='int128', pos=getpos(self.stmt)) rounds = LLLnode.from_list(arg1_val - arg0_val, typ='int128', pos=getpos(self.stmt)) # Type 3 for, e.g. for i in range(x, x + 10): ... else: arg1 = self.stmt.iter.args[1] if not isinstance(arg1, ast.BinOp) or not isinstance( arg1.op, ast.Add): raise StructureException( ("Two-arg for statements must be of the form `for i " "in range(start, start + rounds): ...`"), arg1, ) if arg0 != arg1.left: raise StructureException( ("Two-arg for statements of the form `for i in " "range(x, x + y): ...` must have x identical in both " f"places: {ast_to_dict(arg0)} {ast_to_dict(arg1.left)}" ), self.stmt.iter, ) rounds = self._get_range_const_value(arg1.right) start = Expr.parse_value_expr(arg0, self.context) varname = self.stmt.target.id pos = self.context.new_variable(varname, BaseType('int128'), pos=getpos(self.stmt)) self.context.forvars[varname] = True o = LLLnode.from_list( [ 'repeat', pos, start, rounds, parse_body(self.stmt.body, self.context) ], typ=None, pos=getpos(self.stmt), ) del self.context.vars[varname] del self.context.forvars[varname] return o
def blockhash(expr, args, kwargs, contact): return LLLnode.from_list(['blockhash', ['uclamplt', ['clampge', args[0], ['sub', ['number'], 256]], 'number']], typ=BaseType('bytes32'), pos=getpos(expr))
def aug_assign(self): target = self.get_target(self.stmt.target) sub = Expr.parse_value_expr(self.stmt.value, self.context) if not isinstance(self.stmt.op, (ast.Add, ast.Sub, ast.Mult, ast.Div, ast.Mod)): raise StructureException("Unsupported operator for augassign", self.stmt) if not isinstance(target.typ, BaseType): raise TypeMismatchException( "Can only use aug-assign operators with simple types!", self.stmt.target) if target.location == 'storage': o = Expr.parse_value_expr( ast.BinOp( left=LLLnode.from_list(['sload', '_stloc'], typ=target.typ, pos=target.pos), right=sub, op=self.stmt.op, lineno=self.stmt.lineno, col_offset=self.stmt.col_offset, end_lineno=self.stmt.end_lineno, end_col_offset=self.stmt.end_col_offset, ), self.context, ) return LLLnode.from_list([ 'with', '_stloc', target, [ 'sstore', '_stloc', base_type_conversion( o, o.typ, target.typ, pos=getpos(self.stmt)), ], ], typ=None, pos=getpos(self.stmt)) elif target.location == 'memory': o = Expr.parse_value_expr( ast.BinOp( left=LLLnode.from_list(['mload', '_mloc'], typ=target.typ, pos=target.pos), right=sub, op=self.stmt.op, lineno=self.stmt.lineno, col_offset=self.stmt.col_offset, end_lineno=self.stmt.end_lineno, end_col_offset=self.stmt.end_col_offset, ), self.context, ) return LLLnode.from_list([ 'with', '_mloc', target, [ 'mstore', '_mloc', base_type_conversion( o, o.typ, target.typ, pos=getpos(self.stmt)), ], ], typ=None, pos=getpos(self.stmt))
def _RLPlist(expr, args, kwargs, context): # Second argument must be a list of types if not isinstance(args[1], ast.List): raise TypeMismatchException("Expecting list of types for second argument", args[1]) if len(args[1].elts) == 0: raise TypeMismatchException("RLP list must have at least one item", expr) if len(args[1].elts) > 32: raise TypeMismatchException("RLP list must have at most 32 items", expr) # Get the output format _format = [] for arg in args[1].elts: if isinstance(arg, ast.Name) and arg.id == "bytes": subtyp = ByteArrayType(args[0].typ.maxlen) else: subtyp = context.parse_type(arg, 'memory') if not isinstance(subtyp, BaseType): raise TypeMismatchException("RLP lists only accept BaseTypes and byte arrays", arg) if not is_base_type(subtyp, ('int128', 'uint256', 'bytes32', 'address', 'bool')): raise TypeMismatchException("Unsupported base type: %s" % subtyp.typ, arg) _format.append(subtyp) output_type = TupleType(_format) output_placeholder_type = ByteArrayType((2 * len(_format) + 1 + get_size_of_type(output_type)) * 32) output_placeholder = context.new_placeholder(output_placeholder_type) output_node = LLLnode.from_list(output_placeholder, typ=output_placeholder_type, location='memory') # Create a decoder for each element in the tuple decoder = [] for i, typ in enumerate(_format): # Decoder for bytes32 if is_base_type(typ, 'bytes32'): decoder.append(LLLnode.from_list( ['seq', ['assert', ['eq', ['mload', ['add', output_node, ['mload', ['add', output_node, 32 * i]]]], 32]], ['mload', ['add', 32, ['add', output_node, ['mload', ['add', output_node, 32 * i]]]]]], typ, annotation='getting and checking bytes32 item')) # Decoder for address elif is_base_type(typ, 'address'): decoder.append(LLLnode.from_list( ['seq', ['assert', ['eq', ['mload', ['add', output_node, ['mload', ['add', output_node, 32 * i]]]], 20]], ['mod', ['mload', ['add', 20, ['add', output_node, ['mload', ['add', output_node, 32 * i]]]]], ['mload', MemoryPositions.ADDRSIZE]]], typ, annotation='getting and checking address item')) # Decoder for bytes elif isinstance(typ, ByteArrayType): decoder.append(LLLnode.from_list( ['add', output_node, ['mload', ['add', output_node, 32 * i]]], typ, location='memory', annotation='getting byte array')) # Decoder for num and uint256 elif is_base_type(typ, ('int128', 'uint256')): bytez = LLLnode.from_list( ['add', output_node, ['mload', ['add', output_node, 32 * i]]], typ, location='memory', annotation='getting and checking %s' % typ.typ) decoder.append(byte_array_to_num(bytez, expr, typ.typ)) # Decoder for bools elif is_base_type(typ, ('bool')): # This is basically a really clever way to test for a length-prefixed one or zero. We take the 32 bytes # starting one byte *after* the start of the length declaration; this includes the last 31 bytes of the # length and the first byte of the value. 0 corresponds to length 0, first byte 0, and 257 corresponds # to length 1, first byte \x01 decoder.append(LLLnode.from_list( ['with', '_ans', ['mload', ['add', 1, ['add', output_node, ['mload', ['add', output_node, 32 * i]]]]], ['seq', ['assert', ['or', ['eq', '_ans', 0], ['eq', '_ans', 257]]], ['div', '_ans', 257]]], typ, annotation='getting and checking bool')) else: # Should never reach because of top level base level check. raise Exception("Type not yet supported") # pragma: no cover # Copy the input data to memory if args[0].location == "memory": variable_pointer = args[0] elif args[0].location == "storage": placeholder = context.new_placeholder(args[0].typ) placeholder_node = LLLnode.from_list(placeholder, typ=args[0].typ, location='memory') copier = make_byte_array_copier(placeholder_node, LLLnode.from_list('_ptr', typ=args[0].typ, location=args[0].location)) variable_pointer = ['with', '_ptr', args[0], ['seq', copier, placeholder_node]] else: # Should never reach because of top level base level check. raise Exception("Location not yet supported") # pragma: no cover # Decode the input data initial_setter = LLLnode.from_list( ['seq', ['with', '_sub', variable_pointer, ['pop', ['call', 1500 + 400 * len(_format) + 10 * len(args), LLLnode.from_list(RLP_DECODER_ADDRESS, annotation='RLP decoder'), 0, ['add', '_sub', 32], ['mload', '_sub'], output_node, 64 * len(_format) + 32 + 32 * get_size_of_type(output_type)]]], ['assert', ['eq', ['mload', output_node], 32 * len(_format) + 32]]], typ=None) # Shove the input data decoder in front of the first variable decoder decoder[0] = LLLnode.from_list(['seq', initial_setter, decoder[0]], typ=decoder[0].typ, location=decoder[0].location) return LLLnode.from_list(["multi"] + decoder, typ=output_type, location='memory', pos=getpos(expr))
def parse_break(self): return LLLnode.from_list('break', typ=None, pos=getpos(self.stmt))
def uint256_mulmod(expr, args, kwargs, context): return LLLnode.from_list(['seq', ['assert', args[2]], ['assert', ['or', ['iszero', args[0]], ['eq', ['div', ['mul', args[0], args[1]], args[0]], args[1]]]], ['mulmod', args[0], args[1], args[2]]], typ=BaseType('uint256'), pos=getpos(expr))
def parse_pass(self): return LLLnode.from_list('pass', typ=None, pos=getpos(self.stmt))
def bitwise_not(expr, args, kwargs, context): return LLLnode.from_list(['not', args[0]], typ=BaseType('uint256'), pos=getpos(expr))
def compile_vyper_lll(vyper_code: List[Any]) -> Tuple[bytes, Dict[str, Any]]: lll_node = LLLnode.from_list(vyper_code) assembly = compile_to_assembly(lll_node) code = assembly_to_evm(assembly) return code
def method_id(expr, args, kwargs, context): method_id = fourbytes_to_int(sha3(args[0])[:4]) return LLLnode(method_id, typ=BaseType('method_id'), pos=getpos(expr))