def base_type_conversion(orig, frm, to, pos=None): orig = unwrap_location(orig) if not isinstance(frm, (BaseType, NullType)) or not isinstance(to, BaseType): raise TypeMismatchException( "Base type conversion from or to non-base type: %r %r" % (frm, to), pos) elif is_base_type(frm, to.typ) and are_units_compatible(frm, to): return LLLnode(orig.value, orig.args, typ=to, add_gas_estimate=orig.add_gas_estimate) elif is_base_type(frm, 'num') and is_base_type( to, 'decimal') and are_units_compatible(frm, to): return LLLnode.from_list(['mul', orig, DECIMAL_DIVISOR], typ=BaseType('decimal', to.unit, to.positional)) elif is_base_type(frm, 'num256') and is_base_type( to, 'num') and are_units_compatible(frm, to): return LLLnode.from_list( ['uclample', orig, ['mload', MemoryPositions.MAXNUM]], typ=BaseType("num")) elif isinstance(frm, NullType): if to.typ not in ('num', 'bool', 'num256', 'address', 'bytes32', 'decimal'): raise TypeMismatchException( "Cannot convert null-type object to type %r" % to, pos) return LLLnode.from_list(0, typ=to) else: raise TypeMismatchException( "Typecasting from base type %r to %r unavailable" % (frm, to), pos)
def attribute(self): # x.balance: balance of address x if self.expr.attr == 'balance': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatchException("Type mismatch: balance keyword expects an address as input", self.expr) return LLLnode.from_list(['balance', addr], typ=BaseType('num', {'wei': 1}), location=None, pos=getpos(self.expr)) # x.codesize: codesize of address x elif self.expr.attr == 'codesize' or self.expr.attr == 'is_contract': addr = Expr.parse_value_expr(self.expr.value, self.context) if not is_base_type(addr.typ, 'address'): raise TypeMismatchException("Type mismatch: codesize keyword expects an address as input", self.expr) if self.expr.attr == 'codesize': output_type = 'num' else: output_type = 'bool' return LLLnode.from_list(['extcodesize', addr], typ=BaseType(output_type), location=None, pos=getpos(self.expr)) # self.x: global attribute elif isinstance(self.expr.value, ast.Name) and self.expr.value.id == "self": if self.expr.attr not in self.context.globals: raise VariableDeclarationException("Persistent variable undeclared: " + self.expr.attr, self.expr) var = self.context.globals[self.expr.attr] return LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.attr) # Reserved keywords elif isinstance(self.expr.value, ast.Name) and self.expr.value.id in ("msg", "block", "tx"): key = self.expr.value.id + "." + self.expr.attr if key == "msg.sender": return LLLnode.from_list(['caller'], typ='address', pos=getpos(self.expr)) elif key == "msg.value": if not self.context.is_payable: raise NonPayableViolationException("Cannot use msg.value in a non-payable function", self.expr) return LLLnode.from_list(['callvalue'], typ=BaseType('num', {'wei': 1}), pos=getpos(self.expr)) elif key == "msg.gas": return LLLnode.from_list(['gas'], typ='num', pos=getpos(self.expr)) elif key == "block.difficulty": return LLLnode.from_list(['difficulty'], typ='num', pos=getpos(self.expr)) elif key == "block.timestamp": return LLLnode.from_list(['timestamp'], typ=BaseType('num', {'sec': 1}, True), pos=getpos(self.expr)) elif key == "block.coinbase": return LLLnode.from_list(['coinbase'], typ='address', pos=getpos(self.expr)) elif key == "block.number": return LLLnode.from_list(['number'], typ='num', pos=getpos(self.expr)) elif key == "block.prevhash": return LLLnode.from_list(['blockhash', ['sub', 'number', 1]], typ='bytes32', pos=getpos(self.expr)) elif key == "tx.origin": return LLLnode.from_list(['origin'], typ='address', pos=getpos(self.expr)) else: raise Exception("Unsupported keyword: " + key) # Other variables else: sub = Expr.parse_variable_location(self.expr.value, self.context) if not isinstance(sub.typ, StructType): raise TypeMismatchException("Type mismatch: member variable access not expected", self.expr.value) attrs = sorted(sub.typ.members.keys()) if self.expr.attr not in attrs: raise TypeMismatchException("Member %s not found. Only the following available: %s" % (self.expr.attr, " ".join(attrs)), self.expr) return add_variable_offset(sub, self.expr.attr)
def pack_logging_data(expected_data, args, context): # Checks to see if there's any data if not args: return ['seq'], 0, None, 0 holder = ['seq'] maxlen = len(args) * 32 # total size of all packed args (upper limit) dynamic_offset_counter = context.new_placeholder(BaseType(32)) dynamic_placeholder = context.new_placeholder(BaseType(32)) # Populate static placeholders. placeholder_map = {} for i, (arg, data) in enumerate(zip(args, expected_data)): typ = data.typ placeholder = context.new_placeholder(BaseType(32)) placeholder_map[i] = placeholder if not isinstance(typ, ByteArrayType): holder, maxlen = pack_args_by_32(holder, maxlen, arg, typ, context, placeholder) # Dynamic position starts right after the static args. holder.append(LLLnode.from_list(['mstore', dynamic_offset_counter, maxlen])) # Calculate maximum dynamic offset placeholders, used for gas estimation. for i, (arg, data) in enumerate(zip(args, expected_data)): typ = data.typ if isinstance(typ, ByteArrayType): maxlen += 32 + ceil32(typ.maxlen) # Obtain the start of the arg section. if isinstance(expected_data[0].typ, ListType): datamem_start = holder[1].to_list()[1][0] else: datamem_start = dynamic_placeholder + 32 # Copy necessary data into allocated dynamic section. for i, (arg, data) in enumerate(zip(args, expected_data)): typ = data.typ pack_args_by_32(holder=holder, maxlen=maxlen, arg=arg, typ=typ, context=context, placeholder=placeholder_map[i], datamem_start=datamem_start, dynamic_offset_counter=dynamic_offset_counter) return holder, maxlen, dynamic_offset_counter, datamem_start
def from_list(cls, obj, typ=None, location=None, pos=None, annotation=None, mutable=True, add_gas_estimate=0): if isinstance(typ, str): typ = BaseType(typ) if isinstance(obj, LLLnode): if obj.pos is None: obj.pos = pos if obj.location is None: obj.location = location return obj elif not isinstance(obj, list): return cls(obj, [], typ, location, pos, annotation, mutable, add_gas_estimate=add_gas_estimate) else: return cls(obj[0], [cls.from_list(o, pos=pos) for o in obj[1:]], typ, location, pos, annotation, mutable, add_gas_estimate=add_gas_estimate)
def pack_args_by_32(holder, maxlen, arg, typ, context, placeholder): if isinstance(typ, BaseType): input = parse_expr(arg, context) input = base_type_conversion(input, input.typ, typ) holder.append( LLLnode.from_list(['mstore', placeholder, input], typ=typ, location='memory')) elif isinstance(typ, ByteArrayType): bytez = b'' # String literals if isinstance(arg, ast.Str): if len(arg.s) > typ.maxlen: raise TypeMismatchException( "Data input bytes are to big: %r %r" % (len(arg.s), typ)) for c in arg.s: if ord(c) >= 256: raise InvalidLiteralException( "Cannot insert special character %r into byte array" % c) bytez += bytes([ord(c)]) bytez_length = len(bytez) if len(bytez) > 32: raise InvalidLiteralException( "Can only log a maximum of 32 bytes at a time.") holder.append( LLLnode.from_list([ 'mstore', placeholder, bytes_to_int(bytez + b'\x00' * (32 - bytez_length)) ], typ=typ, location='memory')) # Variables else: input = parse_expr(arg, context) if input.typ.maxlen > typ.maxlen: raise TypeMismatchException( "Data input bytes are to big: %r %r" % (input.typ, typ)) if arg.id in context.vars: size = context.vars[arg.id].size holder.append( LLLnode.from_list([ 'mstore', placeholder, byte_array_to_num(parse_expr(arg, context), arg, 'num256', size) ], typ=typ, location='memory')) elif isinstance(typ, ListType): maxlen += (typ.count - 1) * 32 typ = typ.subtype holder, maxlen = pack_args_by_32(holder, maxlen, arg.elts[0], typ, context, placeholder) for j, arg2 in enumerate(arg.elts[1:]): holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32))) return holder, maxlen
def byte_array_to_num( arg, expr, out_type, offset=32, ): if arg.location == "memory": lengetter = LLLnode.from_list(['mload', '_sub'], typ=BaseType('num')) first_el_getter = LLLnode.from_list(['mload', ['add', 32, '_sub']], typ=BaseType('num')) elif arg.location == "storage": lengetter = LLLnode.from_list(['sload', ['sha3_32', '_sub']], typ=BaseType('num')) first_el_getter = LLLnode.from_list( ['sload', ['add', 1, ['sha3_32', '_sub']]], typ=BaseType('num')) if out_type == 'num': result = [ 'clamp', ['mload', MemoryPositions.MINNUM], ['div', '_el1', ['exp', 256, ['sub', 32, '_len']]], ['mload', MemoryPositions.MAXNUM] ] elif out_type == 'num256': result = ['div', '_el1', ['exp', 256, ['sub', offset, '_len']]] return LLLnode.from_list( [ 'with', '_sub', arg, [ 'with', '_el1', first_el_getter, [ 'with', '_len', ['clamp', 0, lengetter, 32], [ 'seq', [ 'assert', [ 'or', ['iszero', '_len'], ['div', '_el1', ['exp', 256, 31]] ] ], result ] ] ] ], typ=BaseType(out_type), annotation='bytearray to number, verify no leading zbytes')
def add_globals_and_events(_contracts, _defs, _events, _getters, _globals, item): if item.value is not None: raise StructureException('May not assign value whilst defining type', item) elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "__log__": if _globals or len(_defs): raise StructureException( "Events must all come before global declarations and function definitions", item) _events.append(item) elif not isinstance(item.target, ast.Name): raise StructureException( "Can only assign type to variable in top-level statement", item) # Check if variable name is reserved or invalid elif not is_varname_valid(item.target.id): raise VariableDeclarationException( "Variable name invalid or reserved: ", item.target) # Check if global already exists, if so error elif item.target.id in _globals: raise VariableDeclarationException( "Cannot declare a persistent variable twice!", item.target) elif len(_defs): raise StructureException( "Global variables must all come before function definitions", item) # If the type declaration is of the form public(<type here>), then proceed with # the underlying type but also add getters elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "address": if len(item.annotation.args) != 1: raise StructureException("Address expects one arg (the type)") if item.annotation.args[0].id not in premade_contracts: raise VariableDeclarationException( "Unsupported premade contract declaration", item.annotation.args[0]) premade_contract = premade_contracts[item.annotation.args[0].id] _contracts[item.target.id] = add_contract(premade_contract.body) _globals[item.target.id] = VariableRecord(item.target.id, len(_globals), BaseType('address'), True) elif isinstance(item.annotation, ast.Call) and item.annotation.func.id == "public": if len(item.annotation.args) != 1: raise StructureException("Public expects one arg (the type)") typ = parse_type(item.annotation.args[0], 'storage') _globals[item.target.id] = VariableRecord(item.target.id, len(_globals), typ, True) # Adding getters here for getter in mk_getter(item.target.id, typ): _getters.append(parse_line('\n' * (item.lineno - 1) + getter)) _getters[-1].pos = getpos(item) else: _globals[item.target.id] = VariableRecord( item.target.id, len(_globals), parse_type(item.annotation, 'storage'), True) return _contracts, _events, _globals, _getters
def build_in_comparator(self): from viper.parser.parser import make_setter left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.comparators[0], self.context).lll_node result_placeholder = self.context.new_placeholder(BaseType('bool')) setter = [] # Load nth item from list in memory. if right.value == 'multi': # Copy literal to memory to be compared. tmp_list = LLLnode.from_list( obj=self.context.new_placeholder(ListType(right.typ.subtype, right.typ.count)), typ=ListType(right.typ.subtype, right.typ.count), location='memory' ) setter = make_setter(tmp_list, right, 'memory') load_i_from_list = ['mload', ['add', tmp_list, ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]]]] elif right.location == "storage": load_i_from_list = ['sload', ['add', ['sha3_32', right], ['mload', MemoryPositions.FREE_LOOP_INDEX]]] else: load_i_from_list = ['mload', ['add', right, ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]]]] # Condition repeat loop has to break on. break_loop_condition = [ 'if', ['eq', unwrap_location(left), load_i_from_list], ['seq', ['mstore', '_result', 1], # store true. 'break'] ] # Repeat loop to loop-compare each item in the list. for_loop_sequence = [ ['mstore', result_placeholder, 0], ['with', '_result', result_placeholder, ['repeat', MemoryPositions.FREE_LOOP_INDEX, 0, right.typ.count, break_loop_condition]], ['mload', result_placeholder] ] # Save list to memory, so one can iterate over it, # used when literal was created with tmp_list. if setter: compare_sequence = ['seq', setter] + for_loop_sequence else: compare_sequence = ['seq'] + for_loop_sequence # Compare the result of the repeat loop to 1, to know if a match was found. o = LLLnode.from_list([ 'eq', 1, compare_sequence], typ='bool', annotation="in comporator" ) return o
def pack_logging_data(types, args, context): # Checks to see if there's any data if not args: return ['seq'], 0, 0 holder = ['seq'] maxlen = len(args) * 32 for i, (arg, typ) in enumerate(zip(args, types)): holder, maxlen = pack_args_by_32(holder, maxlen, arg, typ, context, context.new_placeholder(BaseType(32))) return holder, maxlen, holder[1].to_list()[1][0]
def number(self): orignum = get_original_if_0x_prefixed(self.expr, self.context) if orignum is None and isinstance(self.expr.n, int): if not (SizeLimits.MINNUM <= self.expr.n <= SizeLimits.MAXNUM): raise InvalidLiteralException( "Number out of range: " + str(self.expr.n), self.expr) return LLLnode.from_list(self.expr.n, typ=BaseType('num', None), pos=getpos(self.expr)) elif isinstance(self.expr.n, float): numstring, num, den = get_number_as_fraction( self.expr, self.context) if not (SizeLimits.MINNUM * den < num < SizeLimits.MAXNUM * den): raise InvalidLiteralException( "Number out of range: " + numstring, self.expr) if DECIMAL_DIVISOR % den: raise InvalidLiteralException( "Too many decimal places: " + numstring, self.expr) return LLLnode.from_list(num * DECIMAL_DIVISOR // den, typ=BaseType('decimal', None), pos=getpos(self.expr)) elif len(orignum) == 42: if checksum_encode(orignum) != orignum: raise InvalidLiteralException( "Address checksum mismatch. If you are sure this is the " "right address, the correct checksummed form is: " + checksum_encode(orignum), self.expr) return LLLnode.from_list(self.expr.n, typ=BaseType('address'), pos=getpos(self.expr)) elif len(orignum) == 66: return LLLnode.from_list(self.expr.n, typ=BaseType('bytes32'), pos=getpos(self.expr)) else: raise InvalidLiteralException( "Cannot read 0x value with length %d. Expecting 42 (address incl 0x) or 66 (bytes32 incl 0x)" % len(orignum), self.expr)
def arithmetic(self): left = Expr.parse_value_expr(self.expr.left, self.context) right = Expr.parse_value_expr(self.expr.right, self.context) if not is_numeric_type(left.typ) or not is_numeric_type(right.typ): raise TypeMismatchException( "Unsupported types for arithmetic op: %r %r" % (left.typ, right.typ), self.expr) ltyp, rtyp = left.typ.typ, right.typ.typ if isinstance(self.expr.op, (ast.Add, ast.Sub)): if left.typ.unit != right.typ.unit and left.typ.unit is not None and right.typ.unit is not None: raise TypeMismatchException( "Unit mismatch: %r %r" % (left.typ.unit, right.typ.unit), self.expr) if left.typ.positional and right.typ.positional and isinstance( self.expr.op, ast.Add): raise TypeMismatchException("Cannot add two positional units!", self.expr) new_unit = left.typ.unit or right.typ.unit new_positional = left.typ.positional ^ right.typ.positional # xor, as subtracting two positionals gives a delta op = 'add' if isinstance(self.expr.op, ast.Add) else 'sub' if ltyp == rtyp: o = LLLnode.from_list([op, left, right], typ=BaseType(ltyp, new_unit, new_positional), pos=getpos(self.expr)) elif ltyp == 'num' and rtyp == 'decimal': o = LLLnode.from_list( [op, ['mul', left, DECIMAL_DIVISOR], right], typ=BaseType('decimal', new_unit, new_positional), pos=getpos(self.expr)) elif ltyp == 'decimal' and rtyp == 'num': o = LLLnode.from_list( [op, left, ['mul', right, DECIMAL_DIVISOR]], typ=BaseType('decimal', new_unit, new_positional), pos=getpos(self.expr)) else: raise Exception("How did I get here? %r %r" % (ltyp, rtyp)) elif isinstance(self.expr.op, ast.Mult): if left.typ.positional or right.typ.positional: raise TypeMismatchException( "Cannot multiply positional values!", self.expr) new_unit = combine_units(left.typ.unit, right.typ.unit) if ltyp == rtyp == 'num': o = LLLnode.from_list(['mul', left, right], typ=BaseType('num', new_unit), pos=getpos(self.expr)) elif ltyp == rtyp == 'decimal': o = LLLnode.from_list([ 'with', 'r', right, [ 'with', 'l', left, [ 'with', 'ans', ['mul', 'l', 'r'], [ 'seq', [ 'assert', [ 'or', [ 'eq', ['sdiv', 'ans', 'l'], 'r' ], ['not', 'l'] ] ], ['sdiv', 'ans', DECIMAL_DIVISOR] ] ] ] ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr)) elif (ltyp == 'num' and rtyp == 'decimal') or (ltyp == 'decimal' and rtyp == 'num'): o = LLLnode.from_list([ 'with', 'r', right, [ 'with', 'l', left, [ 'with', 'ans', ['mul', 'l', 'r'], [ 'seq', [ 'assert', [ 'or', [ 'eq', ['sdiv', 'ans', 'l'], 'r' ], ['not', 'l'] ] ], 'ans' ] ] ] ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr)) elif isinstance(self.expr.op, ast.Div): if left.typ.positional or right.typ.positional: raise TypeMismatchException("Cannot divide positional values!", self.expr) new_unit = combine_units(left.typ.unit, right.typ.unit, div=True) if rtyp == 'num': o = LLLnode.from_list(['sdiv', left, ['clamp_nonzero', right]], typ=BaseType(ltyp, new_unit), pos=getpos(self.expr)) elif ltyp == rtyp == 'decimal': o = LLLnode.from_list([ 'with', 'l', left, [ 'with', 'r', ['clamp_nonzero', right], ['sdiv', ['mul', 'l', DECIMAL_DIVISOR], 'r'] ] ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr)) elif ltyp == 'num' and rtyp == 'decimal': o = LLLnode.from_list([ 'sdiv', ['mul', left, DECIMAL_DIVISOR**2], ['clamp_nonzero', right] ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr)) elif isinstance(self.expr.op, ast.Mod): if left.typ.positional or right.typ.positional: raise TypeMismatchException( "Cannot use positional values as modulus arguments!", self.expr) if left.typ.unit != right.typ.unit and left.typ.unit is not None and right.typ.unit is not None: raise TypeMismatchException( "Modulus arguments must have same unit", self.expr) new_unit = left.typ.unit or right.typ.unit if ltyp == rtyp: o = LLLnode.from_list(['smod', left, ['clamp_nonzero', right]], typ=BaseType(ltyp, new_unit), pos=getpos(self.expr)) elif ltyp == 'decimal' and rtyp == 'num': o = LLLnode.from_list([ 'smod', left, ['mul', ['clamp_nonzero', right], DECIMAL_DIVISOR] ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr)) elif ltyp == 'num' and rtyp == 'decimal': o = LLLnode.from_list([ 'smod', ['mul', left, DECIMAL_DIVISOR], ['clamp_nonzero', right] ], typ=BaseType('decimal', new_unit), pos=getpos(self.expr)) elif isinstance(self.expr.op, ast.Pow): if left.typ.positional or right.typ.positional: raise TypeMismatchException( "Cannot use positional values as exponential arguments!", self.expr) if right.typ.unit: raise TypeMismatchException( "Cannot use unit values as exponents", self.expr) if ltyp != 'num' and isinstance(self.expr.right, ast.Name): raise TypeMismatchException( "Cannot use dynamic values as exponents, for unit base types", self.expr) if ltyp == rtyp == 'num': new_unit = left.typ.unit if left.typ.unit and not isinstance(self.expr.right, ast.Name): new_unit = { left.typ.unit.copy().popitem()[0]: self.expr.right.n } o = LLLnode.from_list(['exp', left, right], typ=BaseType('num', new_unit), pos=getpos(self.expr)) else: raise TypeMismatchException( 'Only whole number exponents are supported', self.expr) else: raise Exception("Unsupported binop: %r" % self.expr.op) if o.typ.typ == 'num': return LLLnode.from_list([ 'clamp', ['mload', MemoryPositions.MINNUM], o, ['mload', MemoryPositions.MAXNUM] ], typ=o.typ, pos=getpos(self.expr)) elif o.typ.typ == 'decimal': return LLLnode.from_list([ 'clamp', ['mload', MemoryPositions.MINDECIMAL], o, ['mload', MemoryPositions.MAXDECIMAL] ], typ=o.typ, pos=getpos(self.expr)) else: raise Exception("%r %r" % (o, o.typ))
def parse_for_list(self): from .parser import (parse_body, make_setter) 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)) i_pos = self.context.new_variable('_index_for_' + varname, BaseType(subtype)) self.context.forvars[varname] = True if iter_var_type: # Is a list that is already allocated to memory. self.context.set_in_for_loop( self.stmt.iter.id ) # make sure list cannot be altered whilst iterating. iter_var = self.context.vars.get(self.stmt.iter.id) body = [ 'seq', [ 'mstore', value_pos, [ 'mload', ['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)) self.context.remove_in_for_loop(self.stmt.iter.id) elif isinstance(self.stmt.iter, ast.List): # List gets defined in the for statement. # 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') 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)) elif isinstance(self.stmt.iter, ast.Attribute): # List is contained in storage. count = iter_list_node.typ.count self.context.set_in_for_loop( iter_list_node.annotation ) # make sure list cannot be altered whilst iterating. 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)) self.context.remove_in_for_loop(iter_list_node.annotation) del self.context.vars[varname] del self.context.vars['_index_for_' + varname] del self.context.forvars[varname] return o
def add_variable_offset(parent, key): typ, location = parent.typ, parent.location if isinstance(typ, (StructType, TupleType)): if isinstance(typ, StructType): if not isinstance(key, str): raise TypeMismatchException("Expecting a member variable access; cannot access element %r" % key) if key not in typ.members: raise TypeMismatchException("Object does not have member variable %s" % key) subtype = typ.members[key] attrs = sorted(typ.members.keys()) if key not in attrs: raise TypeMismatchException("Member %s not found. Only the following available: %s" % (key, " ".join(attrs))) index = attrs.index(key) annotation = key else: if not isinstance(key, int): raise TypeMismatchException("Expecting a static index; cannot access element %r" % key) attrs = list(range(len(typ.members))) index = key annotation = None if location == 'storage': return LLLnode.from_list(['add', ['sha3_32', parent], LLLnode.from_list(index, annotation=annotation)], typ=subtype, location='storage') elif location == 'storage_prehashed': return LLLnode.from_list(['add', parent, LLLnode.from_list(index, annotation=annotation)], typ=subtype, location='storage') elif location == 'memory': offset = 0 for i in range(index): offset += 32 * get_size_of_type(typ.members[attrs[i]]) return LLLnode.from_list(['add', offset, parent], typ=typ.members[key], location='memory', annotation=annotation) else: raise TypeMismatchException("Not expecting a member variable access") elif isinstance(typ, (ListType, MappingType)): if isinstance(typ, ListType): subtype = typ.subtype sub = ['uclamplt', base_type_conversion(key, key.typ, BaseType('num')), typ.count] elif isinstance(typ, MappingType) and isinstance(key.typ, ByteArrayType): if not isinstance(typ.keytype, ByteArrayType) or (typ.keytype.maxlen < key.typ.maxlen): raise TypeMismatchException('Mapping keys of bytes cannot be cast, use exact same bytes type of: %s', str(typ.keytype)) subtype = typ.valuetype if len(key.args[0].args) == 3: # handle bytes literal. sub = LLLnode.from_list([ 'seq', key, ['sha3', ['add', key.args[0].args[-1], 32], ceil32(key.typ.maxlen)] ]) else: sub = LLLnode.from_list(['sha3', ['add', key.args[0].value, 32], ceil32(key.typ.maxlen)]) else: subtype = typ.valuetype sub = base_type_conversion(key, key.typ, typ.keytype) if location == 'storage': return LLLnode.from_list(['add', ['sha3_32', parent], sub], typ=subtype, location='storage') elif location == 'storage_prehashed': return LLLnode.from_list(['add', parent, sub], typ=subtype, location='storage') elif location == 'memory': if isinstance(typ, MappingType): raise TypeMismatchException("Can only have fixed-side arrays in memory, not mappings") offset = 32 * get_size_of_type(subtype) return LLLnode.from_list(['add', ['mul', offset, sub], parent], typ=subtype, location='memory') else: raise TypeMismatchException("Not expecting an array access ") else: raise TypeMismatchException("Cannot access the child of a constant variable! %r" % typ)
def get_length(arg): if arg.location == "memory": return LLLnode.from_list(['mload', arg], typ=BaseType('num')) elif arg.location == "storage": return LLLnode.from_list(['sload', ['sha3_32', arg]], typ=BaseType('num'))
def pack_args_by_32(holder, maxlen, arg, typ, context, placeholder, dynamic_offset_counter=None, datamem_start=None): """ Copy necessary variables to pre-allocated memory section. :param holder: Complete holder for all args :param maxlen: Total length in bytes of the full arg section (static + dynamic). :param arg: Current arg to pack :param context: Context of arg :param placeholder: Static placeholder for static argument part. :param dynamic_offset_counter: position counter stored in static args. :param dynamic_placeholder: pointer to current position in memory to write dynamic values to. :param datamem_start: position where the whole datemem section starts. """ if isinstance(typ, BaseType): value = parse_expr(arg, context) value = base_type_conversion(value, value.typ, typ) holder.append( LLLnode.from_list(['mstore', placeholder, value], typ=typ, location='memory')) elif isinstance(typ, ByteArrayType): bytez = b'' source_expr = Expr(arg, context) if isinstance(arg, ast.Str): if len(arg.s) > typ.maxlen: raise TypeMismatchException( "Data input bytes are to big: %r %r" % (len(arg.s), typ)) for c in arg.s: if ord(c) >= 256: raise InvalidLiteralException( "Cannot insert special character %r into byte array" % c) bytez += bytes([ord(c)]) holder.append(source_expr.lll_node) # Set static offset, in arg slot. holder.append( LLLnode.from_list( ['mstore', placeholder, ['mload', dynamic_offset_counter]])) # Get the biginning to write the ByteArray to. dest_placeholder = LLLnode.from_list( ['add', datamem_start, ['mload', dynamic_offset_counter]], typ=typ, location='memory', annotation="pack_args_by_32:dest_placeholder") copier = make_byte_array_copier(dest_placeholder, source_expr.lll_node) holder.append(copier) # Increment offset counter. increment_counter = LLLnode.from_list([ 'mstore', dynamic_offset_counter, [ 'add', [ 'add', ['mload', dynamic_offset_counter], ['ceil32', ['mload', dest_placeholder]] ], 32 ] ]) holder.append(increment_counter) elif isinstance(typ, ListType): maxlen += (typ.count - 1) * 32 typ = typ.subtype def check_list_type_match(provided): # Check list types match. if provided != typ: raise TypeMismatchException( "Log list type '%s' does not match provided, expected '%s'" % (provided, typ)) # List from storage if isinstance(arg, ast.Attribute) and arg.value.id == 'self': stor_list = context.globals[arg.attr] check_list_type_match(stor_list.typ.subtype) size = stor_list.typ.count for offset in range(0, size): arg2 = LLLnode.from_list([ 'sload', ['add', ['sha3_32', Expr(arg, context).lll_node], offset] ], typ=typ) holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32))) # List from variable. elif isinstance(arg, ast.Name): size = context.vars[arg.id].size pos = context.vars[arg.id].pos check_list_type_match(context.vars[arg.id].typ.subtype) for i in range(0, size): offset = 32 * i arg2 = LLLnode.from_list(pos + offset, typ=typ, location='memory') holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32))) # is list literal. else: holder, maxlen = pack_args_by_32(holder, maxlen, arg.elts[0], typ, context, placeholder) for j, arg2 in enumerate(arg.elts[1:]): holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32))) return holder, maxlen
def parse_for(self): from .parser import ( parse_body, ) # Type 0 for, eg. for i in list(): ... if self._is_list_iter(): return self.parse_for_list() if not isinstance(self.stmt.iter, ast.Call) or \ not isinstance(self.stmt.iter.func, ast.Name) or \ not isinstance(self.stmt.target, ast.Name) or \ self.stmt.iter.func.id != "range" or \ len(self.stmt.iter.args) not in (1, 2): 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) # noqa # Type 1 for, eg. for i in range(10): ... if len(self.stmt.iter.args) == 1: if not isinstance(self.stmt.iter.args[0], ast.Num): raise StructureException("Range only accepts literal values", self.stmt.iter) start = LLLnode.from_list(0, typ='num', pos=getpos(self.stmt)) rounds = self.stmt.iter.args[0].n elif isinstance(self.stmt.iter.args[0], ast.Num) and isinstance( self.stmt.iter.args[1], ast.Num): # Type 2 for, eg. for i in range(100, 110): ... start = LLLnode.from_list(self.stmt.iter.args[0].n, typ='num', pos=getpos(self.stmt)) rounds = LLLnode.from_list(self.stmt.iter.args[1].n - self.stmt.iter.args[0].n, typ='num', pos=getpos(self.stmt)) else: # Type 3 for, eg. for i in range(x, x + 10): ... if not isinstance(self.stmt.iter.args[1], ast.BinOp) or not isinstance( self.stmt.iter.args[1].op, ast.Add): raise StructureException( "Two-arg for statements must be of the form `for i in range(start, start + rounds): ...`", self.stmt.iter.args[1]) if ast.dump(self.stmt.iter.args[0]) != ast.dump( self.stmt.iter.args[1].left): raise StructureException( "Two-arg for statements of the form `for i in range(x, x + y): ...` must have x identical in both places: %r %r" % (ast.dump(self.stmt.iter.args[0]), ast.dump(self.stmt.iter.args[1].left)), self.stmt.iter) if not isinstance(self.stmt.iter.args[1].right, ast.Num): raise StructureException("Range only accepts literal values", self.stmt.iter.args[1]) start = Expr.parse_value_expr(self.stmt.iter.args[0], self.context) rounds = self.stmt.iter.args[1].right.n varname = self.stmt.target.id pos = self.context.new_variable(varname, BaseType('num')) 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 pack_args_by_32(holder, maxlen, arg, typ, context, placeholder): if isinstance(typ, BaseType): value = parse_expr(arg, context) value = base_type_conversion(value, value.typ, typ) holder.append( LLLnode.from_list(['mstore', placeholder, value], typ=typ, location='memory')) elif isinstance(typ, ByteArrayType): bytez = b'' # Bytes from Storage if isinstance(arg, ast.Attribute) and arg.value.id == 'self': stor_bytes = context.globals[arg.attr] if stor_bytes.typ.maxlen > 32: raise TypeMismatchException( "Can only log a maximum of 32 bytes at a time.") arg2 = LLLnode.from_list([ 'sload', ['add', ['sha3_32', Expr(arg, context).lll_node], 1] ], typ=BaseType(32)) holder, maxlen = pack_args_by_32( holder, maxlen, arg2, BaseType(32), context, context.new_placeholder(BaseType(32))) # String literals elif isinstance(arg, ast.Str): if len(arg.s) > typ.maxlen: raise TypeMismatchException( "Data input bytes are to big: %r %r" % (len(arg.s), typ)) for c in arg.s: if ord(c) >= 256: raise InvalidLiteralException( "Cannot insert special character %r into byte array" % c) bytez += bytes([ord(c)]) bytez_length = len(bytez) if len(bytez) > 32: raise InvalidLiteralException( "Can only log a maximum of 32 bytes at a time.") holder.append( LLLnode.from_list([ 'mstore', placeholder, bytes_to_int(bytez + b'\x00' * (32 - bytez_length)) ], typ=typ, location='memory')) # Variables else: value = parse_expr(arg, context) if value.typ.maxlen > typ.maxlen: raise TypeMismatchException( "Data input bytes are to big: %r %r" % (value.typ, typ)) holder.append( LLLnode.from_list( ['mstore', placeholder, ['mload', ['add', value, 32]]], typ=typ, location='memory')) elif isinstance(typ, ListType): maxlen += (typ.count - 1) * 32 typ = typ.subtype def check_list_type_match(provided): # Check list types match. if provided != typ: raise TypeMismatchException( "Log list type '%s' does not match provided, expected '%s'" % (provided, typ)) # List from storage if isinstance(arg, ast.Attribute) and arg.value.id == 'self': stor_list = context.globals[arg.attr] check_list_type_match(stor_list.typ.subtype) size = stor_list.typ.count for offset in range(0, size): arg2 = LLLnode.from_list([ 'sload', ['add', ['sha3_32', Expr(arg, context).lll_node], offset] ], typ=typ) holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32))) # List from variable. elif isinstance(arg, ast.Name): size = context.vars[arg.id].size pos = context.vars[arg.id].pos check_list_type_match(context.vars[arg.id].typ.subtype) for i in range(0, size): offset = 32 * i arg2 = LLLnode.from_list(pos + offset, typ=typ, location='memory') holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32))) # is list literal. else: holder, maxlen = pack_args_by_32(holder, maxlen, arg.elts[0], typ, context, placeholder) for j, arg2 in enumerate(arg.elts[1:]): holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32))) return holder, maxlen
def pack_args_by_32(holder, maxlen, arg, typ, context, placeholder): if isinstance(typ, BaseType): value = parse_expr(arg, context) value = base_type_conversion(value, value.typ, typ) holder.append( LLLnode.from_list(['mstore', placeholder, value], typ=typ, location='memory')) elif isinstance(typ, ByteArrayType): bytez = b'' # String literals if isinstance(arg, ast.Str): if len(arg.s) > typ.maxlen: raise TypeMismatchException( "Data input bytes are to big: %r %r" % (len(arg.s), typ)) for c in arg.s: if ord(c) >= 256: raise InvalidLiteralException( "Cannot insert special character %r into byte array" % c) bytez += bytes([ord(c)]) bytez_length = len(bytez) if len(bytez) > 32: raise InvalidLiteralException( "Can only log a maximum of 32 bytes at a time.") holder.append( LLLnode.from_list([ 'mstore', placeholder, bytes_to_int(bytez + b'\x00' * (32 - bytez_length)) ], typ=typ, location='memory')) # Variables else: value = parse_expr(arg, context) if value.typ.maxlen > typ.maxlen: raise TypeMismatchException( "Data input bytes are to big: %r %r" % (value.typ, typ)) holder.append( LLLnode.from_list( ['mstore', placeholder, ['mload', ['add', value, 32]]], typ=typ, location='memory')) elif isinstance(typ, ListType): maxlen += (typ.count - 1) * 32 typ = typ.subtype if isinstance(arg, ast.Name): size = context.vars[arg.id].size pos = context.vars[arg.id].pos for i in range(0, size): offset = 32 * i arg2 = LLLnode.from_list(pos + offset, typ=typ, location='memory') holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32))) else: # is list literal. holder, maxlen = pack_args_by_32(holder, maxlen, arg.elts[0], typ, context, placeholder) for j, arg2 in enumerate(arg.elts[1:]): holder, maxlen = pack_args_by_32( holder, maxlen, arg2, typ, context, context.new_placeholder(BaseType(32))) return holder, maxlen