def mem_extend(mem, compustate, op, start, sz): if sz: newsize = start + sz if len(mem) < utils.ceil32(newsize): m_extend = utils.ceil32(newsize) - len(mem) memfee = opcodes.GMEMORY * (m_extend / 32) if compustate.gas < memfee: compustate.gas = 0 return False compustate.gas -= memfee mem.extend([0] * m_extend) return True
def data_copy(compustate, size): if size: copyfee = opcodes.GCOPY * utils.ceil32(size) / 32 if compustate.gas < copyfee: compustate.gas = 0 return False compustate.gas -= copyfee return True
def proc_identity(ext, msg): print 'identity proc', msg.gas OP_GAS = 1 + (utils.ceil32(msg.data.size) / 32) gas_cost = OP_GAS if msg.gas < gas_cost: return 0, 0, [] o = [0] * msg.data.size msg.data.extract_copy(o, 0, 0, len(o)) return 1, msg.gas - gas_cost, o
def proc_ripemd160(ext, msg): print 'ripemd160 proc', msg.gas OP_GAS = 50 + (utils.ceil32(msg.data.size) / 32) * 50 gas_cost = OP_GAS if msg.gas < gas_cost: return 0, 0, [] d = msg.data.extract_all() o = [0] * 12 + [ord(x) for x in bitcoin.ripemd.RIPEMD160(d).digest()] return 1, msg.gas - gas_cost, o
def proc_sha256(ext, msg): print 'sha256 proc', msg.gas OP_GAS = 50 + (utils.ceil32(msg.data.size) / 32) * 50 gas_cost = OP_GAS if msg.gas < gas_cost: return 0, 0, [] d = msg.data.extract_all() o = [ord(x) for x in bitcoin.bin_sha256(d)] return 1, msg.gas - gas_cost, o
def dec(typ, arg): base, sub, arrlist = typ sz = get_size(typ) # Dynamic-sized strings are encoded as <len(str)> + <str> if base in ('string', 'bytes') and not sub: L = big_endian_to_int(arg[:32]) assert len( arg[32:]) == ceil32(L), "Wrong data size for string/bytes object" return arg[32:][:L] # Dynamic-sized arrays elif sz is None: L = big_endian_to_int(arg[:32]) subtyp = base, sub, arrlist[:-1] subsize = get_size(subtyp) # If children are dynamic, use the head/tail mechanism. Fortunately, # here the code is simpler since we do not have to worry about # mixed dynamic and static children, as we do in the top-level multi-arg # case if subsize is None: assert len(arg) >= 32 + 32 * L, "Not enough data for head" start_positions = [ big_endian_to_int(arg[32 + 32 * i:64 + 32 * i]) for i in range(L) ] + [len(arg)] outs = [ arg[start_positions[i]:start_positions[i + 1]] for i in range(L) ] return [dec(subtyp, out) for out in outs] # If children are static, then grab the data slice for each one and # sequentially decode them manually else: return [ dec(subtyp, arg[32 + subsize * i:32 + subsize * (i + 1)]) for i in range(L) ] # Static-sized arrays: decode piece-by-piece elif len(arrlist): L = arrlist[-1][0] subtyp = base, sub, arrlist[:-1] subsize = get_size(subtyp) return [ dec(subtyp, arg[subsize * i:subsize * (i + 1)]) for i in range(L) ] else: return decode_single(typ, arg)
def enc(typ, arg): base, sub, arrlist = typ sz = get_size(typ) # Encode dynamic-sized strings as <len(str)> + <str> if base in ('string', 'bytes') and not sub: assert isinstance(arg, (str, bytes, utils.unicode)), \ "Expecting a string" return enc(lentyp, len(arg)) + \ utils.to_string(arg) + \ b'\x00' * (utils.ceil32(len(arg)) - len(arg)) # Encode dynamic-sized lists via the head/tail mechanism described in # https://github.com/ethereum/wiki/wiki/Proposal-for-new-ABI-value-encoding elif sz is None: assert isinstance(arg, list), \ "Expecting a list argument" subtyp = base, sub, arrlist[:-1] subsize = get_size(subtyp) myhead, mytail = b'', b'' if arrlist[-1] == []: myhead += enc(lentyp, len(arg)) else: assert len(arg) == arrlist[-1][0], \ "Wrong array size: found %d, expecting %d" % \ (len(arg), arrlist[-1][0]) for i in range(len(arg)): if subsize is None: myhead += enc(lentyp, 32 * len(arg) + len(mytail)) mytail += enc(subtyp, arg[i]) else: myhead += enc(subtyp, arg[i]) return myhead + mytail # Encode static-sized lists via sequential packing else: if arrlist == []: return utils.to_string(encode_single(typ, arg)) else: subtyp = base, sub, arrlist[:-1] o = b'' for x in arg: o += enc(subtyp, x) return o
def dec(typ, arg): base, sub, arrlist = typ sz = get_size(typ) # Dynamic-sized strings are encoded as <len(str)> + <str> if base in ('string', 'bytes') and not sub: L = big_endian_to_int(arg[:32]) assert len(arg[32:]) == ceil32(L), "Wrong data size for string/bytes object" return arg[32:][:L] # Dynamic-sized arrays elif sz is None: L = big_endian_to_int(arg[:32]) subtyp = base, sub, arrlist[:-1] subsize = get_size(subtyp) # If children are dynamic, use the head/tail mechanism. Fortunately, # here the code is simpler since we do not have to worry about # mixed dynamic and static children, as we do in the top-level multi-arg # case if subsize is None: assert len(arg) >= 32 + 32 * L, "Not enough data for head" start_positions = [big_endian_to_int(arg[32 + 32 * i: 64 + 32 * i]) for i in range(L)] + [len(arg)] outs = [arg[start_positions[i]: start_positions[i + 1]] for i in range(L)] return [dec(subtyp, out) for out in outs] # If children are static, then grab the data slice for each one and # sequentially decode them manually else: return [dec(subtyp, arg[32 + subsize * i: 32 + subsize * (i + 1)]) for i in range(L)] # Static-sized arrays: decode piece-by-piece elif len(arrlist): L = arrlist[-1][0] subtyp = base, sub, arrlist[:-1] subsize = get_size(subtyp) return [dec(subtyp, arg[subsize * i:subsize * (i + 1)]) for i in range(L)] else: return decode_single(typ, arg)
def encode_single(typ, arg): base, sub, _ = typ # Unsigned integers: uint<sz> if base == 'uint': sub = int(sub) i = decint(arg, False) if not 0 <= i < 2 ** sub: raise ValueOutOfBounds(repr(arg)) return zpad(encode_int(i), 32) # bool: int<sz> elif base == 'bool': assert isinstance(arg, bool) return zpad(encode_int(int(arg)), 32) # Signed integers: int<sz> elif base == 'int': sub = int(sub) i = decint(arg, True) if not -2 ** (sub - 1) <= i < 2 ** (sub - 1): raise ValueOutOfBounds(repr(arg)) return zpad(encode_int(i % 2 ** sub), 32) # Unsigned reals: ureal<high>x<low> elif base == 'ureal': high, low = [int(x) for x in sub.split('x')] if not 0 <= arg < 2 ** high: raise ValueOutOfBounds(repr(arg)) return zpad(encode_int(int(arg * 2 ** low)), 32) # Signed reals: real<high>x<low> elif base == 'real': high, low = [int(x) for x in sub.split('x')] if not -2 ** (high - 1) <= arg < 2 ** (high - 1): raise ValueOutOfBounds(repr(arg)) i = int(arg * 2 ** low) return zpad(encode_int(i % 2 ** (high + low)), 32) # Strings elif base == 'string' or base == 'bytes': if not is_string(arg): raise EncodingError("Expecting string: %r" % arg) # Fixed length: string<sz> if len(sub): assert int(sub) <= 32 assert len(arg) <= int(sub) return arg + b'\x00' * (32 - len(arg)) # Variable length: string else: return zpad(encode_int(len(arg)), 32) + \ arg + \ b'\x00' * (utils.ceil32(len(arg)) - len(arg)) # Hashes: hash<sz> elif base == 'hash': if not (int(sub) and int(sub) <= 32): raise EncodingError("too long: %r" % arg) if isnumeric(arg): return zpad(encode_int(arg), 32) elif len(arg) == int(sub): return zpad(arg, 32) elif len(arg) == int(sub) * 2: return zpad(decode_hex(arg), 32) else: raise EncodingError("Could not parse hash: %r" % arg) # Addresses: address (== hash160) elif base == 'address': assert sub == '' if isnumeric(arg): return zpad(encode_int(arg), 32) elif len(arg) == 20: return zpad(arg, 32) elif len(arg) == 40: return zpad(decode_hex(arg), 32) elif len(arg) == 42 and arg[:2] in {'0x', b'0x'}: return zpad(decode_hex(arg[2:]), 32) else: raise EncodingError("Could not parse address: %r" % arg) raise EncodingError("Unhandled type: %r %r" % (base, sub))
def vm_execute(ext, msg, code): # precompute trace flag # if we trace vm, we're in slow mode anyway trace_vm = log_vm_op.is_active('trace') compustate = Compustate(gas=msg.gas) stk = compustate.stack mem = compustate.memory if code in code_cache: processed_code = code_cache[code] else: processed_code = preprocess_code(code) code_cache[code] = processed_code codelen = len(processed_code) while 1: if compustate.pc >= codelen: return peaceful_exit('CODE OUT OF RANGE', compustate.gas, []) op, in_args, out_args, fee, opcode, pushval = \ processed_code[compustate.pc] # out of gas error if fee > compustate.gas: return vm_exception('OUT OF GAS') # empty stack error if in_args > len(compustate.stack): return vm_exception('INSUFFICIENT STACK', op=op, needed=str(in_args), available=str(len(compustate.stack))) # Apply operation compustate.gas -= fee compustate.pc += 1 if trace_vm: """ This diverges from normal logging, as we use the logging namespace only to decide which features get logged in 'eth.vm.op' i.e. tracing can not be activated by activating a sub like 'eth.vm.op.stack' """ trace_data = {} if log_vm_op_stack.is_active(): trace_data['stack'] = map(str, list(compustate.stack)) if log_vm_op_memory.is_active(): trace_data['memory'] = \ ''.join([chr(x).encode('hex') for x in compustate.memory]) if log_vm_op_storage.is_active(): trace_data['storage'] = ext.log_storage(msg.to) trace_data['gas'] = str(compustate.gas + fee) trace_data['pc'] = str(compustate.pc - 1) trace_data['op'] = op if op[:4] == 'PUSH': trace_data['pushvalue'] = pushval log_vm_op.trace('vm', **trace_data) # Invalid operation if op == 'INVALID': return vm_exception('INVALID OP', opcode=opcode) # Valid operations if opcode < 0x10: if op == 'STOP': return peaceful_exit('STOP', compustate.gas, []) elif op == 'ADD': stk.append((stk.pop() + stk.pop()) & TT256M1) elif op == 'SUB': stk.append((stk.pop() - stk.pop()) & TT256M1) elif op == 'MUL': stk.append((stk.pop() * stk.pop()) & TT256M1) elif op == 'DIV': s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 / s1) elif op == 'MOD': s0, s1 = stk.pop(), stk.pop() stk.append(0 if s1 == 0 else s0 % s1) elif op == 'SDIV': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) // abs(s1) * (-1 if s0 * s1 < 0 else 1)) & TT256M1) elif op == 'SMOD': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(0 if s1 == 0 else (abs(s0) % abs(s1) * (-1 if s0 < 0 else 1)) & TT256M1) elif op == 'ADDMOD': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 + s1) % s2 if s2 else 0) elif op == 'MULMOD': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() stk.append((s0 * s1) % s2 if s2 else 0) elif op == 'EXP': base, exponent = stk.pop(), stk.pop() # fee for exponent is dependent on its bytes # calc n bytes to represent exponent nbytes = len(utils.encode_int(exponent)) expfee = nbytes * opcodes.GEXPONENTBYTE if compustate.gas < expfee: compustate.gas = 0 return vm_exception('OOG EXPONENT') compustate.gas -= expfee stk.append(pow(base, exponent, TT256)) elif op == 'SIGNEXTEND': s0, s1 = stk.pop(), stk.pop() if s0 <= 31: testbit = s0 * 8 + 7 if s1 & (1 << testbit): stk.append(s1 | (TT256 - (1 << testbit))) else: stk.append(s1 & ((1 << testbit) - 1)) else: stk.append(s1) elif opcode < 0x20: if op == 'LT': stk.append(1 if stk.pop() < stk.pop() else 0) elif op == 'GT': stk.append(1 if stk.pop() > stk.pop() else 0) elif op == 'SLT': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(1 if s0 < s1 else 0) elif op == 'SGT': s0, s1 = utils.to_signed(stk.pop()), utils.to_signed(stk.pop()) stk.append(1 if s0 > s1 else 0) elif op == 'EQ': stk.append(1 if stk.pop() == stk.pop() else 0) elif op == 'ISZERO': stk.append(0 if stk.pop() else 1) elif op == 'AND': stk.append(stk.pop() & stk.pop()) elif op == 'OR': stk.append(stk.pop() | stk.pop()) elif op == 'XOR': stk.append(stk.pop() ^ stk.pop()) elif op == 'NOT': stk.append(TT256M1 - stk.pop()) elif op == 'BYTE': s0, s1 = stk.pop(), stk.pop() if s0 >= 32: stk.append(0) else: stk.append((s1 / 256**(31 - s0)) % 256) elif opcode < 0x40: if op == 'SHA3': s0, s1 = stk.pop(), stk.pop() compustate.gas -= 10 * (utils.ceil32(s1) / 32) if compustate.gas < 0: return vm_exception('OOG PAYING FOR SHA3') if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') data = ''.join(map(chr, mem[s0:s0 + s1])) stk.append(utils.big_endian_to_int(utils.sha3(data))) elif op == 'ADDRESS': stk.append(utils.coerce_to_int(msg.to)) elif op == 'BALANCE': addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(ext.get_balance(addr)) elif op == 'ORIGIN': stk.append(utils.coerce_to_int(ext.tx_origin)) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': stk.append(msg.data.extract32(stk.pop())) elif op == 'CALLDATASIZE': stk.append(msg.data.size) elif op == 'CALLDATACOPY': mstart, dstart, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') msg.data.extract_copy(mem, mstart, dstart, size) elif op == 'CODESIZE': stk.append(len(processed_code)) elif op == 'CODECOPY': start, s1, size = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s1 + i < len(processed_code): mem[start + i] = processed_code[s1 + i][4] else: mem[start + i] = 0 elif op == 'GASPRICE': stk.append(ext.tx_gasprice) elif op == 'EXTCODESIZE': addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) stk.append(len(ext.get_code(addr) or '')) elif op == 'EXTCODECOPY': addr = utils.coerce_addr_to_hex(stk.pop() % 2**160) start, s2, size = stk.pop(), stk.pop(), stk.pop() extcode = ext.get_code(addr) or '' if not mem_extend(mem, compustate, op, start, size): return vm_exception('OOG EXTENDING MEMORY') if not data_copy(compustate, size): return vm_exception('OOG COPY DATA') for i in range(size): if s2 + i < len(extcode): mem[start + i] = ord(extcode[s2 + i]) else: mem[start + i] = 0 elif opcode < 0x50: if op == 'BLOCKHASH': stk.append(utils.big_endian_to_int(ext.block_hash(stk.pop()))) elif op == 'COINBASE': stk.append( utils.big_endian_to_int(ext.block_coinbase.decode('hex'))) elif op == 'TIMESTAMP': stk.append(ext.block_timestamp) elif op == 'NUMBER': stk.append(ext.block_number) elif op == 'DIFFICULTY': stk.append(ext.block_difficulty) elif op == 'GASLIMIT': stk.append(ext.block_gas_limit) elif opcode < 0x60: if op == 'POP': stk.pop() elif op == 'MLOAD': s0 = stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') data = ''.join(map(chr, mem[s0:s0 + 32])) stk.append(utils.big_endian_to_int(data)) elif op == 'MSTORE': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 32): return vm_exception('OOG EXTENDING MEMORY') v = s1 for i in range(31, -1, -1): mem[s0 + i] = v % 256 v /= 256 elif op == 'MSTORE8': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, 1): return vm_exception('OOG EXTENDING MEMORY') mem[s0] = s1 % 256 elif op == 'SLOAD': stk.append(ext.get_storage_data(msg.to, stk.pop())) elif op == 'SSTORE': s0, s1 = stk.pop(), stk.pop() if ext.get_storage_data(msg.to, s0): gascost = opcodes.GSTORAGEMOD if s1 else opcodes.GSTORAGEKILL else: gascost = opcodes.GSTORAGEADD if s1 else opcodes.GSTORAGEMOD if compustate.gas < gascost: return vm_exception('OUT OF GAS') compustate.gas -= max(gascost, 0) ext.add_refund(max( -gascost, 0)) # adds neg gascost as a refund if below zero ext.set_storage_data(msg.to, s0, s1) elif op == 'JUMP': compustate.pc = stk.pop() opnew = processed_code[compustate.pc][0] if \ compustate.pc < len(processed_code) else 'STOP' if opnew != 'JUMPDEST': return vm_exception('BAD JUMPDEST') elif op == 'JUMPI': s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 opnew = processed_code[compustate.pc][0] if \ compustate.pc < len(processed_code) else 'STOP' if opnew != 'JUMPDEST': return vm_exception('BAD JUMPDEST') elif op == 'PC': stk.append(compustate.pc - 1) elif op == 'MSIZE': stk.append(len(mem)) elif op == 'GAS': stk.append(compustate.gas) # AFTER subtracting cost 1 elif op[:4] == 'PUSH': pushnum = int(op[4:]) compustate.pc += pushnum stk.append(pushval) elif op[:3] == 'DUP': depth = int(op[3:]) stk.append(stk[-depth]) elif op[:4] == 'SWAP': depth = int(op[4:]) temp = stk[-depth - 1] stk[-depth - 1] = stk[-1] stk[-1] = temp elif op[:3] == 'LOG': """ 0xa0 ... 0xa4, 32/64/96/128/160 + len(data) gas a. Opcodes LOG0...LOG4 are added, takes 2-6 stack arguments MEMSTART MEMSZ (TOPIC1) (TOPIC2) (TOPIC3) (TOPIC4) b. Logs are kept track of during tx execution exactly the same way as suicides (except as an ordered list, not a set). Each log is in the form [address, [topic1, ... ], data] where: * address is what the ADDRESS opcode would output * data is mem[MEMSTART: MEMSTART + MEMSZ] * topics are as provided by the opcode c. The ordered list of logs in the transaction are expressed as [log0, log1, ..., logN]. """ depth = int(op[3:]) mstart, msz = stk.pop(), stk.pop() topics = [stk.pop() for x in range(depth)] compustate.gas -= msz if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') data = ''.join(map(chr, mem[mstart:mstart + msz])) ext.log(msg.to, topics, data) log_log.trace('LOG', to=msg.to, topics=topics, data=map(ord, data)) print('LOG', msg.to, topics, map(ord, data)) elif op == 'CREATE': value, mstart, msz = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart, msz): return vm_exception('OOG EXTENDING MEMORY') if ext.get_balance(msg.to) >= value and msg.depth < 1024: cd = CallData(mem, mstart, msz) create_msg = Message(msg.to, '', value, compustate.gas, cd, msg.depth + 1) o, gas, addr = ext.create(create_msg) if o: stk.append(utils.coerce_to_int(addr)) compustate.gas = gas else: stk.append(0) compustate.gas = 0 else: stk.append(0) elif op == 'CALL': gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') if compustate.gas < gas: return vm_exception('OUT OF GAS') if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= gas to = utils.encode_int(to) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') cd = CallData(mem, meminstart, meminsz) call_msg = Message(msg.to, to, value, gas, cd, msg.depth + 1) result, gas, data = ext.call(call_msg) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: stk.append(0) elif op == 'CALLCODE': gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, meminstart, meminsz) or \ not mem_extend(mem, compustate, op, memoutstart, memoutsz): return vm_exception('OOG EXTENDING MEMORY') if compustate.gas < gas: return vm_exception('OUT OF GAS') if ext.get_balance(msg.to) >= value and msg.depth < 1024: compustate.gas -= gas to = utils.encode_int(to) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') cd = CallData(mem, meminstart, meminsz) call_msg = Message(msg.to, msg.to, value, gas, cd, msg.depth + 1) result, gas, data = ext.sendmsg(call_msg, ext.get_code(to)) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), memoutsz)): mem[memoutstart + i] = data[i] else: stk.append(0) elif op == 'RETURN': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0, s1): return vm_exception('OOG EXTENDING MEMORY') return peaceful_exit('RETURN', compustate.gas, mem[s0:s0 + s1]) elif op == 'SUICIDE': to = utils.encode_int(stk.pop()) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') xfer = ext.get_balance(msg.to) ext.set_balance(msg.to, 0) ext.set_balance(to, ext.get_balance(to) + xfer) ext.add_suicide(msg.to) return 1, compustate.gas, []
def encode_single(typ, arg): base, sub, _ = typ # Unsigned integers: uint<sz> if base == 'uint': sub = int(sub) i = decint(arg, False) if not 0 <= i < 2**sub: raise ValueOutOfBounds(repr(arg)) return zpad(encode_int(i), 32) # bool: int<sz> elif base == 'bool': assert isinstance(arg, bool) return zpad(encode_int(int(arg)), 32) # Signed integers: int<sz> elif base == 'int': sub = int(sub) i = decint(arg, True) if not -2**(sub - 1) <= i < 2**(sub - 1): raise ValueOutOfBounds(repr(arg)) return zpad(encode_int(i % 2**sub), 32) # Unsigned reals: ureal<high>x<low> elif base == 'ureal': high, low = [int(x) for x in sub.split('x')] if not 0 <= arg < 2**high: raise ValueOutOfBounds(repr(arg)) return zpad(encode_int(int(arg * 2**low)), 32) # Signed reals: real<high>x<low> elif base == 'real': high, low = [int(x) for x in sub.split('x')] if not -2**(high - 1) <= arg < 2**(high - 1): raise ValueOutOfBounds(repr(arg)) i = int(arg * 2**low) return zpad(encode_int(i % 2**(high + low)), 32) # Strings elif base == 'string' or base == 'bytes': if not is_string(arg): raise EncodingError("Expecting string: %r" % arg) # Fixed length: string<sz> if len(sub): assert int(sub) <= 32 assert len(arg) <= int(sub) return arg + b'\x00' * (32 - len(arg)) # Variable length: string else: return zpad(encode_int(len(arg)), 32) + \ arg + \ b'\x00' * (utils.ceil32(len(arg)) - len(arg)) # Hashes: hash<sz> elif base == 'hash': if not (int(sub) and int(sub) <= 32): raise EncodingError("too long: %r" % arg) if isnumeric(arg): return zpad(encode_int(arg), 32) elif len(arg) == int(sub): return zpad(arg, 32) elif len(arg) == int(sub) * 2: return zpad(decode_hex(arg), 32) else: raise EncodingError("Could not parse hash: %r" % arg) # Addresses: address (== hash160) elif base == 'address': assert sub == '' if isnumeric(arg): return zpad(encode_int(arg), 32) elif len(arg) == 20: return zpad(arg, 32) elif len(arg) == 40: return zpad(decode_hex(arg), 32) elif len(arg) == 42 and arg[:2] == '0x': return zpad(decode_hex(arg[2:]), 32) else: raise EncodingError("Could not parse address: %r" % arg) raise EncodingError("Unhandled type: %r %r" % (base, sub))