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 ecadd(expr, args, kwargs, context): placeholder_node = LLLnode.from_list( context.new_placeholder(ByteArrayType(128)), typ=ByteArrayType(128), location='memory' ) pos = getpos(expr) o = LLLnode.from_list([ 'seq', ['mstore', placeholder_node, avo(args[0], 0, pos)], ['mstore', ['add', placeholder_node, 32], avo(args[0], 1, pos)], ['mstore', ['add', placeholder_node, 64], avo(args[1], 0, pos)], ['mstore', ['add', placeholder_node, 96], avo(args[1], 1, pos)], ['assert', ['call', 500, 6, 0, placeholder_node, 128, placeholder_node, 64]], placeholder_node, ], typ=ListType(BaseType('uint256'), 2), pos=getpos(expr), location='memory') return o
def method_id(expr, args, kwargs, context): if b' ' in args[0]: raise TypeMismatchException('Invalid function signature no spaces allowed.') method_id = fourbytes_to_int(sha3(args[0])[:4]) if args[1] == 'bytes32': return LLLnode(method_id, typ=BaseType('bytes32'), pos=getpos(expr)) elif args[1] == 'bytes[4]': placeholder = LLLnode.from_list(context.new_placeholder(ByteArrayType(4))) return LLLnode.from_list( ['seq', ['mstore', ['add', placeholder, 4], method_id], ['mstore', placeholder, 4], placeholder], typ=ByteArrayType(4), location='memory', pos=getpos(expr)) else: raise StructureException('Can only produce bytes32 or bytes[4] as outputs')
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 pack_arguments(signature, args, context, pos): 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), ['set', '_poz', ['add', 32, ['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) if needpos: return LLLnode.from_list(['with', '_poz', len(args) * 32 + staticarray_offset, ['seq'] + setters + [placeholder + 28]], typ=placeholder_typ, location='memory'), \ placeholder_typ.maxlen - 28 else: return LLLnode.from_list(['seq'] + setters + [placeholder + 28], typ=placeholder_typ, location='memory'), \ placeholder_typ.maxlen - 28
def parse_assert(self): if self.stmt.msg: if len(self.stmt.msg.s.strip()) == 0: raise StructureException('Empty reason string not allowed.', self.stmt) reason_str = self.stmt.msg.s.strip() sig_placeholder = self.context.new_placeholder(BaseType(32)) arg_placeholder = self.context.new_placeholder(BaseType(32)) reason_str_type = ByteArrayType(len(reason_str)) placeholder_bytes = Expr(self.stmt.msg, self.context).lll_node method_id = fourbytes_to_int(sha3(b"Error(string)")[:4]) assert_reason = \ ['seq', ['mstore', sig_placeholder, method_id], ['mstore', arg_placeholder, 32], placeholder_bytes, ['assert_reason', Expr.parse_value_expr(self.stmt.test, self.context), int(sig_placeholder + 28), int(4 + 32 + get_size_of_type(reason_str_type) * 32)]] return LLLnode.from_list(assert_reason, typ=None, pos=getpos(self.stmt)) else: return LLLnode.from_list([ 'assert', Expr.parse_value_expr(self.stmt.test, self.context) ], typ=None, pos=getpos(self.stmt))
def _assert_reason(self, test_expr, msg): if isinstance(msg, vy_ast.Name) and msg.id == "UNREACHABLE": return LLLnode.from_list(["assert_unreachable", test_expr], typ=None, pos=getpos(msg)) reason_str_type = ByteArrayType(len(msg.value.strip())) sig_placeholder = self.context.new_internal_variable(BaseType(32)) arg_placeholder = self.context.new_internal_variable(BaseType(32)) placeholder_bytes = Expr(msg, self.context).lll_node method_id = fourbytes_to_int(keccak256(b"Error(string)")[:4]) revert_seq = [ "seq", ["mstore", sig_placeholder, method_id], ["mstore", arg_placeholder, 32], placeholder_bytes, [ "revert", sig_placeholder + 28, int(4 + get_size_of_type(reason_str_type) * 32) ], ] if test_expr: lll_node = ["if", ["iszero", test_expr], revert_seq] else: lll_node = revert_seq return LLLnode.from_list(lll_node, typ=None, pos=getpos(self.stmt))
def parse_assert(self): with self.context.assertion_scope(): test_expr = Expr.parse_value_expr(self.stmt.test, self.context) if not self.is_bool_expr(test_expr): raise TypeMismatchException('Only boolean expressions allowed', self.stmt.test) if self.stmt.msg: if not isinstance(self.stmt.msg, ast.Str): raise StructureException( 'Reason parameter of assert needs to be a literal string.', self.stmt.msg) if len(self.stmt.msg.s.strip()) == 0: raise StructureException('Empty reason string not allowed.', self.stmt) reason_str = self.stmt.msg.s.strip() sig_placeholder = self.context.new_placeholder(BaseType(32)) arg_placeholder = self.context.new_placeholder(BaseType(32)) reason_str_type = ByteArrayType(len(reason_str)) placeholder_bytes = Expr(self.stmt.msg, self.context).lll_node method_id = fourbytes_to_int(sha3(b"Error(string)")[:4]) assert_reason = \ ['seq', ['mstore', sig_placeholder, method_id], ['mstore', arg_placeholder, 32], placeholder_bytes, ['assert_reason', test_expr, int(sig_placeholder + 28), int(4 + 32 + get_size_of_type(reason_str_type) * 32)]] return LLLnode.from_list(assert_reason, typ=None, pos=getpos(self.stmt)) else: return LLLnode.from_list(['assert', test_expr], typ=None, pos=getpos(self.stmt))
def _assert_reason(self, test_expr, msg): if isinstance(msg, ast.Name) and msg.id == 'UNREACHABLE': return self._assert_unreachable(test_expr, msg) if not isinstance(msg, ast.Str): raise StructureException( 'Reason parameter of assert needs to be a literal string ' '(or UNREACHABLE constant).', msg) if len(msg.s.strip()) == 0: raise StructureException('Empty reason string not allowed.', self.stmt) reason_str = msg.s.strip() sig_placeholder = self.context.new_placeholder(BaseType(32)) arg_placeholder = self.context.new_placeholder(BaseType(32)) reason_str_type = ByteArrayType(len(reason_str)) placeholder_bytes = Expr(msg, self.context).lll_node method_id = fourbytes_to_int(keccak256(b"Error(string)")[:4]) assert_reason = [ 'seq', ['mstore', sig_placeholder, method_id], ['mstore', arg_placeholder, 32], placeholder_bytes, [ 'assert_reason', test_expr, int(sig_placeholder + 28), int(4 + 32 + get_size_of_type(reason_str_type) * 32), ], ] return LLLnode.from_list(assert_reason, typ=None, pos=getpos(self.stmt))
def _assert_reason(self, test_expr, msg): if isinstance(msg, vy_ast.Name) and msg.id == "UNREACHABLE": return LLLnode.from_list(["assert_unreachable", test_expr], typ=None, pos=getpos(msg)) reason_str = msg.s.strip() sig_placeholder = self.context.new_placeholder(BaseType(32)) arg_placeholder = self.context.new_placeholder(BaseType(32)) reason_str_type = ByteArrayType(len(reason_str)) placeholder_bytes = Expr(msg, self.context).lll_node method_id = fourbytes_to_int(keccak256(b"Error(string)")[:4]) assert_reason = [ "seq", ["mstore", sig_placeholder, method_id], ["mstore", arg_placeholder, 32], placeholder_bytes, [ "assert_reason", test_expr, int(sig_placeholder + 28), int(4 + get_size_of_type(reason_str_type) * 32), ], ] return LLLnode.from_list(assert_reason, typ=None, pos=getpos(self.stmt))
def build_LLL(self, expr, args, kwargs, context): value = kwargs['value'] if context.is_constant(): raise ConstancyViolation( f"Cannot make calls from {context.pp_constancy()}", expr, ) placeholder = context.new_placeholder(ByteArrayType(96)) kode = get_create_forwarder_to_bytecode() high = bytes_to_int(kode[:32]) low = bytes_to_int((kode + b'\x00' * 32)[47:79]) return LLLnode.from_list( [ 'seq', ['mstore', placeholder, high], ['mstore', ['add', placeholder, 27], ['mul', args[0], 2**96]], ['mstore', ['add', placeholder, 47], low], ['clamp_nonzero', ['create', value, placeholder, 96]], ], typ=BaseType('address'), pos=getpos(expr), add_gas_estimate=11000, )
def create_forwarder_to(expr, args, kwargs, context): value = kwargs['value'] if value != zero_value: enforce_units(value.typ, get_keyword(expr, 'value'), BaseType('uint256', {'wei': 1})) if context.is_constant(): raise ConstancyViolationException( "Cannot make calls from %s" % context.pp_constancy(), expr, ) placeholder = context.new_placeholder(ByteArrayType(96)) kode = get_create_forwarder_to_bytecode() high = bytes_to_int(kode[:32]) low = bytes_to_int((kode + b'\x00' * 32)[47:79]) return LLLnode.from_list( [ 'seq', ['mstore', placeholder, high], ['mstore', ['add', placeholder, 27], ['mul', args[0], 2**96]], ['mstore', ['add', placeholder, 47], low], ['clamp_nonzero', ['create', value, placeholder, 96]], ], typ=BaseType('address'), pos=getpos(expr), add_gas_estimate=11000, )
def test_get_size_of_type(): assert get_size_of_type(BaseType('int128')) == 1 assert get_size_of_type(ByteArrayType(12)) == 3 assert get_size_of_type(ByteArrayType(33)) == 4 assert get_size_of_type(ListType(BaseType('int128'), 10)) == 10 _tuple = TupleType([BaseType('int128'), BaseType('decimal')]) assert get_size_of_type(_tuple) == 2 _struct = StructType({'a': BaseType('int128'), 'b': BaseType('decimal')}) assert get_size_of_type(_struct) == 2 # Don't allow unknow types. with raises(Exception): get_size_of_type(int) # Maps are not supported for function arguments or outputs with raises(Exception): get_size_of_type(MappingType(BaseType('int128'), BaseType('int128')))
def to_bytes(expr, args, kwargs, context): in_arg = args[0] if in_arg.typ.maxlen > args[1].slice.value.n: raise TypeMismatchException( 'Cannot convert as input bytes are larger than max length', expr) return LLLnode(value=in_arg.value, args=in_arg.args, typ=ByteArrayType(in_arg.typ.maxlen), pos=getpos(expr), location=in_arg.location)
def test_canonicalize_type(): # Non-basetype not allowed with raises(Exception): canonicalize_type(int) # List of byte arrays not allowed a = ListType(ByteArrayType(12), 2) with raises(Exception): canonicalize_type(a) # Test ABI format of multiple args. c = TupleType([BaseType('int128'), BaseType('address')]) assert canonicalize_type(c) == "(int128,address)"
def raw_call(expr, args, kwargs, context): to, data = args gas, value, outsize = kwargs['gas'], kwargs['value'], kwargs['outsize'] if context.is_constant: raise ConstancyViolationException("Cannot make calls from a constant function", expr) if value != zero_value: enforce_units(value.typ, get_keyword(expr, 'value'), BaseType('int128', {'wei': 1})) placeholder = context.new_placeholder(data.typ) placeholder_node = LLLnode.from_list(placeholder, typ=data.typ, location='memory') copier = make_byte_array_copier(placeholder_node, data) output_placeholder = context.new_placeholder(ByteArrayType(outsize)) output_node = LLLnode.from_list(output_placeholder, typ=ByteArrayType(outsize), location='memory') z = LLLnode.from_list(['seq', copier, ['assert', ['call', gas, to, value, ['add', placeholder_node, 32], ['mload', placeholder_node], ['add', output_node, 32], outsize]], ['mstore', output_node, outsize], output_node], typ=ByteArrayType(outsize), location='memory', pos=getpos(expr)) return z
def ecrecover(expr, args, kwargs, context): placeholder_node = LLLnode.from_list(context.new_placeholder( ByteArrayType(128)), typ=ByteArrayType(128), location='memory') return LLLnode.from_list([ 'seq', ['mstore', placeholder_node, args[0]], ['mstore', ['add', placeholder_node, 32], args[1]], ['mstore', ['add', placeholder_node, 64], args[2]], ['mstore', ['add', placeholder_node, 96], args[3]], [ 'pop', [ 'call', 3000, 1, 0, placeholder_node, 128, MemoryPositions.FREE_VAR_SPACE, 32 ] ], ['mload', MemoryPositions.FREE_VAR_SPACE] ], typ=BaseType('address'), pos=getpos(expr))
def ecmul(expr, args, kwargs, context): placeholder_node = LLLnode.from_list(context.new_placeholder( ByteArrayType(128)), typ=ByteArrayType(128), location='memory') o = LLLnode.from_list([ 'seq', ['mstore', placeholder_node, avo(args[0], 0)], ['mstore', ['add', placeholder_node, 32], avo(args[0], 1)], ['mstore', ['add', placeholder_node, 64], args[1]], [ 'assert', ['call', 40000, 7, 0, placeholder_node, 96, placeholder_node, 64] ], placeholder_node ], typ=ListType(BaseType('num256'), 2), pos=getpos(expr), location='memory') return o
def test_get_size_of_type(): assert get_size_of_type(BaseType("int128")) == 1 assert get_size_of_type(ByteArrayType(12)) == 3 assert get_size_of_type(ByteArrayType(33)) == 4 assert get_size_of_type(ListType(BaseType("int128"), 10)) == 10 _tuple = TupleType([BaseType("int128"), BaseType("decimal")]) assert get_size_of_type(_tuple) == 2 _struct = StructType({ "a": BaseType("int128"), "b": BaseType("decimal") }, "Foo") assert get_size_of_type(_struct) == 2 # Don't allow unknown types. with raises(Exception): get_size_of_type(int) # Maps are not supported for function arguments or outputs with raises(Exception): get_size_of_type(MappingType(BaseType("int128"), BaseType("int128")))
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, in_function_call=True)) 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, (StructType, ListType)): if has_dynamic_data(typ): raise TypeMismatchException("Cannot pack bytearray in struct") target = LLLnode.from_list([placeholder + 32 + staticarray_offset + i * 32], typ=typ, location='memory') setters.append(make_setter(target, arg, 'memory', pos=pos)) if (isinstance(typ, ListType)): count = typ.count else: count = len(typ.tuple_items()) staticarray_offset += 32 * (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 raw_call(expr, args, kwargs, context): to, data = args gas, value, outsize, delegate_call = kwargs['gas'], kwargs['value'], kwargs['outsize'], kwargs['delegate_call'] if delegate_call.typ.is_literal is False: raise TypeMismatchException('The delegate_call parameter has to be a static/literal boolean value.') if context.is_constant: raise ConstancyViolationException("Cannot make calls from a constant function", expr) if value != zero_value: enforce_units(value.typ, get_keyword(expr, 'value'), BaseType('uint256', {'wei': 1})) placeholder = context.new_placeholder(data.typ) placeholder_node = LLLnode.from_list(placeholder, typ=data.typ, location='memory') copier = make_byte_array_copier(placeholder_node, data, pos=getpos(expr)) output_placeholder = context.new_placeholder(ByteArrayType(outsize)) output_node = LLLnode.from_list(output_placeholder, typ=ByteArrayType(outsize), location='memory') if delegate_call.value == 1: z = LLLnode.from_list( ['seq', copier, ['assert', ['delegatecall', gas, to, ['add', placeholder_node, 32], ['mload', placeholder_node], ['add', output_node, 32], outsize]], ['mstore', output_node, outsize], output_node], typ=ByteArrayType(outsize), location='memory', pos=getpos(expr) ) else: z = LLLnode.from_list( ['seq', copier, ['assert', ['call', gas, to, value, ['add', placeholder_node, 32], ['mload', placeholder_node], ['add', output_node, 32], outsize]], ['mstore', output_node, outsize], output_node], typ=ByteArrayType(outsize), location='memory', pos=getpos(expr) ) return z
def create_with_code_of(expr, args, kwargs, context): value = kwargs['value'] if value != zero_value: enforce_units(value.typ, get_keyword(expr, 'value'), BaseType('int128', {'wei': 1})) if context.is_constant: raise ConstancyViolationException("Cannot make calls from a constant function", expr) placeholder = context.new_placeholder(ByteArrayType(96)) kode = b'`.`\x0c`\x009`.`\x00\xf36`\x00`\x007a\x10\x00`\x006`\x00s\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Z\xf4\x15XWa\x10\x00`\x00\xf3' assert len(kode) <= 64 high = bytes_to_int(kode[:32]) low = bytes_to_int((kode + b'\x00' * 32)[47:79]) return LLLnode.from_list(['seq', ['mstore', placeholder, high], ['mstore', ['add', placeholder, 27], ['mul', args[0], 2**96]], ['mstore', ['add', placeholder, 47], low], ['clamp_nonzero', ['create', value, placeholder, 64]]], typ=BaseType('address'), pos=getpos(expr), add_gas_estimate=10000)
def build_LLL(self, expr, args, kwargs, context): to, data = args gas, value, outsize, delegate_call = ( kwargs['gas'], kwargs['value'], kwargs['outsize'], kwargs['is_delegate_call'], ) if delegate_call.typ.is_literal is False: raise TypeMismatch( 'The delegate_call parameter has to be a static/literal boolean value.' ) if context.is_constant(): raise ConstancyViolation( f"Cannot make calls from {context.pp_constancy()}", expr, ) placeholder = context.new_placeholder(data.typ) placeholder_node = LLLnode.from_list(placeholder, typ=data.typ, location='memory') copier = make_byte_array_copier(placeholder_node, data, pos=getpos(expr)) output_placeholder = context.new_placeholder(ByteArrayType(outsize)) output_node = LLLnode.from_list( output_placeholder, typ=ByteArrayType(outsize), location='memory', ) # build LLL for call or delegatecall common_call_lll = [ ['add', placeholder_node, 32], ['mload', placeholder_node], # if there is no return value, the return offset can be 0 ['add', output_node, 32] if outsize else 0, outsize ] if delegate_call.value == 1: call_lll = ['delegatecall', gas, to] + common_call_lll else: call_lll = ['call', gas, to, value] + common_call_lll # build sequence LLL if outsize: # only copy the return value to memory if outsize > 0 seq = [ 'seq', copier, ['assert', call_lll], ['mstore', output_node, outsize], output_node ] typ = ByteArrayType(outsize) else: seq = ['seq', copier, ['assert', call_lll]] typ = None return LLLnode.from_list(seq, typ=typ, location="memory", pos=getpos(expr))
def _RLPlist(expr, args, kwargs, context): # Second argument must be a list of types if not isinstance(args[1], ast.List): raise TypeMismatchException("Expecting list of types for second argument", args[1]) if len(args[1].elts) == 0: raise TypeMismatchException("RLP list must have at least one item", expr) if len(args[1].elts) > 32: raise TypeMismatchException("RLP list must have at most 32 items", expr) # Get the output format _format = [] for arg in args[1].elts: if isinstance(arg, ast.Name) and arg.id == "bytes": subtyp = ByteArrayType(args[0].typ.maxlen) else: subtyp = context.parse_type(arg, 'memory') if not isinstance(subtyp, BaseType): raise TypeMismatchException("RLP lists only accept BaseTypes and byte arrays", arg) if not is_base_type(subtyp, ('int128', 'uint256', 'bytes32', 'address', 'bool')): raise TypeMismatchException("Unsupported base type: %s" % subtyp.typ, arg) _format.append(subtyp) output_type = TupleType(_format) output_placeholder_type = ByteArrayType( (2 * len(_format) + 1 + get_size_of_type(output_type)) * 32, ) output_placeholder = context.new_placeholder(output_placeholder_type) output_node = LLLnode.from_list( output_placeholder, typ=output_placeholder_type, location='memory', ) # Create a decoder for each element in the tuple decoder = [] for i, typ in enumerate(_format): # Decoder for bytes32 if is_base_type(typ, 'bytes32'): decoder.append(LLLnode.from_list( [ 'seq', [ 'assert', [ 'eq', [ 'mload', [ 'add', output_node, ['mload', ['add', output_node, 32 * i]], ], ], 32, ], ], [ 'mload', [ 'add', 32, [ 'add', output_node, ['mload', ['add', output_node, 32 * i]], ], ], ], ], typ, annotation='getting and checking bytes32 item', )) # Decoder for address elif is_base_type(typ, 'address'): decoder.append(LLLnode.from_list( [ 'seq', [ 'assert', [ 'eq', [ 'mload', [ 'add', output_node, ['mload', ['add', output_node, 32 * i]], ], ], 20, ] ], [ 'mod', [ 'mload', [ 'add', 20, ['add', output_node, ['mload', ['add', output_node, 32 * i]]], ], ], ['mload', MemoryPositions.ADDRSIZE], ] ], typ, annotation='getting and checking address item', )) # Decoder for bytes elif isinstance(typ, ByteArrayType): decoder.append(LLLnode.from_list( [ 'add', output_node, ['mload', ['add', output_node, 32 * i]], ], typ, location='memory', annotation='getting byte array', )) # Decoder for num and uint256 elif is_base_type(typ, ('int128', 'uint256')): bytez = LLLnode.from_list( [ 'add', output_node, ['mload', ['add', output_node, 32 * i]], ], typ, location='memory', annotation='getting and checking %s' % typ.typ, ) decoder.append(byte_array_to_num(bytez, expr, typ.typ)) # Decoder for bools elif is_base_type(typ, ('bool')): # This is basically a really clever way to test for a # length-prefixed one or zero. We take the 32 bytes starting one # byte *after* the start of the length declaration; this includes # the last 31 bytes of the length and the first byte of the value. # 0 corresponds to length 0, first byte 0, and 257 corresponds to # length 1, first byte \x01 decoder.append(LLLnode.from_list( [ 'with', '_ans', [ 'mload', [ 'add', 1, ['add', output_node, ['mload', ['add', output_node, 32 * i]]] ], ], [ 'seq', ['assert', ['or', ['eq', '_ans', 0], ['eq', '_ans', 257]]], ['div', '_ans', 257], ], ], typ, annotation='getting and checking bool', )) else: # Should never reach because of top level base level check. raise Exception("Type not yet supported") # pragma: no cover # Copy the input data to memory if args[0].location == "memory": variable_pointer = args[0] elif args[0].location == "storage": placeholder = context.new_placeholder(args[0].typ) placeholder_node = LLLnode.from_list(placeholder, typ=args[0].typ, location='memory') copier = make_byte_array_copier( placeholder_node, LLLnode.from_list('_ptr', typ=args[0].typ, location=args[0].location), ) variable_pointer = ['with', '_ptr', args[0], ['seq', copier, placeholder_node]] else: # Should never reach because of top level base level check. raise Exception("Location not yet supported") # pragma: no cover # Decode the input data initial_setter = LLLnode.from_list( ['seq', ['with', '_sub', variable_pointer, ['pop', ['call', 1500 + 400 * len(_format) + 10 * len(args), LLLnode.from_list(RLP_DECODER_ADDRESS, annotation='RLP decoder'), 0, ['add', '_sub', 32], ['mload', '_sub'], output_node, 64 * len(_format) + 32 + 32 * get_size_of_type(output_type)]]], ['assert', ['eq', ['mload', output_node], 32 * len(_format) + 32]]], typ=None) # Shove the input data decoder in front of the first variable decoder decoder[0] = LLLnode.from_list( ['seq', initial_setter, decoder[0]], typ=decoder[0].typ, location=decoder[0].location, ) return LLLnode.from_list( ["multi"] + decoder, typ=output_type, location='memory', pos=getpos(expr), )
def bytes(self): bytez = self.expr.s bytez_length = len(self.expr.s) return self._make_bytelike(ByteArrayType(bytez_length), bytez, bytez_length)
def pack_arguments(signature, args, context, stmt_expr, is_external_call): pos = getpos(stmt_expr) setters = [] staticarray_offset = 0 maxlen = sum([get_size_of_type(arg.typ) for arg in signature.args]) * 32 if is_external_call: maxlen += 32 placeholder_typ = ByteArrayType(maxlen=maxlen) placeholder = context.new_internal_variable(placeholder_typ) if is_external_call: setters.append(["mstore", placeholder, signature.method_id]) placeholder += 32 if len(signature.args) != len(args): return # check for dynamic-length types dynamic_remaining = len( [i for i in signature.args if isinstance(i.typ, ByteArrayLike)]) needpos = bool(dynamic_remaining) 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 + i * 32, typ=typ, ), arg, "memory", pos=pos, in_function_call=True, )) elif isinstance(typ, ByteArrayLike): dynamic_remaining -= 1 setters.append( ["mstore", placeholder + staticarray_offset + i * 32, "_poz"]) arg_copy = LLLnode.from_list("_s", typ=arg.typ, location=arg.location) target = LLLnode.from_list( ["add", placeholder, "_poz"], typ=typ, location="memory", ) pos_setter = "pass" # if `arg.value` is None, this is a call to `empty()` # if `arg.typ.maxlen` is 0, this is a literal "" or b"" if arg.value is None or arg.typ.maxlen == 0: if dynamic_remaining: # only adjust the dynamic pointer if this is not the last dynamic type pos_setter = ["set", "_poz", ["add", "_poz", 64]] setters.append(["seq", mzero(target, 64), pos_setter]) else: if dynamic_remaining: pos_setter = [ "set", "_poz", [ "add", 32, ["ceil32", ["add", "_poz", get_length(arg_copy)]] ], ] setters.append([ "with", "_s", arg, [ "seq", make_byte_array_copier(target, arg_copy, pos), pos_setter ], ]) elif isinstance(typ, (StructType, ListType)): if has_dynamic_data(typ): return target = LLLnode.from_list( [placeholder + staticarray_offset + i * 32], typ=typ, location="memory", ) setters.append(make_setter(target, arg, "memory", pos=pos)) staticarray_offset += 32 * (get_size_of_type(typ) - 1) else: return if is_external_call: returner = [[placeholder - 4]] inargsize = placeholder_typ.maxlen - 28 else: # internal call does not use a returner or adjust max length for signature returner = [] inargsize = placeholder_typ.maxlen if needpos: return ( LLLnode.from_list( [ "with", "_poz", len(args) * 32 + staticarray_offset, ["seq"] + setters + returner ], typ=placeholder_typ, location="memory", ), inargsize, placeholder, ) else: return ( LLLnode.from_list(["seq"] + setters + returner, typ=placeholder_typ, location="memory"), inargsize, placeholder, )
def bytes(self): bytez = self.expr.s bytez_length = len(self.expr.s) typ = ByteArrayType(bytez_length, is_literal=True) return self._make_bytelike(typ, bytez, bytez_length)
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 parse_Attribute(self): # x.balance: balance of address x if self.expr.attr == "balance": addr = Expr.parse_value_expr(self.expr.value, self.context) if is_base_type(addr.typ, "address"): if (isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id == "self" and version_check(begin="istanbul")): seq = ["selfbalance"] else: seq = ["balance", addr] return LLLnode.from_list( seq, typ=BaseType("uint256"), 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 is_base_type(addr.typ, "address"): if self.expr.attr == "codesize": if self.expr.value.id == "self": eval_code = ["codesize"] else: eval_code = ["extcodesize", addr] output_type = "uint256" else: eval_code = ["gt", ["extcodesize", addr], 0] output_type = "bool" return LLLnode.from_list( eval_code, typ=BaseType(output_type), location=None, pos=getpos(self.expr), ) # x.codehash: keccak of address x elif self.expr.attr == "codehash": addr = Expr.parse_value_expr(self.expr.value, self.context) if not version_check(begin="constantinople"): raise EvmVersionException( "address.codehash is unavailable prior to constantinople ruleset", self.expr) if is_base_type(addr.typ, "address"): return LLLnode.from_list( ["extcodehash", addr], typ=BaseType("bytes32"), location=None, pos=getpos(self.expr), ) # self.x: global attribute elif isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id == "self": type_ = self.expr._metadata["type"] var = self.context.globals[self.expr.attr] return LLLnode.from_list( type_.position.position, typ=var.typ, location="storage", pos=getpos(self.expr), annotation="self." + self.expr.attr, ) # Reserved keywords elif (isinstance(self.expr.value, vy_ast.Name) and self.expr.value.id in ENVIRONMENT_VARIABLES): key = f"{self.expr.value.id}.{self.expr.attr}" if key == "msg.sender" and not self.context.is_internal: return LLLnode.from_list(["caller"], typ="address", pos=getpos(self.expr)) elif key == "msg.data" and not self.context.is_internal: is_len = self.expr._metadata.get("is_len") if is_len is True: typ = ByteArrayType(32) pos = self.context.new_internal_variable(typ) node = ["seq", ["mstore", pos, "calldatasize"], pos] return LLLnode.from_list(node, typ=typ, pos=getpos(self.expr), location="memory") size = self.expr._metadata.get("size") typ = ByteArrayType(size + 32) pos = self.context.new_internal_variable(typ) node = [ "seq", ["assert", ["le", size, "calldatasize"]], ["mstore", pos, size], ["calldatacopy", pos + 32, 0, size], pos, ] return LLLnode.from_list(node, typ=typ, pos=getpos(self.expr), location="memory") elif key == "msg.value" and self.context.is_payable: return LLLnode.from_list( ["callvalue"], typ=BaseType("uint256"), pos=getpos(self.expr), ) elif key == "msg.gas": return LLLnode.from_list( ["gas"], typ="uint256", pos=getpos(self.expr), ) elif key == "block.difficulty": return LLLnode.from_list( ["difficulty"], typ="uint256", pos=getpos(self.expr), ) elif key == "block.timestamp": return LLLnode.from_list( ["timestamp"], typ=BaseType("uint256"), 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="uint256", 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)) elif key == "chain.id": if not version_check(begin="istanbul"): raise EvmVersionException( "chain.id is unavailable prior to istanbul ruleset", self.expr) return LLLnode.from_list(["chainid"], typ="uint256", pos=getpos(self.expr)) # Other variables else: sub = Expr.parse_variable_location(self.expr.value, self.context) # contract type if isinstance(sub.typ, InterfaceType): return sub if isinstance(sub.typ, StructType) and self.expr.attr in sub.typ.members: return add_variable_offset(sub, self.expr.attr, pos=getpos(self.expr))
def concat(expr, context): args = [Expr(arg, context).lll_node for arg in expr.args] if len(args) < 2: raise StructureException("Concat expects at least two arguments", expr) for expr_arg, arg in zip(expr.args, args): if not isinstance(arg.typ, ByteArrayType) and not is_base_type( arg.typ, 'bytes32') and not is_base_type(arg.typ, 'method_id'): raise TypeMismatchException( "Concat expects byte arrays or bytes32 objects", expr_arg) # Maximum length of the output total_maxlen = sum([ arg.typ.maxlen if isinstance(arg.typ, ByteArrayType) else 32 for arg in args ]) # Node representing the position of the output in memory placeholder = context.new_placeholder(ByteArrayType(total_maxlen)) # Object representing the output seq = [] # For each argument we are concatenating... for arg in args: # Start pasting into a position the starts at zero, and keeps # incrementing as we concatenate arguments placeholder_node = LLLnode.from_list(['add', placeholder, '_poz'], typ=ByteArrayType(total_maxlen), location='memory') placeholder_node_plus_32 = LLLnode.from_list( ['add', ['add', placeholder, '_poz'], 32], typ=ByteArrayType(total_maxlen), location='memory') if isinstance(arg.typ, ByteArrayType): # Ignore empty strings if arg.typ.maxlen == 0: continue # Get the length of the current argument if arg.location == "memory": length = LLLnode.from_list(['mload', '_arg'], typ=BaseType('int128')) argstart = LLLnode.from_list(['add', '_arg', 32], typ=arg.typ, location=arg.location) elif arg.location == "storage": length = LLLnode.from_list(['sload', ['sha3_32', '_arg']], typ=BaseType('int128')) argstart = LLLnode.from_list(['add', ['sha3_32', '_arg'], 1], typ=arg.typ, location=arg.location) # Make a copier to copy over data from that argyument seq.append([ 'with', '_arg', arg, [ 'seq', make_byte_slice_copier(placeholder_node_plus_32, argstart, length, arg.typ.maxlen), # Change the position to start at the correct # place to paste the next value ['set', '_poz', ['add', '_poz', length]] ] ]) elif isinstance(arg.typ, BaseType) and arg.typ.typ == "method_id": seq.append([ 'seq', ['mstore', ['add', placeholder_node, 32], arg.value * 2**224], ['set', '_poz', ['add', '_poz', 4]] ]) else: seq.append([ 'seq', [ 'mstore', ['add', placeholder_node, 32], unwrap_location(arg) ], ['set', '_poz', ['add', '_poz', 32]] ]) # The position, after all arguments are processing, equals the total # length. Paste this in to make the output a proper bytearray seq.append(['mstore', placeholder, '_poz']) # Memory location of the output seq.append(placeholder) return LLLnode.from_list(['with', '_poz', 0, ['seq'] + seq], typ=ByteArrayType(total_maxlen), location='memory', pos=getpos(expr), annotation='concat')