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 make_clamper(datapos, mempos, typ, is_init=False): if not is_init: data_decl = ['calldataload', ['add', 4, datapos]] copier = lambda pos, sz: ['calldatacopy', mempos, ['add', 4, pos], sz] else: data_decl = ['codeload', ['add', '~codelen', datapos]] copier = lambda pos, sz: [ 'codecopy', mempos, ['add', '~codelen', pos], sz ] # Numbers: make sure they're in range if is_base_type(typ, 'num'): return LLLnode.from_list([ 'clamp', ['mload', MemoryPositions.MINNUM], data_decl, ['mload', MemoryPositions.MAXNUM] ], typ=typ, annotation='checking num input') # Booleans: make sure they're zero or one elif is_base_type(typ, 'bool'): return LLLnode.from_list(['uclamplt', data_decl, 2], typ=typ, annotation='checking bool input') # Addresses: make sure they're in range elif is_base_type(typ, 'address'): return LLLnode.from_list( ['uclamplt', data_decl, ['mload', MemoryPositions.ADDRSIZE]], typ=typ, annotation='checking address input') # Bytes: make sure they have the right size elif isinstance(typ, ByteArrayType): return LLLnode.from_list([ 'seq', copier(data_decl, 32 + typ.maxlen), [ 'assert', ['le', ['calldataload', ['add', 4, data_decl]], typ.maxlen] ] ], typ=None, annotation='checking bytearray input') # Lists: recurse elif isinstance(typ, ListType): o = [] for i in range(typ.count): offset = get_size_of_type(typ.subtype) * 32 * i o.append( make_clamper(datapos + offset, mempos + offset, typ.subtype, is_init)) return LLLnode.from_list(['seq'] + o, typ=None, annotation='checking list input') # Otherwise don't make any checks else: return LLLnode.from_list('pass')
def boolean_operations(self): if len(self.expr.values) != 2: raise StructureException("Expected two arguments for a bool op", self.expr) left = Expr.parse_value_expr(self.expr.values[0], self.context) right = Expr.parse_value_expr(self.expr.values[1], self.context) if not is_base_type(left.typ, 'bool') or not is_base_type(right.typ, 'bool'): raise TypeMismatchException("Boolean operations can only be between booleans!", self.expr) if isinstance(self.expr.op, ast.And): op = 'and' elif isinstance(self.expr.op, ast.Or): op = 'or' else: raise Exception("Unsupported bool op: " + self.expr.op) return LLLnode.from_list([op, left, right], typ='bool', pos=getpos(self.expr))
def parse_return(self): from .parser import (make_setter) 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(['return', 0, 0], typ=None, pos=getpos(self.stmt)) if not self.stmt.value: raise TypeMismatchException("Expecting to return a value", self.stmt) sub = Expr(self.stmt.value, self.context).lll_node self.context.increment_return_counter() # Returning a value (most common case) if isinstance(sub.typ, BaseType): if not isinstance(self.context.return_type, BaseType): raise TypeMismatchException( "Trying to return base type %r, output expecting %r" % (sub.typ, self.context.return_type), self.stmt.value) sub = unwrap_location(sub) if not are_units_compatible(sub.typ, self.context.return_type): raise TypeMismatchException( "Return type units mismatch %r %r" % (sub.typ, self.context.return_type), self.stmt.value) elif is_base_type(sub.typ, self.context.return_type.typ) or \ (is_base_type(sub.typ, 'num') and is_base_type(self.context.return_type, 'signed256')): return LLLnode.from_list( ['seq', ['mstore', 0, sub], ['return', 0, 32]], typ=None, pos=getpos(self.stmt)) else: raise TypeMismatchException( "Unsupported type conversion: %r to %r" % (sub.typ, self.context.return_type), self.stmt.value) # Returning a byte array elif isinstance(sub.typ, ByteArrayType): if not isinstance(self.context.return_type, ByteArrayType): raise TypeMismatchException( "Trying to return base type %r, output expecting %r" % (sub.typ, self.context.return_type), self.stmt.value) if sub.typ.maxlen > self.context.return_type.maxlen: raise TypeMismatchException( "Cannot cast from greater max-length %d to shorter max-length %d" % (sub.typ.maxlen, self.context.return_type.maxlen), self.stmt.value) # Returning something already in memory if sub.location == 'memory': return LLLnode.from_list([ 'with', '_loc', sub, [ 'seq', ['mstore', ['sub', '_loc', 32], 32], [ 'return', ['sub', '_loc', 32], ['ceil32', ['add', ['mload', '_loc'], 64]] ] ] ], typ=None, pos=getpos(self.stmt)) # Copying from storage elif sub.location == 'storage': # Instantiate a byte array at some index fake_byte_array = LLLnode(self.context.get_next_mem() + 32, typ=sub.typ, location='memory', pos=getpos(self.stmt)) o = [ 'seq', # Copy the data to this byte array make_byte_array_copier(fake_byte_array, sub), # Store the number 32 before it for ABI formatting purposes ['mstore', self.context.get_next_mem(), 32], # Return it [ 'return', self.context.get_next_mem(), [ 'add', [ 'ceil32', ['mload', self.context.get_next_mem() + 32] ], 64 ] ] ] return LLLnode.from_list(o, typ=None, pos=getpos(self.stmt)) else: raise Exception("Invalid location: %s" % 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] if sub_base_type != ret_base_type and sub.value != 'multi': raise TypeMismatchException( "List return type %r does not match specified return type, expecting %r" % (sub_base_type, ret_base_type), self.stmt) if sub.location == "memory" and sub.value != "multi": return LLLnode.from_list([ 'return', sub, get_size_of_type(self.context.return_type) * 32 ], typ=None, pos=getpos(self.stmt)) 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, [ 'return', new_sub, get_size_of_type(self.context.return_type) * 32 ] ], typ=None, pos=getpos(self.stmt)) # Returning a tuple. elif isinstance(sub.typ, TupleType): if len(self.context.return_type.members) != len(sub.typ.members): raise StructureException("Tuple lengths don't match!") subs = [] dynamic_offset_counter = LLLnode( self.context.get_next_mem(), typ=None, annotation="dynamic_offset_counter" ) # dynamic offset position counter. new_sub = LLLnode.from_list(self.context.get_next_mem() + 32, typ=self.context.return_type, location='memory', annotation='new_sub') keyz = list(range(len(sub.typ.members))) dynamic_offset_start = 32 * len( sub.args) # The static list of args end. left_token = LLLnode.from_list('_loc', typ=new_sub.typ, location="memory") def get_dynamic_offset_value(): # Get value of dynamic offset counter. return ['mload', dynamic_offset_counter] def increment_dynamic_offset(dynamic_spot): # Increment dyanmic offset counter in memory. return [ 'mstore', dynamic_offset_counter, [ 'add', ['add', ['ceil32', ['mload', dynamic_spot]], 32], ['mload', dynamic_offset_counter] ] ] for i, typ in enumerate(keyz): arg = sub.args[i] variable_offset = LLLnode.from_list( ['add', 32 * i, left_token], typ=arg.typ, annotation='variable_offset') if isinstance(arg.typ, ByteArrayType): # Store offset pointer value. subs.append([ 'mstore', variable_offset, get_dynamic_offset_value() ]) # Store dynamic data, from offset pointer onwards. dynamic_spot = LLLnode.from_list( ['add', left_token, get_dynamic_offset_value()], location="memory", typ=arg.typ, annotation='dynamic_spot') subs.append( make_setter(dynamic_spot, arg, location="memory", pos=getpos(self.stmt))) subs.append(increment_dynamic_offset(dynamic_spot)) elif isinstance(arg.typ, BaseType): subs.append( make_setter(variable_offset, arg, "memory", pos=getpos(self.stmt))) else: raise Exception("Can't return type %s as part of tuple", type(arg.typ)) setter = LLLnode.from_list([ 'seq', [ 'mstore', dynamic_offset_counter, dynamic_offset_start ], ['with', '_loc', new_sub, ['seq'] + subs] ], typ=None) return LLLnode.from_list([ 'seq', setter, ['return', new_sub, get_dynamic_offset_value()] ], typ=None, pos=getpos(self.stmt)) else: raise TypeMismatchException("Can only return base type!", self.stmt)
def parse_return(self): from .parser import (parse_expr, make_setter) 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(['return', 0, 0], typ=None, pos=getpos(self.stmt)) if not self.stmt.value: raise TypeMismatchException("Expecting to return a value", self.stmt) sub = parse_expr(self.stmt.value, self.context) # Returning a value (most common case) if isinstance(sub.typ, BaseType): if not isinstance(self.context.return_type, BaseType): raise TypeMismatchException( "Trying to return base type %r, output expecting %r" % (sub.typ, self.context.return_type), self.stmt.value) sub = unwrap_location(sub) if not are_units_compatible(sub.typ, self.context.return_type): raise TypeMismatchException( "Return type units mismatch %r %r" % (sub.typ, self.context.return_type), self.stmt.value) elif is_base_type(sub.typ, self.context.return_type.typ) or \ (is_base_type(sub.typ, 'num') and is_base_type(self.context.return_type, 'signed256')): return LLLnode.from_list( ['seq', ['mstore', 0, sub], ['return', 0, 32]], typ=None, pos=getpos(self.stmt)) else: raise TypeMismatchException( "Unsupported type conversion: %r to %r" % (sub.typ, self.context.return_type), self.stmt.value) # Returning a byte array elif isinstance(sub.typ, ByteArrayType): if not isinstance(self.context.return_type, ByteArrayType): raise TypeMismatchException( "Trying to return base type %r, output expecting %r" % (sub.typ, self.context.return_type), self.stmt.value) if sub.typ.maxlen > self.context.return_type.maxlen: raise TypeMismatchException( "Cannot cast from greater max-length %d to shorter max-length %d" % (sub.typ.maxlen, self.context.return_type.maxlen), self.stmt.value) # Returning something already in memory if sub.location == 'memory': return LLLnode.from_list([ 'with', '_loc', sub, [ 'seq', ['mstore', ['sub', '_loc', 32], 32], [ 'return', ['sub', '_loc', 32], ['ceil32', ['add', ['mload', '_loc'], 64]] ] ] ], typ=None, pos=getpos(self.stmt)) # Copying from storage elif sub.location == 'storage': # Instantiate a byte array at some index fake_byte_array = LLLnode(self.context.get_next_mem() + 32, typ=sub.typ, location='memory', pos=getpos(self.stmt)) o = [ 'seq', # Copy the data to this byte array make_byte_array_copier(fake_byte_array, sub), # Store the number 32 before it for ABI formatting purposes ['mstore', self.context.get_next_mem(), 32], # Return it [ 'return', self.context.get_next_mem(), [ 'add', [ 'ceil32', ['mload', self.context.get_next_mem() + 32] ], 64 ] ] ] return LLLnode.from_list(o, typ=None, pos=getpos(self.stmt)) else: raise Exception("Invalid location: %s" % sub.location) # Returning a list elif isinstance(sub.typ, ListType): if sub.location == "memory" and sub.value != "multi": return LLLnode.from_list([ 'return', sub, get_size_of_type(self.context.return_type) * 32 ], typ=None, pos=getpos(self.stmt)) 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') return LLLnode.from_list([ 'seq', setter, [ 'return', new_sub, get_size_of_type(self.context.return_type) * 32 ] ], typ=None, pos=getpos(self.stmt)) else: raise TypeMismatchException("Can only return base type!", self.stmt)