def unwrap_location(orig): if orig.location == 'memory': return LLLnode.from_list(['mload', orig], typ=orig.typ) elif orig.location == 'storage': return LLLnode.from_list(['sload', orig], typ=orig.typ) else: return orig
def byte_array_to_num( arg, expr, out_type, offset=32, ): if arg.location == "memory": lengetter = LLLnode.from_list(['mload', '_sub'], typ=BaseType('int128')) first_el_getter = LLLnode.from_list(['mload', ['add', 32, '_sub']], typ=BaseType('int128')) elif arg.location == "storage": lengetter = LLLnode.from_list(['sload', ['sha3_32', '_sub']], typ=BaseType('int128')) first_el_getter = LLLnode.from_list( ['sload', ['add', 1, ['sha3_32', '_sub']]], typ=BaseType('int128')) if out_type == 'int128': result = [ 'clamp', ['mload', MemoryPositions.MINNUM], ['div', '_el1', ['exp', 256, ['sub', 32, '_len']]], ['mload', MemoryPositions.MAXNUM] ] elif out_type == 'uint256': 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], result] ] ], typ=BaseType(out_type), annotation='bytearray to number (%s)' % out_type)
def make_byte_array_copier(destination, source, pos=None): if not isinstance(source.typ, (ByteArrayType, NullType)): raise TypeMismatchException( "Can only set a byte array to another byte array", pos) if isinstance( source.typ, ByteArrayType) and source.typ.maxlen > destination.typ.maxlen: raise TypeMismatchException( "Cannot cast from greater max-length %d to shorter max-length %d" % (source.typ.maxlen, destination.typ.maxlen)) # Special case: memory to memory if source.location == "memory" and destination.location == "memory": gas_calculation = GAS_IDENTITY + GAS_IDENTITYWORD * ( ceil32(source.typ.maxlen) // 32) o = LLLnode.from_list([ 'with', '_source', source, [ 'with', '_sz', ['add', 32, ['mload', '_source']], [ 'assert', [ 'call', ['add', 18, ['div', '_sz', 10]], 4, 0, '_source', '_sz', destination, '_sz' ] ] ] ], typ=None, add_gas_estimate=gas_calculation, annotation='Memory copy') return o pos_node = LLLnode.from_list('_pos', typ=source.typ, location=source.location) # Get the length if isinstance(source.typ, NullType): length = 1 elif source.location == "memory": length = ['add', ['mload', '_pos'], 32] elif source.location == "storage": length = ['add', ['sload', '_pos'], 32] pos_node = LLLnode.from_list(['sha3_32', pos_node], typ=source.typ, location=source.location) else: raise Exception("Unsupported location:" + source.location) if destination.location == "storage": destination = LLLnode.from_list(['sha3_32', destination], typ=destination.typ, location=destination.location) # Maximum theoretical length max_length = 32 if isinstance(source.typ, NullType) else source.typ.maxlen + 32 return LLLnode.from_list([ 'with', '_pos', 0 if isinstance(source.typ, NullType) else source, make_byte_slice_copier( destination, pos_node, length, max_length, pos=pos) ], typ=None)
def build_in_comparator(self): from ophydia.parser.parser import make_setter left = Expr(self.expr.left, self.context).lll_node right = Expr(self.expr.comparators[0], self.context).lll_node if left.typ.typ != right.typ.subtype.typ: raise TypeMismatchException("%s cannot be in a list of %s" % (left.typ.typ, right.typ.subtype.typ)) 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', pos=getpos(self.expr)) 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 constants(self): if self.expr.value is True: return LLLnode.from_list(1, typ=BaseType('bool', is_literal=True), pos=getpos(self.expr)) elif self.expr.value is False: return LLLnode.from_list(0, typ=BaseType('bool', is_literal=True), pos=getpos(self.expr)) elif self.expr.value is None: return LLLnode.from_list(None, typ=NullType(), pos=getpos(self.expr)) else: raise Exception("Unknown name constant: %r" % self.expr.value.value)
def variables(self): builtin_constants = { 'ZERO_ADDRESS': LLLnode.from_list([0], typ=BaseType('address', None, is_literal=True), pos=getpos(self.expr)), 'MAX_INT128': LLLnode.from_list(['mload', MemoryPositions.MAXNUM], typ=BaseType('int128', None, is_literal=True), pos=getpos(self.expr)), 'MIN_INT128': LLLnode.from_list(['mload', MemoryPositions.MINNUM], typ=BaseType('int128', None, is_literal=True), pos=getpos(self.expr)), 'MAX_DECIMAL': LLLnode.from_list(['mload', MemoryPositions.MAXDECIMAL], typ=BaseType('decimal', None, is_literal=True), pos=getpos(self.expr)), 'MIN_DECIMAL': LLLnode.from_list(['mload', MemoryPositions.MINDECIMAL], typ=BaseType('decimal', None, is_literal=True), pos=getpos(self.expr)), 'MAX_UINT256': LLLnode.from_list([2**256 - 1], typ=BaseType('uint256', None, is_literal=True), pos=getpos(self.expr)), } if self.expr.id == 'self': return LLLnode.from_list(['address'], typ='address', pos=getpos(self.expr)) elif self.expr.id in self.context.vars: var = self.context.vars[self.expr.id] return LLLnode.from_list(var.pos, typ=var.typ, location='memory', pos=getpos(self.expr), annotation=self.expr.id, mutable=var.mutable) elif self.expr.id in builtin_constants: return builtin_constants[self.expr.id] elif self.expr.id in self.context.constants: # check if value is compatible with const = self.context.constants[self.expr.id] if isinstance(const, ast.AnnAssign): # Handle ByteArrays. expr = Expr(const.value, self.context).lll_node return expr # Other types are already unwrapped, no need return self.context.constants[self.expr.id] else: raise VariableDeclarationException("Undeclared variable: " + self.expr.id, self.expr)
def call_self_public(stmt_expr, context, sig): # self.* style call to a public function. method_name, expr_args, sig = call_lookup_specs(stmt_expr, context) add_gas = sig.gas # gas of call inargs, inargsize, _ = pack_arguments(sig, expr_args, context, pos=getpos(stmt_expr)) output_placeholder, returner, output_size = call_make_placeholder( stmt_expr, context, sig) assert_call = [ 'assert', [ 'call', ['gas'], ['address'], 0, inargs, inargsize, output_placeholder, output_size ] ] if output_size > 0: assert_call = ['seq', assert_call, returner] o = LLLnode.from_list(assert_call, typ=sig.output_type, location='memory', pos=getpos(stmt_expr), add_gas_estimate=add_gas, annotation='Internal Call: %s' % method_name) o.gas += sig.gas return o
def get_sig_statements(sig, pos): method_id_node = LLLnode.from_list(sig.method_id, pos=pos, annotation='%s' % sig.sig) if sig.private: sig_compare = 0 private_label = LLLnode.from_list( ['label', 'priv_{}'.format(sig.method_id)], pos=pos, annotation='%s' % sig.sig) else: sig_compare = ['eq', ['mload', 0], method_id_node] private_label = ['pass'] return sig_compare, private_label
def tuple_literals(self): if not len(self.expr.elts): raise StructureException("Tuple must have elements", self.expr) o = [] for elt in self.expr.elts: o.append(Expr(elt, self.context).lll_node) return LLLnode.from_list(["multi"] + o, typ=TupleType(o), pos=getpos(self.expr))
def arithmetic_get_reference(self, item): item_lll = Expr.parse_value_expr(item, self.context) if isinstance(item, ast.Call): # We only want to perform call statements once. placeholder = self.context.new_placeholder(item_lll.typ) pre_alloc = ['mstore', placeholder, item_lll] return pre_alloc, LLLnode.from_list(['mload', placeholder], location='memory', typ=item_lll.typ) else: return None, item_lll
def string(self): bytez, bytez_length = string_to_bytes(self.expr.s) placeholder = self.context.new_placeholder(ByteArrayType(bytez_length)) seq = [] seq.append(['mstore', placeholder, bytez_length]) for i in range(0, len(bytez), 32): seq.append(['mstore', ['add', placeholder, i + 32], bytes_to_int((bytez + b'\x00' * 31)[i: i + 32])]) return LLLnode.from_list(['seq'] + seq + [placeholder], typ=ByteArrayType(bytez_length), location='memory', pos=getpos(self.expr), annotation='Create ByteArray: %s' % bytez)
def parse_body(code, context): if not isinstance(code, list): return parse_stmt(code, context) o = [] for stmt in code: lll = parse_stmt(stmt, context) o.append(lll) return LLLnode.from_list(['seq'] + o, pos=getpos(code[0]) if code else None)
def base_type_conversion(orig, frm, to, pos): orig = unwrap_location(orig) if getattr(frm, 'is_literal', False) and frm.typ in ( 'int128', 'uint256') and not SizeLimits.in_bounds(frm.typ, orig.value): raise InvalidLiteralException( "Number out of range: " + str(orig.value), pos) 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, 'int128') 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 isinstance(frm, NullType): if to.typ not in ('int128', 'bool', 'uint256', 'address', 'bytes32', 'decimal'): # This is only to future proof the use of base_type_conversion. raise TypeMismatchException( "Cannot convert null-type object to type %r" % to, pos) # pragma: no cover return LLLnode.from_list(0, typ=to) elif isinstance(to, ContractType) and frm.typ == 'address': return LLLnode(orig.value, orig.args, typ=to, add_gas_estimate=orig.add_gas_estimate) # Integer literal conversion. elif (frm.typ, to.typ, frm.is_literal) == ('int128', 'uint256', True): return LLLnode(orig.value, orig.args, typ=to, add_gas_estimate=orig.add_gas_estimate) else: raise TypeMismatchException( "Typecasting from base type %r to %r unavailable" % (frm, to), pos)
def struct_literals(self): o = {} members = {} for key, value in zip(self.expr.keys, self.expr.values): if not isinstance(key, ast.Name) or not is_varname_valid(key.id, self.context.custom_units): raise TypeMismatchException("Invalid member variable for struct: %r" % vars(key).get('id', key), key) if key.id in o: raise TypeMismatchException("Member variable duplicated: " + key.id, key) o[key.id] = Expr(value, self.context).lll_node members[key.id] = o[key.id].typ return LLLnode.from_list(["multi"] + [o[key] for key in sorted(list(o.keys()))], typ=StructType(members), pos=getpos(self.expr))
def unary_operations(self): operand = Expr.parse_value_expr(self.expr.operand, self.context) if isinstance(self.expr.op, ast.Not): if isinstance(operand.typ, BaseType) and operand.typ.typ == 'bool': return LLLnode.from_list(["iszero", operand], typ='bool', pos=getpos(self.expr)) else: raise TypeMismatchException("Only bool is supported for not operation, %r supplied." % operand.typ, self.expr) elif isinstance(self.expr.op, ast.USub): if not is_numeric_type(operand.typ): raise TypeMismatchException("Unsupported type for negation: %r" % operand.typ, operand) if operand.typ.is_literal and 'int' in operand.typ.typ: num = ast.Num(0 - operand.value) num.source_code = self.expr.source_code num.lineno = self.expr.lineno num.col_offset = self.expr.col_offset return Expr.parse_value_expr(num, self.context) return LLLnode.from_list(["sub", 0, operand], typ=operand.typ, pos=getpos(self.expr)) else: raise StructureException("Only the 'not' unary operator is supported")
def external_contract_call(node, context, contract_name, contract_address, pos, value=None, gas=None): if value is None: value = 0 if gas is None: gas = 'gas' if contract_name not in context.sigs: raise VariableDeclarationException("Contract not declared yet: %s" % contract_name) method_name = node.func.attr if method_name not in context.sigs[contract_name]: raise FunctionDeclarationException( "Function not declared yet: %s (reminder: " "function must be declared in the correct contract)" % method_name, pos) sig = context.sigs[contract_name][method_name] inargs, inargsize, _ = pack_arguments( sig, [parse_expr(arg, context) for arg in node.args], context, pos=pos) 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 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
def call(self): from ophydia.parser.parser import ( external_contract_call ) from ophydia.functions import ( dispatch_table, ) if isinstance(self.expr.func, ast.Name): function_name = self.expr.func.id if function_name in dispatch_table: return dispatch_table[function_name](self.expr, self.context) else: err_msg = "Not a top-level function: {}".format(function_name) if function_name in [x.split('(')[0] for x, _ in self.context.sigs['self'].items()]: err_msg += ". Did you mean self.{}?".format(function_name) raise StructureException(err_msg, self.expr) elif isinstance(self.expr.func, ast.Attribute) and isinstance(self.expr.func.value, ast.Name) and self.expr.func.value.id == "self": return self_call.make_call(self.expr, self.context) elif isinstance(self.expr.func, ast.Attribute) and isinstance(self.expr.func.value, ast.Call): contract_name = self.expr.func.value.func.id contract_address = Expr.parse_value_expr(self.expr.func.value.args[0], self.context) value, gas = self._get_external_contract_keywords() return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas) elif isinstance(self.expr.func.value, ast.Attribute) and self.expr.func.value.attr in self.context.sigs: contract_name = self.expr.func.value.attr var = self.context.globals[self.expr.func.value.attr] contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.func.value.attr)) value, gas = self._get_external_contract_keywords() return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas) elif isinstance(self.expr.func.value, ast.Attribute) and self.expr.func.value.attr in self.context.globals: contract_name = self.context.globals[self.expr.func.value.attr].typ.unit var = self.context.globals[self.expr.func.value.attr] contract_address = unwrap_location(LLLnode.from_list(var.pos, typ=var.typ, location='storage', pos=getpos(self.expr), annotation='self.' + self.expr.func.value.attr)) value, gas = self._get_external_contract_keywords() return external_contract_call(self.expr, self.context, contract_name, contract_address, pos=getpos(self.expr), value=value, gas=gas) else: raise StructureException("Unsupported operator: %r" % ast.dump(self.expr), self.expr)
def list_literals(self): if not len(self.expr.elts): raise StructureException("List must have elements", self.expr) o = [] out_type = None for elt in self.expr.elts: o.append(Expr(elt, self.context).lll_node) if not out_type: out_type = o[-1].typ previous_type = o[-1].typ.subtype.typ if hasattr(o[-1].typ, 'subtype') else o[-1].typ current_type = out_type.subtype.typ if hasattr(out_type, 'subtype') else out_type if len(o) > 1 and previous_type != current_type: raise TypeMismatchException("Lists may only contain one type", self.expr) return LLLnode.from_list(["multi"] + o, typ=ListType(out_type, len(o)), pos=getpos(self.expr))
def parse_tree_to_lll(code, origcode, runtime_only=False): global_ctx = GlobalContext.get_global_context(code) _names_def = [_def.name for _def in global_ctx._defs] # Checks for duplicate function names if len(set(_names_def)) < len(_names_def): raise FunctionDeclarationException( "Duplicate function name: %s" % [name for name in _names_def if _names_def.count(name) > 1][0]) # TODO: remove events # _names_events = [_event.target.id for _event in global_ctx._events] # # Checks for duplicate event names # if len(set(_names_events)) < len(_names_events): # raise EventDeclarationException("Duplicate event name: %s" % [name for name in _names_events if _names_events.count(name) > 1][0]) # Initialization function initfunc = [_def for _def in global_ctx._defs if is_initializer(_def)] # Default function defaultfunc = [_def for _def in global_ctx._defs if is_default_func(_def)] # Regular functions otherfuncs = [ _def for _def in global_ctx._defs if not is_initializer(_def) and not is_default_func(_def) ] sigs = {} external_contracts = {} # Create the main statement o = ['seq'] # TODO: remove events # if global_ctx._events: # sigs = parse_events(sigs, global_ctx._events, global_ctx._custom_units) if global_ctx._contracts: external_contracts = parse_external_contracts(external_contracts, global_ctx._contracts) # If there is an init func... if initfunc: o.append(['seq', initializer_lll]) # TODO: breakpoint o.append( parse_func(initfunc[0], { **{ 'self': sigs }, **external_contracts }, origcode, global_ctx)) # If there are regular functions... if otherfuncs or defaultfunc: o = parse_other_functions(o, otherfuncs, sigs, external_contracts, origcode, global_ctx, defaultfunc, runtime_only) return LLLnode.from_list(o, typ=None)
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, 'int128'): return LLLnode.from_list([ 'clamp', ['mload', MemoryPositions.MINNUM], data_decl, ['mload', MemoryPositions.MAXNUM] ], typ=typ, annotation='checking int128 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) if self.context.in_assignment and (isinstance(self.expr.values[0], ast.Call) or isinstance(self.expr.values[1], ast.Call)): raise StructureException("Boolean operations with calls may not be performed on assignment", 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 number(self): orignum = get_original_if_0_prefixed(self.expr, self.context) if orignum is None and isinstance(self.expr.n, int): # Literal (mostly likely) becomes int128 if SizeLimits.in_bounds('int128', self.expr.n) or self.expr.n < 0: return LLLnode.from_list(self.expr.n, typ=BaseType('int128', unit=None, is_literal=True), pos=getpos(self.expr)) # Literal is large enough (mostly likely) becomes uint256. else: return LLLnode.from_list(self.expr.n, typ=BaseType('uint256', unit=None, is_literal=True), pos=getpos(self.expr)) elif isinstance(self.expr.n, float): numstring, num, den = get_number_as_fraction(self.expr, self.context) # if not SizeLimits.in_bounds('decimal', num // den): # if not SizeLimits.MINDECIMAL * den <= num <= SizeLimits.MAXDECIMAL * den: 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', unit=None), pos=getpos(self.expr)) # Binary literal. elif orignum[:2] == '0b': str_val = orignum[2:] total_bits = len(orignum[2:]) total_bits = total_bits if total_bits % 8 == 0 else total_bits + 8 - (total_bits % 8) # ceil8 to get byte length. if len(orignum[2:]) != total_bits: # Support only full formed bit definitions. raise InvalidLiteralException("Bit notation requires a multiple of 8 bits / 1 byte. {} bit(s) are missing.".format(total_bits - len(orignum[2:])), self.expr) byte_len = int(total_bits / 8) placeholder = self.context.new_placeholder(ByteArrayType(byte_len)) seq = [] seq.append(['mstore', placeholder, byte_len]) for i in range(0, total_bits, 256): section = str_val[i:i + 256] int_val = int(section, 2) << (256 - len(section)) # bytes are right padded. seq.append( ['mstore', ['add', placeholder, i + 32], int_val]) return LLLnode.from_list(['seq'] + seq + [placeholder], typ=ByteArrayType(byte_len), location='memory', pos=getpos(self.expr), annotation='Create ByteArray (Binary literal): %s' % str_val) 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', is_literal=True), pos=getpos(self.expr)) elif len(orignum) == 66: return LLLnode.from_list(self.expr.n, typ=BaseType('bytes32', is_literal=True), 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 parse_other_functions(o, otherfuncs, sigs, external_contracts, origcode, global_ctx, default_function, runtime_only): sub = ['seq', initializer_lll] add_gas = initializer_lll.gas for _def in otherfuncs: sub.append( parse_func(_def, { **{ 'self': sigs }, **external_contracts }, origcode, global_ctx)) # noqa E999 sub[-1].total_gas += add_gas add_gas += 30 for sig in generate_default_arg_sigs(_def, external_contracts, global_ctx._custom_units): sig.gas = sub[-1].total_gas sigs[sig.sig] = sig # Add fallback function if default_function: default_func = parse_func(default_function[0], { **{ 'self': sigs }, **external_contracts }, origcode, global_ctx) sub.append(default_func) else: sub.append( LLLnode.from_list(['revert', 0, 0], typ=None, annotation='Default function')) if runtime_only: return sub else: o.append(['return', 0, ['lll', sub, 0]]) return o
def pack_args_by_32(holder, maxlen, arg, typ, context, placeholder, dynamic_offset_counter=None, datamem_start=None, zero_pad_i=None, pos=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, pos) 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), pos) for c in arg.s: if ord(c) >= 256: raise InvalidLiteralException( "Cannot insert special character %r into byte array" % c, pos) 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, pos=pos) holder.append(copier) # Add zero padding. new_maxlen = ceil32(source_expr.lll_node.typ.maxlen) holder.append([ 'with', '_bytearray_loc', dest_placeholder, [ 'seq', [ 'repeat', zero_pad_i, ['mload', '_bytearray_loc'], new_maxlen, [ 'seq', [ 'if', ['ge', ['mload', zero_pad_i], new_maxlen], 'break' ], # stay within allocated bounds [ 'mstore8', [ 'add', ['add', '_bytearray_loc', 32], ['mload', zero_pad_i] ], 0 ] ] ] ] ]) # 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) p_holder = context.new_placeholder( BaseType(32)) if offset > 0 else placeholder holder, maxlen = pack_args_by_32(holder, maxlen, arg2, typ, context, p_holder, pos=pos) # 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') p_holder = context.new_placeholder( BaseType(32)) if i > 0 else placeholder holder, maxlen = pack_args_by_32(holder, maxlen, arg2, typ, context, p_holder, pos=pos) # is list literal. else: holder, maxlen = pack_args_by_32(holder, maxlen, arg.elts[0], typ, context, placeholder, pos=pos) for j, arg2 in enumerate(arg.elts[1:]): holder, maxlen = pack_args_by_32(holder, maxlen, arg2, typ, context, context.new_placeholder( BaseType(32)), pos=pos) return holder, maxlen
def make_setter(left, right, location, pos): # Basic types if isinstance(left.typ, BaseType): right = base_type_conversion(right, right.typ, left.typ, pos) 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, ByteArrayType): return make_byte_array_copier(left, right, pos) # Can't copy mappings elif isinstance(left.typ, MappingType): raise TypeMismatchException( "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 TypeMismatchException( "Setter type mismatch: left side is array, right side is %r" % 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 TypeMismatchException( "Left side is array, right side is not", pos) if left.typ.count != right.typ.count: raise TypeMismatchException("Mismatched number of elements", pos) # If the right side is a literal if right.value == "multi": if len(right.args) != left.typ.count: raise TypeMismatchException("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), right.args[i], location, 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 i in range(left.typ.count): subs.append( make_setter(add_variable_offset(left_token, LLLnode.from_list( i, typ='int128'), pos=pos), 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), add_variable_offset(right_token, LLLnode.from_list(i, typ='int128'), pos=pos), location, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None) # Structs elif isinstance(left.typ, (StructType, TupleType)): 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 TypeMismatchException( "Setter type mismatch: left side is %r, right side is %r" % (left.typ, right.typ), pos) if isinstance(left.typ, StructType): for k in left.typ.members: if k not in right.typ.members: raise TypeMismatchException( "Keys don't match for structs, missing %s" % k, pos) for k in right.typ.members: if k not in left.typ.members: raise TypeMismatchException( "Keys don't match for structs, extra %s" % k, pos) else: if len(left.typ.members) != len(right.typ.members): raise TypeMismatchException( "Tuple lengths don't match, %d vs %d" % (len(left.typ.members), 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" if isinstance(left.typ, StructType): keyz = sorted(list(left.typ.members.keys())) else: keyz = list(range(len(left.typ.members))) # If the right side is a literal if right.value == "multi": if len(right.args) != len(keyz): raise TypeMismatchException("Mismatched number of elements", pos) subs = [] for i, typ in enumerate(keyz): subs.append( make_setter(add_variable_offset(left_token, typ, pos=pos), right.args[i], location, 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 in keyz: subs.append( make_setter(add_variable_offset(left_token, typ, pos=pos), LLLnode.from_list(None, typ=NullType()), location, 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): right_token = LLLnode.from_list('_R', typ=right.typ, location="memory") subs = [] static_offset_counter = 0 for idx, (left_arg, right_arg) in enumerate(zip(left.args, right.typ.members)): # if left_arg.typ.typ != right_arg.typ: # raise TypeMismatchException("Tuple assignment mismatch position %d, expected '%s'" % (idx, right.typ), pos) if isinstance(right_arg, ByteArrayType): offset = LLLnode.from_list([ 'add', '_R', ['mload', ['add', '_R', static_offset_counter]] ], typ=ByteArrayType( 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, location="memory", 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 in keyz: subs.append( make_setter(add_variable_offset(left_token, typ, pos=pos), add_variable_offset(right_token, typ, pos=pos), location, pos=pos)) return LLLnode.from_list( ['with', '_L', left, ['with', '_R', right, ['seq'] + subs]], typ=None) else: raise Exception("Invalid type for setters")
def pack_arguments(signature, args, context, pos, return_placeholder=True): placeholder_typ = ByteArrayType( maxlen=sum([get_size_of_type(arg.typ) for arg in signature.args]) * 32 + 32) placeholder = context.new_placeholder(placeholder_typ) setters = [['mstore', placeholder, signature.method_id]] needpos = False staticarray_offset = 0 expected_arg_count = len(signature.args) actual_arg_count = len(args) if actual_arg_count != expected_arg_count: raise StructureException( "Wrong number of args for: %s (%s args, expected %s)" % (signature.name, actual_arg_count, expected_arg_count)) for i, (arg, typ) in enumerate(zip(args, [arg.typ for arg in signature.args])): if isinstance(typ, BaseType): setters.append( make_setter(LLLnode.from_list(placeholder + staticarray_offset + 32 + i * 32, typ=typ), arg, 'memory', pos=pos)) elif isinstance(typ, ByteArrayType): setters.append([ 'mstore', placeholder + staticarray_offset + 32 + i * 32, '_poz' ]) arg_copy = LLLnode.from_list('_s', typ=arg.typ, location=arg.location) target = LLLnode.from_list(['add', placeholder + 32, '_poz'], typ=typ, location='memory') setters.append([ 'with', '_s', arg, [ 'seq', make_byte_array_copier(target, arg_copy, pos), [ 'set', '_poz', [ 'add', 32, ['ceil32', ['add', '_poz', get_length(arg_copy)]] ] ] ] ]) needpos = True elif isinstance(typ, ListType): target = LLLnode.from_list( [placeholder + 32 + staticarray_offset + i * 32], typ=typ, location='memory') setters.append(make_setter(target, arg, 'memory', pos=pos)) staticarray_offset += 32 * (typ.count - 1) else: raise TypeMismatchException("Cannot pack argument of type %r" % typ) # For private call usage, doesn't use a returner. returner = [[placeholder + 28]] if return_placeholder else [] if needpos: return (LLLnode.from_list([ 'with', '_poz', len(args) * 32 + staticarray_offset, ['seq'] + setters + returner ], typ=placeholder_typ, location='memory'), placeholder_typ.maxlen - 28, placeholder + 32) else: return (LLLnode.from_list(['seq'] + setters + returner, typ=placeholder_typ, location='memory'), placeholder_typ.maxlen - 28, placeholder + 32)
def parse_func(code, sigs, origcode, global_ctx, _vars=None): if _vars is None: _vars = {} sig = FunctionSignature.from_definition( code, sigs=sigs, custom_units=global_ctx._custom_units) # Get base args for function. total_default_args = len(code.args.defaults) base_args = sig.args[: -total_default_args] if total_default_args > 0 else sig.args default_args = code.args.args[-total_default_args:] default_values = dict( zip([arg.arg for arg in default_args], code.args.defaults)) # __init__ function may not have defaults. if sig.name == '__init__' and total_default_args > 0: raise FunctionDeclarationException( "__init__ function may not have default parameters.") # Check for duplicate variables with globals for arg in sig.args: if arg.name in global_ctx._globals: raise FunctionDeclarationException( "Variable name duplicated between function arguments and globals: " + arg.name) # Create a local (per function) context. context = Context(vars=_vars, global_ctx=global_ctx, sigs=sigs, return_type=sig.output_type, is_constant=sig.const, is_payable=sig.payable, origcode=origcode, is_private=sig.private, method_id=sig.method_id) # Copy calldata to memory for fixed-size arguments max_copy_size = sum([ 32 if isinstance(arg.typ, ByteArrayType) else get_size_of_type(arg.typ) * 32 for arg in sig.args ]) base_copy_size = sum([ 32 if isinstance(arg.typ, ByteArrayType) else get_size_of_type(arg.typ) * 32 for arg in base_args ]) context.next_mem += max_copy_size clampers = [] # Create callback_ptr, this stores a destination in the bytecode for a private # function to jump to after a function has executed. _post_callback_ptr = "{}_{}_post_callback_ptr".format( sig.name, sig.method_id) if sig.private: context.callback_ptr = context.new_placeholder(typ=BaseType('uint256')) clampers.append( LLLnode.from_list(['mstore', context.callback_ptr, 'pass'], annotation='pop callback pointer')) if total_default_args > 0: clampers.append(['label', _post_callback_ptr]) # private functions without return types need to jump back to # the calling function, as there is no return statement to handle the # jump. stop_func = [['stop']] if sig.output_type is None and sig.private: stop_func = [['jump', ['mload', context.callback_ptr]]] if not len(base_args): copier = 'pass' elif sig.name == '__init__': copier = [ 'codecopy', MemoryPositions.RESERVED_MEMORY, '~codelen', base_copy_size ] else: copier = get_arg_copier(sig=sig, total_size=base_copy_size, memory_dest=MemoryPositions.RESERVED_MEMORY) clampers.append(copier) # Add asserts for payable and internal # private never gets payable check. if not sig.payable and not sig.private: clampers.append(['assert', ['iszero', 'callvalue']]) # Fill variable positions for i, arg in enumerate(sig.args): if i < len(base_args) and not sig.private: clampers.append( make_clamper(arg.pos, context.next_mem, arg.typ, sig.name == '__init__')) if isinstance(arg.typ, ByteArrayType): context.vars[arg.name] = VariableRecord(arg.name, context.next_mem, arg.typ, False) context.next_mem += 32 * get_size_of_type(arg.typ) else: context.vars[arg.name] = VariableRecord( arg.name, MemoryPositions.RESERVED_MEMORY + arg.pos, arg.typ, False) # Private function copiers. No clamping for private functions. dyn_variable_names = [ a.name for a in base_args if isinstance(a.typ, ByteArrayType) ] if sig.private and dyn_variable_names: i_placeholder = context.new_placeholder(typ=BaseType('uint256')) unpackers = [] for idx, var_name in enumerate(dyn_variable_names): var = context.vars[var_name] ident = "_load_args_%d_dynarg%d" % (sig.method_id, idx) o = make_unpacker(ident=ident, i_placeholder=i_placeholder, begin_pos=var.pos) unpackers.append(o) if not unpackers: unpackers = ['pass'] clampers.append( LLLnode.from_list( ['seq_unchecked'] + unpackers + [ 0 ], # [0] to complete full overarching 'seq' statement, see private_label. typ=None, annotation='dynamic unpacker', pos=getpos(code))) # Create "clampers" (input well-formedness checkers) # Return function body if sig.name == '__init__': o = LLLnode.from_list(['seq'] + clampers + [parse_body(code.body, context)], pos=getpos(code)) elif is_default_func(sig): if len(sig.args) > 0: raise FunctionDeclarationException( 'Default function may not receive any arguments.', code) if sig.private: raise FunctionDeclarationException( 'Default function may only be public.', code) o = LLLnode.from_list(['seq'] + clampers + [parse_body(code.body, context)], pos=getpos(code)) else: if total_default_args > 0: # Function with default parameters. function_routine = "{}_{}".format(sig.name, sig.method_id) default_sigs = generate_default_arg_sigs(code, sigs, global_ctx._custom_units) sig_chain = ['seq'] for default_sig in default_sigs: sig_compare, private_label = get_sig_statements( default_sig, getpos(code)) # Populate unset default variables populate_arg_count = len(sig.args) - len(default_sig.args) set_defaults = [] if populate_arg_count > 0: current_sig_arg_names = {x.name for x in default_sig.args} missing_arg_names = [ arg.arg for arg in default_args if arg.arg not in current_sig_arg_names ] for arg_name in missing_arg_names: value = Expr(default_values[arg_name], context).lll_node var = context.vars[arg_name] left = LLLnode.from_list(var.pos, typ=var.typ, location='memory', pos=getpos(code), mutable=var.mutable) set_defaults.append( make_setter(left, value, 'memory', pos=getpos(code))) current_sig_arg_names = {x.name for x in default_sig.args} base_arg_names = {arg.name for arg in base_args} if sig.private: # Load all variables in default section, if private, # because the stack is a linear pipe. copier_arg_count = len(default_sig.args) copier_arg_names = current_sig_arg_names else: copier_arg_count = len(default_sig.args) - len(base_args) copier_arg_names = current_sig_arg_names - base_arg_names # Order copier_arg_names, this is very important. copier_arg_names = [ x.name for x in default_sig.args if x.name in copier_arg_names ] # Variables to be populated from calldata/stack. default_copiers = [] if copier_arg_count > 0: # Get map of variables in calldata, with thier offsets offset = 4 calldata_offset_map = {} for arg in default_sig.args: calldata_offset_map[arg.name] = offset offset += 32 if isinstance( arg.typ, ByteArrayType) else get_size_of_type(arg.typ) * 32 # Copy set default parameters from calldata dynamics = [] for arg_name in copier_arg_names: var = context.vars[arg_name] calldata_offset = calldata_offset_map[arg_name] if sig.private: _offset = calldata_offset if isinstance(var.typ, ByteArrayType): _size = 32 dynamics.append(var.pos) else: _size = var.size * 32 default_copiers.append( get_arg_copier(sig=sig, memory_dest=var.pos, total_size=_size, offset=_offset)) else: # Add clampers. default_copiers.append( make_clamper(calldata_offset - 4, var.pos, var.typ)) # Add copying code. if isinstance(var.typ, ByteArrayType): _offset = [ 'add', 4, ['calldataload', calldata_offset] ] else: _offset = calldata_offset default_copiers.append( get_arg_copier(sig=sig, memory_dest=var.pos, total_size=var.size * 32, offset=_offset)) # Unpack byte array if necessary. if dynamics: i_placeholder = context.new_placeholder( typ=BaseType('uint256')) for idx, var_pos in enumerate(dynamics): ident = 'unpack_default_sig_dyn_%d_arg%d' % ( default_sig.method_id, idx) default_copiers.append( make_unpacker(ident=ident, i_placeholder=i_placeholder, begin_pos=var_pos)) default_copiers.append(0) # for over arching seq, POP sig_chain.append([ 'if', sig_compare, [ 'seq', private_label, LLLnode.from_list( ['mstore', context.callback_ptr, 'pass'], annotation='pop callback pointer', pos=getpos(code)) if sig.private else ['pass'], ['seq'] + set_defaults if set_defaults else ['pass'], ['seq_unchecked'] + default_copiers if default_copiers else ['pass'], [ 'goto', _post_callback_ptr if sig.private else function_routine ] ] ]) # With private functions all variable loading occurs in the default # function sub routine. if sig.private: _clampers = [['label', _post_callback_ptr]] else: _clampers = clampers # Function with default parameters. o = LLLnode.from_list( [ 'seq', sig_chain, [ 'if', 0, # can only be jumped into [ 'seq', ['label', function_routine] if not sig.private else ['pass'], ['seq'] + _clampers + [parse_body(c, context) for c in code.body] + stop_func ] ] ], typ=None, pos=getpos(code)) else: # Function without default parameters. sig_compare, private_label = get_sig_statements(sig, getpos(code)) o = LLLnode.from_list([ 'if', sig_compare, ['seq'] + [private_label] + clampers + [parse_body(c, context) for c in code.body] + stop_func ], typ=None, pos=getpos(code)) # Check for at leasts one return statement if necessary. if context.return_type and context.function_return_count == 0: raise FunctionDeclarationException( "Missing return statement in function '%s' " % sig.name, code) o.context = context o.total_gas = o.gas + calc_mem_gas(o.context.next_mem) o.func_name = sig.name return o
def add_variable_offset(parent, key, pos): 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, pos) if key not in typ.members: raise TypeMismatchException( "Object does not have member variable %s" % key, pos) 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)), pos) index = attrs.index(key) annotation = key else: if not isinstance(key, int): raise TypeMismatchException( "Expecting a static index; cannot access element %r" % key, pos) 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, MappingType): if 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), pos) 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], ['mload', key.args[0].args[-1]] ] ]) else: sub = LLLnode.from_list([ 'sha3', ['add', key.args[0].value, 32], ['mload', key.args[0].value] ]) else: subtype = typ.valuetype sub = base_type_conversion(key, key.typ, typ.keytype, pos=pos) if location == 'storage': return LLLnode.from_list(['sha3_64', parent, sub], typ=subtype, location='storage') elif location == 'memory': raise TypeMismatchException( "Can only have fixed-side arrays in memory, not mappings", pos) elif isinstance(typ, ListType): subtype = typ.subtype sub = [ 'uclamplt', base_type_conversion(key, key.typ, BaseType('int128'), pos=pos), typ.count ] 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': 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 ", pos) else: raise TypeMismatchException( "Cannot access the child of a constant variable! %r" % typ, pos)
def get_length(arg): if arg.location == "memory": return LLLnode.from_list(['mload', arg], typ=BaseType('int128')) elif arg.location == "storage": return LLLnode.from_list(['sload', ['sha3_32', arg]], typ=BaseType('int128'))
def make_byte_slice_copier(destination, source, length, max_length, pos=None): # Special case: memory to memory if source.location == "memory" and destination.location == "memory": return LLLnode.from_list([ 'with', '_l', max_length, [ 'pop', [ 'call', 18 + max_length // 10, 4, 0, source, '_l', destination, '_l' ] ] ], typ=None, annotation='copy byte slice dest: %s' % str(destination)) # Copy over data if isinstance(source.typ, NullType): loader = 0 elif source.location == "memory": loader = [ 'mload', [ 'add', '_pos', ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]] ] ] elif source.location == "storage": loader = [ 'sload', ['add', '_pos', ['mload', MemoryPositions.FREE_LOOP_INDEX]] ] else: raise Exception("Unsupported location:" + source.location) # Where to paste it? if destination.location == "memory": setter = [ 'mstore', [ 'add', '_opos', ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]] ], loader ] elif destination.location == "storage": setter = [ 'sstore', ['add', '_opos', ['mload', MemoryPositions.FREE_LOOP_INDEX]], loader ] else: raise Exception("Unsupported location:" + destination.location) # Check to see if we hit the length checker = [ 'if', [ 'gt', ['mul', 32, ['mload', MemoryPositions.FREE_LOOP_INDEX]], '_actual_len' ], 'break' ] # Make a loop to do the copying o = [ 'with', '_pos', source, [ 'with', '_opos', destination, [ 'with', '_actual_len', length, [ 'repeat', MemoryPositions.FREE_LOOP_INDEX, 0, (max_length + 31) // 32, ['seq', checker, setter] ] ] ] ] return LLLnode.from_list(o, typ=None, annotation='copy byte slice src: %s dst: %s' % (source, destination), pos=pos)