def OP_EXTCODECOPY(): addr, s1, s2, s3 = stk.pop(), stk.pop(), stk.pop(), stk.pop() extcode = block.get_code(utils.coerce_addr_to_hex(addr)) or '' if not mem_extend(mem, msgtop.compustate, '', s1, s3): return drop(OUT_OF_GAS) for i in range(s3): if s2 + i < len(extcode): mem[s1 + i] = ord(extcode[s2 + i]) else: mem[s1 + i] = 0
def OP_BALANCE(): stk.append(block.get_balance(utils.coerce_addr_to_hex(stk.pop())))
def apply_op(block, tx, msg, code, compustate): op, in_args, out_args = get_op_data(code, compustate.pc) # empty stack error if in_args > len(compustate.stack): return [] # out of gas error fee = calcfee(block, tx, msg, compustate, op) if fee > compustate.gas: logger.debug("Out of gas %s need %s", compustate.gas, fee) logger.debug('%s %s', op, list(reversed(compustate.stack))) return OUT_OF_GAS stackargs = [] for i in range(in_args): stackargs.append(compustate.stack.pop()) if op[:4] == 'PUSH': ind = compustate.pc + 1 v = utils.big_endian_to_int(code[ind: ind + int(op[4:])]) logger.debug('%s %x %s', compustate.pc, op, v) # print '%s %s %s' % (compustate.pc, op, v) else: logger.debug('%s %s %s', compustate.pc, op, stackargs) # print '%s %s %s' % (compustate.pc, op, stackargs) # Apply operation oldgas = compustate.gas oldpc = compustate.pc compustate.gas -= fee compustate.pc += 1 stk = compustate.stack mem = compustate.memory if op == 'STOP': return [] elif op == 'ADD': stk.append((stackargs[0] + stackargs[1]) % 2 ** 256) elif op == 'SUB': stk.append((stackargs[0] - stackargs[1]) % 2 ** 256) elif op == 'MUL': stk.append((stackargs[0] * stackargs[1]) % 2 ** 256) elif op == 'DIV': if stackargs[1] == 0: return [] stk.append(stackargs[0] / stackargs[1]) elif op == 'MOD': if stackargs[1] == 0: return [] stk.append(stackargs[0] % stackargs[1]) elif op == 'SDIV': if stackargs[1] == 0: return [] if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append((stackargs[0] / stackargs[1]) % 2 ** 256) elif op == 'SMOD': if stackargs[1] == 0: return [] if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append((stackargs[0] % stackargs[1]) % 2 ** 256) elif op == 'EXP': stk.append(pow(stackargs[0], stackargs[1], 2 ** 256)) elif op == 'NEG': stk.append(2 ** 256 - stackargs[0]) elif op == 'LT': stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'GT': stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'SLT': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'SGT': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'EQ': stk.append(1 if stackargs[0] == stackargs[1] else 0) elif op == 'NOT': stk.append(0 if stackargs[0] else 1) elif op == 'AND': stk.append(stackargs[0] & stackargs[1]) elif op == 'OR': stk.append(stackargs[0] | stackargs[1]) elif op == 'XOR': stk.append(stackargs[0] ^ stackargs[1]) elif op == 'BYTE': if stackargs[0] >= 32: stk.append(0) else: stk.append((stackargs[1] / 256 ** stackargs[0]) % 256) elif op == 'SHA3': if len(mem) < ceil32(stackargs[0] + stackargs[1]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[1]) - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + stackargs[1]])) stk.append(utils.big_endian_to_int(utils.sha3(data))) elif op == 'ADDRESS': stk.append(msg.to) elif op == 'BALANCE': stk.append(block.get_balance(msg.to)) elif op == 'ORIGIN': stk.append(tx.sender) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': if stackargs[0] >= len(msg.data): stk.append(0) else: dat = msg.data[stackargs[0]:stackargs[0] + 32] stk.append(utils.big_endian_to_int(dat + '\x00' * (32 - len(dat)))) elif op == 'CALLDATASIZE': stk.append(len(msg.data)) elif op == 'CALLDATACOPY': if len(mem) < ceil32(stackargs[1] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[1] + stackargs[2]) - len(mem))) for i in range(stackargs[2]): if stackargs[0] + i < len(msg.data): mem[stackargs[1] + i] = ord(msg.data[stackargs[0] + i]) else: mem[stackargs[1] + i] = 0 elif op == 'GASPRICE': stk.append(tx.gasprice) elif op == 'CODECOPY': if len(mem) < ceil32(stackargs[1] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[1] + stackargs[2]) - len(mem))) for i in range(stackargs[2]): if stackargs[0] + i < len(code): mem[stackargs[1] + i] = ord(code[stackargs[0] + i]) else: mem[stackargs[1] + i] = 0 elif op == 'PREVHASH': stk.append(utils.big_endian_to_int(block.prevhash)) elif op == 'COINBASE': stk.append(utils.big_endian_to_int(block.coinbase.decode('hex'))) elif op == 'TIMESTAMP': stk.append(block.timestamp) elif op == 'NUMBER': stk.append(block.number) elif op == 'DIFFICULTY': stk.append(block.difficulty) elif op == 'GASLIMIT': stk.append(block.gaslimit) elif op == 'POP': pass elif op == 'DUP': stk.append(stackargs[0]) stk.append(stackargs[0]) elif op == 'SWAP': stk.append(stackargs[0]) stk.append(stackargs[1]) elif op == 'MLOAD': if len(mem) < ceil32(stackargs[0] + 32): mem.extend([0] * (ceil32(stackargs[0] + 32) - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + 32])) stk.append(utils.big_endian_to_int(data)) elif op == 'MSTORE': if len(mem) < ceil32(stackargs[0] + 32): mem.extend([0] * (ceil32(stackargs[0] + 32) - len(mem))) v = stackargs[1] for i in range(31, -1, -1): mem[stackargs[0] + i] = v % 256 v /= 256 elif op == 'MSTORE8': if len(mem) < ceil32(stackargs[0] + 1): mem.extend([0] * (ceil32(stackargs[0] + 1) - len(mem))) mem[stackargs[0]] = stackargs[1] % 256 elif op == 'SLOAD': stk.append(block.get_storage_data(msg.to, stackargs[0])) elif op == 'SSTORE': block.set_storage_data(msg.to, stackargs[0], stackargs[1]) elif op == 'JUMP': compustate.pc = stackargs[0] elif op == 'JUMPI': if stackargs[1]: compustate.pc = stackargs[0] elif op == 'PC': stk.append(compustate.pc) elif op == 'MSIZE': stk.append(len(mem)) elif op == 'GAS': stk.append(oldgas) elif op[:4] == 'PUSH': pushnum = int(op[4:]) compustate.pc = oldpc + 1 + pushnum dat = code[oldpc + 1: oldpc + 1 + pushnum] stk.append(utils.big_endian_to_int(dat)) elif op == 'CREATE': if len(mem) < ceil32(stackargs[1] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[1] + stackargs[2]) - len(mem))) value = stackargs[0] data = ''.join(map(chr, mem[stackargs[1]:stackargs[1] + stackargs[2]])) logger.debug("Sub-contract: %s %s %s ", msg.to, value, data) addr, gas, code = create_contract( block, tx, Message(msg.to, '', value, compustate.gas, data)) logger.debug("Output of contract creation: %s %s ", addr, code) if addr: stk.append(utils.coerce_to_int(addr)) compustate.gas = gas else: stk.append(0) compustate.gas = 0 elif op == 'CALL': if len(mem) < ceil32(stackargs[3] + stackargs[4]): mem.extend([0] * (ceil32(stackargs[3] + stackargs[4]) - len(mem))) if len(mem) < ceil32(stackargs[5] + stackargs[6]): mem.extend([0] * (ceil32(stackargs[5] + stackargs[6]) - len(mem))) gas = stackargs[0] to = utils.encode_int(stackargs[1]) to = (('\x00' * (32 - len(to))) + to)[12:] value = stackargs[2] data = ''.join(map(chr, mem[stackargs[3]:stackargs[3] + stackargs[4]])) logger.debug( "Sub-call: %s %s %s %s %s ", utils.coerce_addr_to_hex(msg.to), utils.coerce_addr_to_hex(to), value, gas, data) result, gas, data = apply_msg( block, tx, Message(msg.to, to, value, gas, data)) logger.debug( "Output of sub-call: %s %s length %s expected %s", result, data, len(data), stackargs[6]) for i in range(stackargs[6]): mem[stackargs[5] + i] = 0 if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(len(data)): mem[stackargs[5] + i] = data[i] elif op == 'RETURN': if len(mem) < ceil32(stackargs[0] + stackargs[1]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[1]) - len(mem))) return mem[stackargs[0]:stackargs[0] + stackargs[1]] elif op == 'SUICIDE': to = utils.encode_int(stackargs[0]) to = (('\x00' * (32 - len(to))) + to)[12:] block.transfer_value(msg.to, to, block.get_balance(msg.to)) compustate.suicides.append(msg.to) return []
def apply_op(block, tx, msg, processed_code, compustate): if compustate.pc >= len(processed_code): return [] op, in_args, out_args, mem_grabs, fee, opcode = processed_code[ compustate.pc] # empty stack error if in_args > len(compustate.stack): pblogger.log('INSUFFICIENT STACK ERROR', op=op, needed=in_args, available=len(compustate.stack)) return [] # out of gas error if fee > compustate.gas: return out_of_gas_exception('base_gas', fee, compustate, op) if pblogger.log_apply_op: if pblogger.log_stack: pblogger.log('STK', stk=list(reversed(compustate.stack))) if pblogger.log_memory: for i in range(0, len(compustate.memory), 16): memblk = compustate.memory[i:i + 16] memline = ' '.join([chr(x).encode('hex') for x in memblk]) pblogger.log('MEM', mem=memline) if pblogger.log_storage: pblogger.log('STORAGE', storage=block.account_to_dict(msg.to)['storage']) if pblogger.log_op: log_args = dict(pc=compustate.pc, op=op, stackargs=compustate.stack[-1:-in_args - 1:-1], gas=compustate.gas) if op[:4] == 'PUSH': ind = compustate.pc + 1 log_args['value'] = \ utils.bytearray_to_int([x[-1] for x in processed_code[ind: ind + int(op[4:])]]) elif op == 'CALLDATACOPY': log_args['data'] = msg.data.encode('hex') pblogger.log('OP', **log_args) # Apply operation compustate.gas -= fee compustate.pc += 1 stk = compustate.stack mem = compustate.memory if op == 'STOP' or op == 'INVALID': return [] elif op == 'ADD': stk.append((stk.pop() + stk.pop()) % TT256) elif op == 'SUB': stk.append((stk.pop() - stk.pop()) % TT256) elif op == 'MUL': stk.append((stk.pop() * stk.pop()) % TT256) 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 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(0 if s1 == 0 else (s0 / s1) % TT256) elif op == 'SMOD': s0, s1 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(0 if s1 == 0 else (s0 % s1) % TT256) elif op == 'EXP': stk.append(pow(stk.pop(), stk.pop(), TT256)) elif op == 'NEG': stk.append(-stk.pop() % TT256) elif 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 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(1 if s0 < s1 else 0) elif op == 'SGT': s0, s1 = to_signed(stk.pop()), 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 == 'NOT': 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 == 'BYTE': s0, s1 = stk.pop(), stk.pop() if s0 >= 32: stk.append(0) else: stk.append((s1 / 256**(31 - s0)) % 256) 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 == 'SHA3': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s1): return OUT_OF_GAS 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': stk.append(block.get_balance(utils.coerce_addr_to_hex(stk.pop()))) elif op == 'ORIGIN': stk.append(utils.coerce_to_int(tx.sender)) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': s0 = stk.pop() if s0 >= len(msg.data): stk.append(0) else: dat = msg.data[s0:s0 + 32] stk.append(utils.big_endian_to_int(dat + '\x00' * (32 - len(dat)))) elif op == 'CALLDATASIZE': stk.append(len(msg.data)) elif op == 'CALLDATACOPY': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s2): return OUT_OF_GAS for i in range(s2): if s1 + i < len(msg.data): mem[s0 + i] = ord(msg.data[s1 + i]) else: mem[s0 + i] = 0 elif op == 'GASPRICE': stk.append(tx.gasprice) elif op == 'CODECOPY': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s2): return OUT_OF_GAS for i in range(s2): if s1 + i < len(processed_code): mem[s0 + i] = processed_code[s1 + i][-1] else: mem[s0 + i] = 0 elif op == 'EXTCODESIZE': stk.append(len(block.get_code(stk.pop()) or '')) elif op == 'EXTCODECOPY': addr, s1, s2, s3 = stk.pop(), stk.pop(), stk.pop(), stk.pop() extcode = block.get_code(addr) or '' if not mem_extend(mem, compustate, op, s1 + s3): return OUT_OF_GAS for i in range(s3): if s2 + i < len(extcode): mem[s1 + i] = ord(extcode[s2 + i]) else: mem[s1 + i] = 0 elif op == 'PREVHASH': stk.append(utils.big_endian_to_int(block.prevhash)) elif op == 'COINBASE': stk.append(utils.big_endian_to_int(block.coinbase.decode('hex'))) elif op == 'TIMESTAMP': stk.append(block.timestamp) elif op == 'NUMBER': stk.append(block.number) elif op == 'DIFFICULTY': stk.append(block.difficulty) elif op == 'GASLIMIT': stk.append(block.gas_limit) elif op == 'POP': stk.pop() elif op == 'MLOAD': s0 = stk.pop() if not mem_extend(mem, compustate, op, s0 + 32): return OUT_OF_GAS 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 OUT_OF_GAS 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 OUT_OF_GAS mem[s0] = s1 % 256 elif op == 'SLOAD': stk.append(block.get_storage_data(msg.to, stk.pop())) elif op == 'SSTORE': s0, s1 = stk.pop(), stk.pop() pre_occupied = GSTORAGE if block.get_storage_data(msg.to, s0) else 0 post_occupied = GSTORAGE if s1 else 0 gascost = GSTORAGE + post_occupied - pre_occupied if compustate.gas < gascost: out_of_gas_exception('sstore trie expansion', gascost, compustate, op) compustate.gas -= gascost block.set_storage_data(msg.to, s0, s1) elif op == 'JUMP': compustate.pc = stk.pop() elif op == 'JUMPI': s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 elif op == 'PC': stk.append(compustate.pc) 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:]) dat = [ x[-1] for x in processed_code[compustate.pc:compustate.pc + pushnum] ] compustate.pc += pushnum stk.append(utils.bytearray_to_int(dat)) elif op[:3] == 'DUP': depth = int(op[3:]) # DUP POP POP Debug hint is_debug = 1 for i in range(depth): if compustate.pc + i < len(processed_code) and \ processed_code[compustate.pc + i][0] != 'POP': is_debug = 0 break if is_debug: stackargs = [stk.pop() for i in range(depth)] print(' '.join(map(repr, stackargs))) stk.extend(reversed(stackargs)) stk.append(stackargs[-1]) else: 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 == 'CREATE': value, mstart, msz = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart + msz): return OUT_OF_GAS data = ''.join(map(chr, mem[mstart:mstart + msz])) pblogger.log('SUB CONTRACT NEW', sender=msg.to, value=value, data=data.encode('hex')) create_msg = Message(msg.to, '', value, compustate.gas, data) addr, gas, code = create_contract(block, tx, create_msg) pblogger.log('SUB CONTRACT OUT', address=addr, code=code) if addr: stk.append(addr) compustate.gas = gas else: stk.append(0) compustate.gas = 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() new_memsize = max(meminstart + meminsz, memoutstart + memoutsz) if not mem_extend(mem, compustate, op, new_memsize): return OUT_OF_GAS if compustate.gas < gas: return out_of_gas_exception('subcall gas', gas, compustate, op) compustate.gas -= gas to = utils.encode_int(to) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') data = ''.join(map(chr, mem[meminstart:meminstart + meminsz])) pblogger.log('SUB CALL NEW', sender=msg.to, to=to, value=value, gas=gas, data=data.encode('hex')) call_msg = Message(msg.to, to, value, gas, data) result, gas, data = apply_msg_send(block, tx, call_msg) pblogger.log('SUB CALL OUT', result=result, data=data, length=len(data), expected=memoutsz) 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] elif op == 'RETURN': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s1): return OUT_OF_GAS return mem[s0:s0 + s1] elif op == 'POST': gas, to, value, meminstart, meminsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, meminstart + meminsz): return OUT_OF_GAS if compustate.gas < gas: return out_of_gas_exception('subcall gas', gas, compustate, op) compustate.gas -= gas to = utils.encode_int(to) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') data = ''.join(map(chr, mem[meminstart:meminstart + meminsz])) pblogger.log('POST NEW', sender=msg.to, to=to, value=value, gas=gas, data=data.encode('hex')) post_msg = Message(msg.to, to, value, gas, data) block.postqueue.append(post_msg) elif op == 'CALL_STATELESS': gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() new_memsize = max(meminstart + meminsz, memoutstart + memoutsz) if not mem_extend(mem, compustate, op, new_memsize): return OUT_OF_GAS if compustate.gas < gas: return out_of_gas_exception('subcall gas', gas, compustate, op) compustate.gas -= gas to = utils.encode_int(to) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') data = ''.join(map(chr, mem[meminstart:meminstart + meminsz])) pblogger.log('SUB CALL NEW', sender=msg.to, to=to, value=value, gas=gas, data=data.encode('hex')) call_msg = Message(msg.to, msg.to, value, gas, data) result, gas, data = apply_msg(block, tx, call_msg, block.get_code(to)) pblogger.log('SUB CALL OUT', result=result, data=data, length=len(data), expected=memoutsz) 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] elif op == 'SUICIDE': to = utils.encode_int(stk.pop()) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') block.transfer_value(msg.to, to, block.get_balance(msg.to)) block.suicides.append(msg.to) return [] for a in stk: assert isinstance(a, (int, long))
def apply_transaction(block, tx): def rp(actual, target): return '%r, actual:%r target:%r' % (tx, actual, target) # (1) The transaction signature is valid; if not tx.sender: raise UnsignedTransaction(tx) # (2) the transaction nonce is valid (equivalent to the # sender account's current nonce); acctnonce = block.get_nonce(tx.sender) if acctnonce != tx.nonce: raise InvalidNonce(rp(tx.nonce, acctnonce)) # (3) the gas limit is no smaller than the intrinsic gas, # g0, used by the transaction; intrinsic_gas_used = GTXDATA * len(tx.data) + GTXCOST if tx.startgas < intrinsic_gas_used: raise InsufficientStartGas(rp(tx.startgas, intrinsic_gas_used)) # (4) the sender account balance contains at least the # cost, v0, required in up-front payment. total_cost = tx.value + tx.gasprice * tx.startgas if block.get_balance(tx.sender) < total_cost: raise InsufficientBalance(rp(block.get_balance(tx.sender), total_cost)) # check offered gas price is enough if tx.gasprice < block.min_gas_price: raise GasPriceTooLow(rp(tx.gasprice, block.min_gas_price)) # check block gas limit if block.gas_used + tx.startgas > block.gas_limit: raise BlockGasLimitReached( rp(block.gas_used + tx.startgas, block.gas_limit)) pblogger.log('TX NEW', tx=tx.hex_hash(), tx_dict=tx.to_dict()) # start transacting ################# block.increment_nonce(tx.sender) # buy startgas success = block.transfer_value(tx.sender, block.coinbase, tx.gasprice * tx.startgas) assert success message_gas = tx.startgas - intrinsic_gas_used message = Message(tx.sender, tx.to, tx.value, message_gas, tx.data) block.postqueue = [message] primary_result = None while len(block.postqueue): message = block.postqueue.pop(0) # MESSAGE if tx.to and tx.to != CREATE_CONTRACT_ADDRESS: result, gas_remained, data = apply_msg_send(block, tx, message) else: # CREATE result, gas_remained, data = create_contract(block, tx, message) if result > 0: result = utils.coerce_addr_to_hex(result) if not primary_result: primary_result = result, gas_remained, data result, gas_remained, data = primary_result assert gas_remained >= 0 pblogger.log("TX APPLIED", result=result, gas_remained=gas_remained, data=''.join(map(chr, data)).encode('hex')) if pblogger.log_block: pblogger.log('BLOCK', block=block.to_dict(with_state=True, full_transactions=True)) if not result: # 0 = OOG failure in both cases pblogger.log('TX FAILED', reason='out of gas', startgas=tx.startgas, gas_remained=gas_remained) block.gas_used += tx.startgas output = OUT_OF_GAS else: pblogger.log('TX SUCCESS') gas_used = tx.startgas - gas_remained # sell remaining gas block.transfer_value(block.coinbase, tx.sender, tx.gasprice * gas_remained) block.gas_used += gas_used if tx.to: output = ''.join(map(chr, data)) else: output = result block.commit_state() suicides = block.suicides block.suicides = [] for s in suicides: block.del_account(s) block.add_transaction_to_list(tx) success = output is not OUT_OF_GAS return success, output if success else ''
def apply_op(block, tx, msg, code, compustate): op, in_args, out_args = get_op_data(code, compustate.pc) # empty stack error if in_args > len(compustate.stack): return [] # out of gas error fee = calcfee(block, tx, msg, compustate, op) if fee > compustate.gas: if debug: print "Out of gas", compustate.gas, "need", fee print op, list(reversed(compustate.stack)) return OUT_OF_GAS stackargs = [] for i in range(in_args): stackargs.append(compustate.stack.pop()) if debug: if op[:4] == 'PUSH': start, n = compustate.pc + 1, int(op[4:]) print op, decode_int(code[start:start + n]) else: print op, ' '.join(map(str, stackargs)) # Apply operation oldgas = compustate.gas oldpc = compustate.pc compustate.gas -= fee compustate.pc += 1 stk = compustate.stack mem = compustate.memory if op == 'STOP': return [] elif op == 'ADD': stk.append((stackargs[0] + stackargs[1]) % 2**256) elif op == 'SUB': stk.append((stackargs[0] - stackargs[1]) % 2**256) elif op == 'MUL': stk.append((stackargs[0] * stackargs[1]) % 2**256) elif op == 'DIV': if stackargs[1] == 0: return [] stk.append(stackargs[0] / stackargs[1]) elif op == 'MOD': if stackargs[1] == 0: return [] stk.append(stackargs[0] % stackargs[1]) elif op == 'SDIV': if stackargs[1] == 0: return [] if stackargs[0] >= 2**255: stackargs[0] -= 2**256 if stackargs[1] >= 2**255: stackargs[1] -= 2**256 stk.append((stackargs[0] / stackargs[1]) % 2**256) elif op == 'SMOD': if stackargs[1] == 0: return [] if stackargs[0] >= 2**255: stackargs[0] -= 2**256 if stackargs[1] >= 2**255: stackargs[1] -= 2**256 stk.append((stackargs[0] % stackargs[1]) % 2**256) elif op == 'EXP': stk.append(pow(stackargs[0], stackargs[1], 2**256)) elif op == 'NEG': stk.append(2**256 - stackargs[0]) elif op == 'LT': stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'GT': stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'EQ': stk.append(1 if stackargs[0] == stackargs[1] else 0) elif op == 'NOT': stk.append(0 if stackargs[0] else 1) elif op == 'AND': stk.append(stackargs[0] & stackargs[1]) elif op == 'OR': stk.append(stackargs[0] | stackargs[1]) elif op == 'XOR': stk.append(stackargs[0] ^ stackargs[1]) elif op == 'BYTE': if stackargs[0] >= 32: stk.append(0) else: stk.append((stackargs[1] / 256**stackargs[0]) % 256) elif op == 'SHA3': if len(mem) < stackargs[0] + stackargs[1]: mem.extend([0] * (stackargs[0] + stackargs[1] - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + stackargs[1]])) stk.append(decode(sha3(data), 256)) elif op == 'ADDRESS': stk.append(msg.to) elif op == 'BALANCE': stk.append(block.get_balance(msg.to)) elif op == 'ORIGIN': stk.append(tx.sender) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': if stackargs[0] >= len(msg.data): stk.append(0) else: dat = msg.data[stackargs[0]:stackargs[0] + 32] stk.append(decode_int(dat + '\x00' * (32 - len(dat)))) elif op == 'CALLDATASIZE': stk.append(len(msg.data)) elif op == 'GASPRICE': stk.append(tx.gasprice) elif op == 'PREVHASH': stk.append(decode_int(block.prevhash)) elif op == 'COINBASE': stk.append(decode_int(block.coinbase.decode('hex'))) elif op == 'TIMESTAMP': stk.append(block.timestamp) elif op == 'NUMBER': stk.append(block.number) elif op == 'DIFFICULTY': stk.append(block.difficulty) elif op == 'GASLIMIT': stk.append(block.gaslimit) elif op == 'POP': pass elif op == 'DUP': stk.append(stackargs[0]) stk.append(stackargs[0]) elif op == 'SWAP': stk.append(stackargs[0]) stk.append(stackargs[1]) elif op == 'MLOAD': if len(mem) < stackargs[0] + 32: mem.extend([0] * (stackargs[0] + 32 - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + 32])) stk.append(decode_int(data)) elif op == 'MSTORE': if len(mem) < stackargs[0] + 32: mem.extend([0] * (stackargs[0] + 32 - len(mem))) v = stackargs[1] for i in range(31, -1, -1): mem[stackargs[0] + i] = v % 256 v /= 256 elif op == 'MSTORE8': if len(mem) < stackargs[0] + 1: mem.extend([0] * (stackargs[0] + 1 - len(mem))) mem[stackargs[0]] = stackargs[1] % 256 elif op == 'SLOAD': stk.append(block.get_storage_data(msg.to, stackargs[0])) elif op == 'SSTORE': block.set_storage_data(msg.to, stackargs[0], stackargs[1]) elif op == 'JUMP': compustate.pc = stackargs[0] elif op == 'JUMPI': if stackargs[1]: compustate.pc = stackargs[0] elif op == 'PC': stk.append(compustate.pc) elif op == 'MSIZE': stk.append(len(mem)) elif op == 'GAS': stk.append(oldgas) elif op[:4] == 'PUSH': pushnum = int(op[4:]) compustate.pc = oldpc + 1 + pushnum dat = code[oldpc + 1:oldpc + 1 + pushnum] stk.append(decode_int(dat)) elif op == 'CREATE': if len(mem) < stackargs[2] + stackargs[3]: mem.extend([0] * (stackargs[2] + stackargs[3] - len(mem))) value = stackargs[0] gas = stackargs[1] data = ''.join(map(chr, mem[stackargs[2]:stackargs[2] + stackargs[3]])) if debug: print "Sub-contract:", msg.to, value, gas, data create_contract(block, tx, Message(msg.to, '', value, gas, data)) elif op == 'CALL': if len(mem) < stackargs[3] + stackargs[4]: mem.extend([0] * (stackargs[3] + stackargs[4] - len(mem))) if len(mem) < stackargs[5] + stackargs[6]: mem.extend([0] * (stackargs[5] + stackargs[6] - len(mem))) to = encode_int(stackargs[0]) to = (('\x00' * (32 - len(to))) + to)[12:] value = stackargs[1] gas = stackargs[2] data = ''.join(map(chr, mem[stackargs[3]:stackargs[3] + stackargs[4]])) if debug: print "Sub-call:", utils.coerce_addr_to_hex( msg.to), utils.coerce_addr_to_hex(to), value, gas, data result, gas, data = apply_msg(block, tx, Message(msg.to, to, value, gas, data)) if debug: print "Output of sub-call:", result, data, "length", len( data), "expected", stackargs[6] for i in range(stackargs[6]): mem[stackargs[5] + i] = 0 if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(len(data)): mem[stackargs[5] + i] = data[i] elif op == 'RETURN': if len(mem) < stackargs[0] + stackargs[1]: mem.extend([0] * (stackargs[0] + stackargs[1] - len(mem))) return mem[stackargs[0]:stackargs[0] + stackargs[1]] elif op == 'SUICIDE': to = encode_int(stackhash[0]) to = (('\x00' * (32 - len(to))) + to)[12:] block.delta_balance(to, block.get_balance(msg.to)) block.state.update(msg.to, '') return []
def apply_op(block, tx, msg, code, compustate): op, in_args, out_args = get_op_data(code, compustate.pc) # empty stack error if in_args > len(compustate.stack): return [] # out of gas error fee = calcfee(block, tx, msg, compustate, op) if fee > compustate.gas: if debug: print("Out of gas", compustate.gas, "need", fee) print(op, list(reversed(compustate.stack))) return OUT_OF_GAS stackargs = [] for i in range(in_args): stackargs.append(compustate.stack.pop()) if debug: import serpent if op[:4] == 'PUSH': start, n = compustate.pc + 1, int(op[4:]) print(op, utils.big_endian_to_int(code[start:start + n])) else: print(op, ' '.join(map(str, stackargs)), serpent.decode_datalist(compustate.memory)) # Apply operation oldgas = compustate.gas oldpc = compustate.pc compustate.gas -= fee compustate.pc += 1 stk = compustate.stack mem = compustate.memory if op == 'STOP': return [] elif op == 'ADD': stk.append((stackargs[0] + stackargs[1]) % 2 ** 256) elif op == 'SUB': stk.append((stackargs[0] - stackargs[1]) % 2 ** 256) elif op == 'MUL': stk.append((stackargs[0] * stackargs[1]) % 2 ** 256) elif op == 'DIV': if stackargs[1] == 0: return [] stk.append(stackargs[0] / stackargs[1]) elif op == 'MOD': if stackargs[1] == 0: return [] stk.append(stackargs[0] % stackargs[1]) elif op == 'SDIV': if stackargs[1] == 0: return [] if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append((stackargs[0] / stackargs[1]) % 2 ** 256) elif op == 'SMOD': if stackargs[1] == 0: return [] if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append((stackargs[0] % stackargs[1]) % 2 ** 256) elif op == 'EXP': stk.append(pow(stackargs[0], stackargs[1], 2 ** 256)) elif op == 'NEG': stk.append(2 ** 256 - stackargs[0]) elif op == 'LT': stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'GT': stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'SLT': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'SGT': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'EQ': stk.append(1 if stackargs[0] == stackargs[1] else 0) elif op == 'NOT': stk.append(0 if stackargs[0] else 1) elif op == 'AND': stk.append(stackargs[0] & stackargs[1]) elif op == 'OR': stk.append(stackargs[0] | stackargs[1]) elif op == 'XOR': stk.append(stackargs[0] ^ stackargs[1]) elif op == 'BYTE': if stackargs[0] >= 32: stk.append(0) else: stk.append((stackargs[1] / 256 ** stackargs[0]) % 256) elif op == 'SHA3': if len(mem) < ceil32(stackargs[0] + stackargs[1]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[1]) - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + stackargs[1]])) print 'data time!' print data print data.encode('hex') stk.append(int(utils.sha3(data).encode('hex'), 16)) elif op == 'ECVERIFY': # parameters: msg_hash (32), v (32), r (32), s (32), pubX (32), pubY (32) # stack should have all args msg_hash, v, r, s, pubX, pubY = stackargs pubX = utils.int_to_big_endian(pubX).encode('hex') pubY = utils.int_to_big_endian(pubY).encode('hex') msg_hash = utils.int_to_big_endian(msg_hash) pub = ('04' + pubX + pubY).decode('hex') verified = ecdsa_raw_verify(msg_hash, (v, r, s), pub) print 'verified: ', verified stk.append(verified) elif op == 'ECRECOVER': # parameters: msg_hash (32), v (32), r (32), s (32), p (64 - empty array to hold pubkey) # stack should have all args msg_hash, v, r, s = stackargs msg_hash = utils.int_to_big_endian(msg_hash) pubX, pubY = ecdsa_raw_recover(msg_hash, (v, r, s)) stk.append(pubX) stk.append(pubY) elif op == 'PUB2ADDR': pubX, pubY = stackargs pubX = utils.int_to_big_endian(pubX).encode('hex') pubY = utils.int_to_big_endian(pubY).encode('hex') pub = pubX + pubY pub = pub.decode('hex') addr = utils.sha3(pub)[12:] stk.append(addr) elif op == 'ADDRESS': stk.append(msg.to) elif op == 'BALANCE': stk.append(block.get_balance(msg.to)) elif op == 'ORIGIN': stk.append(tx.sender) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': if stackargs[0] >= len(msg.data): stk.append(0) else: dat = msg.data[stackargs[0]:stackargs[0] + 32] stk.append(utils.big_endian_to_int(dat + '\x00' * (32 - len(dat)))) elif op == 'CALLDATASIZE': stk.append(len(msg.data)) elif op == 'CALLDATACOPY': if len(mem) < ceil32(stackargs[1] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[1] + stackargs[2]) - len(mem))) for i in range(stackargs[2]): if stackargs[0] + i < len(msg.data): mem[stackargs[1] + i] = ord(msg.data[stackargs[0] + i]) else: mem[stackargs[1] + i] = 0 elif op == 'GASPRICE': stk.append(tx.gasprice) elif op == 'CODECOPY': if len(mem) < ceil32(stackargs[1] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[1] + stackargs[2]) - len(mem))) for i in range(stackargs[2]): if stackargs[0] + i < len(code): mem[stackargs[1] + i] = ord(code[stackargs[0] + i]) else: mem[stackargs[1] + i] = 0 elif op == 'PREVHASH': stk.append(utils.big_endian_to_int(block.prevhash)) elif op == 'COINBASE': stk.append(utils.big_endian_to_int(block.coinbase.decode('hex'))) elif op == 'TIMESTAMP': stk.append(block.timestamp) elif op == 'NUMBER': stk.append(block.number) elif op == 'DIFFICULTY': stk.append(block.difficulty) elif op == 'GASLIMIT': stk.append(block.gaslimit) elif op == 'POP': pass elif op == 'DUP': stk.append(stackargs[0]) stk.append(stackargs[0]) elif op == 'SWAP': stk.append(stackargs[0]) stk.append(stackargs[1]) elif op == 'MLOAD': if len(mem) < ceil32(stackargs[0] + 32): mem.extend([0] * (ceil32(stackargs[0] + 32) - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + 32])) stk.append(utils.big_endian_to_int(data)) elif op == 'MSTORE': if len(mem) < ceil32(stackargs[0] + 32): mem.extend([0] * (ceil32(stackargs[0] + 32) - len(mem))) v = stackargs[1] #if isinstance(v, str): # v = int(v.encode('hex'), 16) for i in range(31, -1, -1): mem[stackargs[0] + i] = v % 256 v /= 256 elif op == 'MSTORE8': if len(mem) < ceil32(stackargs[0] + 1): mem.extend([0] * (ceil32(stackargs[0] + 1) - len(mem))) mem[stackargs[0]] = stackargs[1] % 256 elif op == 'SLOAD': stk.append(block.get_storage_data(msg.to, stackargs[0])) elif op == 'SSTORE': block.set_storage_data(msg.to, stackargs[0], stackargs[1]) elif op == 'JUMP': compustate.pc = stackargs[0] elif op == 'JUMPI': if stackargs[1]: compustate.pc = stackargs[0] elif op == 'PC': stk.append(compustate.pc) elif op == 'MSIZE': stk.append(len(mem)) elif op == 'GAS': stk.append(oldgas) elif op[:4] == 'PUSH': pushnum = int(op[4:]) compustate.pc = oldpc + 1 + pushnum dat = code[oldpc + 1: oldpc + 1 + pushnum] stk.append(utils.big_endian_to_int(dat)) elif op == 'CREATE': if len(mem) < ceil32(stackargs[2] + stackargs[3]): mem.extend([0] * (ceil32(stackargs[2] + stackargs[3]) - len(mem))) gas = stackargs[0] value = stackargs[1] data = ''.join(map(chr, mem[stackargs[2]:stackargs[2] + stackargs[3]])) if debug: print("Sub-contract:", msg.to, value, gas, data) addr, gas, code = create_contract( block, tx, Message(msg.to, '', value, gas, data)) if debug: print("Output of contract creation:", addr, code) if addr: stk.append(utils.coerce_to_int(addr)) else: stk.append(0) elif op == 'CALL': if len(mem) < ceil32(stackargs[3] + stackargs[4]): mem.extend([0] * (ceil32(stackargs[3] + stackargs[4]) - len(mem))) if len(mem) < ceil32(stackargs[5] + stackargs[6]): mem.extend([0] * (ceil32(stackargs[5] + stackargs[6]) - len(mem))) gas = stackargs[0] to = utils.encode_int(stackargs[1]) to = (('\x00' * (32 - len(to))) + to)[12:] value = stackargs[2] data = ''.join(map(chr, mem[stackargs[3]:stackargs[3] + stackargs[4]])) if debug: print("Sub-call:", utils.coerce_addr_to_hex(msg.to), utils.coerce_addr_to_hex(to), value, gas, data) result, gas, data = apply_msg( block, tx, Message(msg.to, to, value, gas, data)) if debug: print("Output of sub-call:", result, data, "length", len(data), "expected", stackargs[6]) for i in range(stackargs[6]): mem[stackargs[5] + i] = 0 if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(len(data)): mem[stackargs[5] + i] = data[i] elif op == 'RETURN': if len(mem) < ceil32(stackargs[0] + stackargs[1]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[1]) - len(mem))) return mem[stackargs[0]:stackargs[0] + stackargs[1]] elif op == 'SUICIDE': to = utils.encode_int(stackargs[0]) to = (('\x00' * (32 - len(to))) + to)[12:] block.delta_balance(to, block.get_balance(msg.to)) block.state.update(msg.to, '') return []
def apply_op(block, tx, msg, code, compustate): op, in_args, out_args = get_op_data(code, compustate.pc) # empty stack error if in_args > len(compustate.stack): return [] # out of gas error fee = calcfee(block, tx, msg, compustate, op) if fee > compustate.gas: logger_debug("Out of gas %s need %s", compustate.gas, fee) logger_debug('Op: %s %s', op, list(reversed(compustate.stack))) return OUT_OF_GAS stackargs = [] for i in range(in_args): stackargs.append(compustate.stack.pop()) if op[:4] == 'PUSH': ind = compustate.pc + 1 v = utils.big_endian_to_int(code[ind: ind + int(op[4:])]) logger_debug('%s %s %s %s', compustate.pc, op, v, compustate.gas) else: logger_debug('%s %s %s %s', compustate.pc, op, stackargs, compustate.gas) if print_debug >= 2: for i in range(0, len(compustate.memory), 16): memblk = compustate.memory[i:i+16] memline = ' '.join([chr(x).encode('hex') for x in memblk]) logger_debug('mem: %s', memline) # Apply operation oldpc = compustate.pc compustate.gas -= fee compustate.pc += 1 stk = compustate.stack mem = compustate.memory if op == 'STOP' or op == 'INVALID': return [] elif op == 'ADD': stk.append((stackargs[0] + stackargs[1]) % 2 ** 256) elif op == 'SUB': stk.append((stackargs[0] - stackargs[1]) % 2 ** 256) elif op == 'MUL': stk.append((stackargs[0] * stackargs[1]) % 2 ** 256) elif op == 'DIV': stk.append(0 if stackargs[1] == 0 else stackargs[0] / stackargs[1]) elif op == 'MOD': stk.append(0 if stackargs[1] == 0 else stackargs[0] % stackargs[1]) elif op == 'SDIV': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(0 if stackargs[1] == 0 else (stackargs[0] / stackargs[1]) % 2 ** 256) elif op == 'SMOD': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(0 if stackargs[1] == 0 else (stackargs[0] % stackargs[1]) % 2 ** 256) elif op == 'EXP': stk.append(pow(stackargs[0], stackargs[1], 2 ** 256)) elif op == 'NEG': stk.append(-stackargs[0] % 2**256) elif op == 'LT': stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'GT': stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'SLT': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'SGT': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'EQ': stk.append(1 if stackargs[0] == stackargs[1] else 0) elif op == 'NOT': stk.append(0 if stackargs[0] else 1) elif op == 'AND': stk.append(stackargs[0] & stackargs[1]) elif op == 'OR': stk.append(stackargs[0] | stackargs[1]) elif op == 'XOR': stk.append(stackargs[0] ^ stackargs[1]) elif op == 'BYTE': if stackargs[0] >= 32: stk.append(0) else: stk.append((stackargs[1] / 256 ** (31 - stackargs[0])) % 256) elif op == 'SHA3': if len(mem) < ceil32(stackargs[0] + stackargs[1]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[1]) - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + stackargs[1]])) stk.append(utils.big_endian_to_int(utils.sha3(data))) elif op == 'ADDRESS': stk.append(utils.coerce_to_int(msg.to)) elif op == 'BALANCE': stk.append(block.get_balance(utils.coerce_addr_to_hex(stackargs[0]))) elif op == 'ORIGIN': stk.append(utils.coerce_to_int(tx.sender)) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': if stackargs[0] >= len(msg.data): stk.append(0) else: dat = msg.data[stackargs[0]:stackargs[0] + 32] stk.append(utils.big_endian_to_int(dat + '\x00' * (32 - len(dat)))) elif op == 'CALLDATASIZE': stk.append(len(msg.data)) elif op == 'CALLDATACOPY': logger_debug('data: %s', msg.data.encode('hex')) if len(mem) < ceil32(stackargs[0] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[2]) - len(mem))) for i in range(stackargs[2]): if stackargs[1] + i < len(msg.data): mem[stackargs[0] + i] = ord(msg.data[stackargs[1] + i]) else: mem[stackargs[0] + i] = 0 elif op == 'GASPRICE': stk.append(tx.gasprice) elif op == 'CODECOPY': if len(mem) < ceil32(stackargs[0] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[2]) - len(mem))) for i in range(stackargs[2]): if stackargs[1] + i < len(code): mem[stackargs[0] + i] = ord(code[stackargs[1] + i]) else: mem[stackargs[0] + i] = 0 elif op == 'PREVHASH': stk.append(utils.big_endian_to_int(block.prevhash)) elif op == 'COINBASE': stk.append(utils.big_endian_to_int(block.coinbase.decode('hex'))) elif op == 'TIMESTAMP': stk.append(block.timestamp) elif op == 'NUMBER': stk.append(block.number) elif op == 'DIFFICULTY': stk.append(block.difficulty) elif op == 'GASLIMIT': stk.append(block.gas_limit) elif op == 'POP': pass elif op == 'DUP': # DUP POP POP Debug hint if get_op_data(code, oldpc + 1)[0] == 'POP' and \ get_op_data(code, oldpc + 2)[0] == 'POP': o = print_debug enable_debug() v = stackargs[0] logger_debug("Debug: %s", v if v < 2**255 else v - 2**256) if not o: disable_debug() compustate.pc = oldpc + 3 else: stk.append(stackargs[0]) stk.append(stackargs[0]) elif op == 'SWAP': stk.append(stackargs[0]) stk.append(stackargs[1]) elif op == 'MLOAD': if len(mem) < ceil32(stackargs[0] + 32): mem.extend([0] * (ceil32(stackargs[0] + 32) - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + 32])) stk.append(utils.big_endian_to_int(data)) elif op == 'MSTORE': if len(mem) < ceil32(stackargs[0] + 32): mem.extend([0] * (ceil32(stackargs[0] + 32) - len(mem))) v = stackargs[1] for i in range(31, -1, -1): mem[stackargs[0] + i] = v % 256 v /= 256 elif op == 'MSTORE8': if len(mem) < ceil32(stackargs[0] + 1): mem.extend([0] * (ceil32(stackargs[0] + 1) - len(mem))) mem[stackargs[0]] = stackargs[1] % 256 elif op == 'SLOAD': stk.append(block.get_storage_data(msg.to, stackargs[0])) elif op == 'SSTORE': block.set_storage_data(msg.to, stackargs[0], stackargs[1]) elif op == 'JUMP': compustate.pc = stackargs[0] elif op == 'JUMPI': if stackargs[1]: compustate.pc = stackargs[0] elif op == 'PC': stk.append(compustate.pc) 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 = oldpc + 1 + pushnum dat = code[oldpc + 1: oldpc + 1 + pushnum] stk.append(utils.big_endian_to_int(dat)) elif op == 'CREATE': if len(mem) < ceil32(stackargs[1] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[1] + stackargs[2]) - len(mem))) value = stackargs[0] data = ''.join(map(chr, mem[stackargs[1]:stackargs[1] + stackargs[2]])) logger_debug("Sub-contract: %s %s %s", msg.to, value, data.encode('hex')) addr, gas, code = create_contract( block, tx, Message(msg.to, '', value, compustate.gas, data)) logger_debug("Output of contract creation: %s %s ", addr, code) if addr: stk.append(addr) compustate.gas = gas else: stk.append(0) compustate.gas = 0 elif op == 'CALL': for i in range(3, 7): stackargs[i] = stackargs[i] % 2**64 if len(mem) < ceil32(stackargs[3] + stackargs[4]): mem.extend([0] * (ceil32(stackargs[3] + stackargs[4]) - len(mem))) if len(mem) < ceil32(stackargs[5] + stackargs[6]): mem.extend([0] * (ceil32(stackargs[5] + stackargs[6]) - len(mem))) gas = stackargs[0] to = utils.encode_int(stackargs[1]) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') value = stackargs[2] data = ''.join(map(chr, mem[stackargs[3]:stackargs[3] + stackargs[4]])) logger_debug( "Sub-call: %s %s %s %s %s ", utils.coerce_addr_to_hex(msg.to), utils.coerce_addr_to_hex(to), value, gas, data.encode('hex')) result, gas, data = apply_msg_send( block, tx, Message(msg.to, to, value, gas, data)) logger_debug( "Output of sub-call: %s %s length %s expected %s", result, data, len(data), stackargs[6]) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), stackargs[6])): mem[stackargs[5] + i] = data[i] elif op == 'RETURN': if len(mem) < ceil32(stackargs[0] + stackargs[1]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[1]) - len(mem))) return mem[stackargs[0]:stackargs[0] + stackargs[1]] elif op == 'SUICIDE': to = utils.encode_int(stackargs[0]) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') block.transfer_value(msg.to, to, block.get_balance(msg.to)) block.suicides.append(msg.to) return [] for a in stk: assert isinstance(a, (int, long))
def apply_transaction(block, tx): def rp(actual, target): return '%r, actual:%r target:%r' % (tx, actual, target) # (1) The transaction signature is valid; if not tx.sender: raise UnsignedTransaction(tx) # (2) the transaction nonce is valid (equivalent to the # sender account's current nonce); acctnonce = block.get_nonce(tx.sender) if acctnonce != tx.nonce: raise InvalidNonce(rp(tx.nonce, acctnonce)) # (3) the gas limit is no smaller than the intrinsic gas, # g0, used by the transaction; intrinsic_gas_used = GTXDATA * len(tx.data) + GTXCOST if tx.startgas < intrinsic_gas_used: raise InsufficientStartGas(rp(tx.startgas, intrinsic_gas_used)) # (4) the sender account balance contains at least the # cost, v0, required in up-front payment. total_cost = tx.value + tx.gasprice * tx.startgas if block.get_balance(tx.sender) < total_cost: raise InsufficientBalance( rp(block.get_balance(tx.sender), total_cost)) # check offered gas price is enough if tx.gasprice < block.min_gas_price: raise GasPriceTooLow(rp(tx.gasprice, block.min_gas_price)) # check block gas limit if block.gas_used + tx.startgas > block.gas_limit: raise BlockGasLimitReached(rp(block.gas_used + tx.startgas, block.gas_limit)) pblogger.log('TX NEW', tx=tx.hex_hash(), tx_dict=tx.to_dict()) # start transacting ################# block.increment_nonce(tx.sender) # buy startgas success = block.transfer_value(tx.sender, block.coinbase, tx.gasprice * tx.startgas) assert success message_gas = tx.startgas - intrinsic_gas_used message = Message(tx.sender, tx.to, tx.value, message_gas, tx.data) block.postqueue = [ message ] primary_result = None while len(block.postqueue): message = block.postqueue.pop(0) # MESSAGE if tx.to and tx.to != CREATE_CONTRACT_ADDRESS: result, gas_remained, data = apply_msg_send(block, tx, message) else: # CREATE result, gas_remained, data = create_contract(block, tx, message) if result > 0: result = utils.coerce_addr_to_hex(result) if not primary_result: primary_result = result, gas_remained, data result, gas_remained, data = primary_result assert gas_remained >= 0 pblogger.log("TX APPLIED", result=result, gas_remained=gas_remained, data=''.join(map(chr, data)).encode('hex')) if pblogger.log_block: pblogger.log('BLOCK', block=block.to_dict(with_state=True, full_transactions=True)) if not result: # 0 = OOG failure in both cases pblogger.log('TX FAILED', reason='out of gas', startgas=tx.startgas, gas_remained=gas_remained) block.gas_used += tx.startgas output = OUT_OF_GAS else: pblogger.log('TX SUCCESS') gas_used = tx.startgas - gas_remained # sell remaining gas block.transfer_value( block.coinbase, tx.sender, tx.gasprice * gas_remained) block.gas_used += gas_used if tx.to: output = ''.join(map(chr, data)) else: output = result block.commit_state() suicides = block.suicides block.suicides = [] for s in suicides: block.del_account(s) block.add_transaction_to_list(tx) success = output is not OUT_OF_GAS return success, output if success else ''
def apply_op(block, tx, msg, code, compustate): op, in_args, out_args, mem_grabs, base_gas = opdata = get_op_data( code, compustate.pc) # empty stack error if in_args > len(compustate.stack): return [] # out of gas error fee = calcfee(block, tx, msg, compustate, opdata) if fee > compustate.gas: pblogger.log('OUT OF GAS', needed=fee, available=compustate.gas, op=op, stack=list(reversed(compustate.stack))) return OUT_OF_GAS stackargs = [] for i in range(in_args): stackargs.append(compustate.stack.pop()) if pblogger.log_op: log_args = dict(pc=compustate.pc, op=op, stackargs=stackargs, gas=compustate.gas) if op[:4] == 'PUSH': ind = compustate.pc + 1 log_args['value'] = utils.big_endian_to_int(code[ind:ind + int(op[4:])]) elif op == 'CALLDATACOPY': log_args['data'] = msg.data.encode('hex') elif op == 'SSTORE': log_args['key'] = stackargs[0] log_args['value'] = stackargs[1] pblogger.log('OP', **log_args) if pblogger.log_memory: for i in range(0, len(compustate.memory), 16): memblk = compustate.memory[i:i + 16] memline = ' '.join([chr(x).encode('hex') for x in memblk]) pblogger.log('MEM', mem=memline) # Apply operation oldpc = compustate.pc compustate.gas -= fee compustate.pc += 1 stk = compustate.stack mem = compustate.memory if op == 'STOP' or op == 'INVALID': return [] elif op == 'ADD': stk.append((stackargs[0] + stackargs[1]) % 2**256) elif op == 'SUB': stk.append((stackargs[0] - stackargs[1]) % 2**256) elif op == 'MUL': stk.append((stackargs[0] * stackargs[1]) % 2**256) elif op == 'DIV': stk.append(0 if stackargs[1] == 0 else stackargs[0] / stackargs[1]) elif op == 'MOD': stk.append(0 if stackargs[1] == 0 else stackargs[0] % stackargs[1]) elif op == 'SDIV': if stackargs[0] >= 2**255: stackargs[0] -= 2**256 if stackargs[1] >= 2**255: stackargs[1] -= 2**256 stk.append(0 if stackargs[1] == 0 else (stackargs[0] / stackargs[1]) % 2**256) elif op == 'SMOD': if stackargs[0] >= 2**255: stackargs[0] -= 2**256 if stackargs[1] >= 2**255: stackargs[1] -= 2**256 stk.append(0 if stackargs[1] == 0 else (stackargs[0] % stackargs[1]) % 2**256) elif op == 'EXP': stk.append(pow(stackargs[0], stackargs[1], 2**256)) elif op == 'NEG': stk.append(-stackargs[0] % 2**256) elif op == 'LT': stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'GT': stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'SLT': if stackargs[0] >= 2**255: stackargs[0] -= 2**256 if stackargs[1] >= 2**255: stackargs[1] -= 2**256 stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'SGT': if stackargs[0] >= 2**255: stackargs[0] -= 2**256 if stackargs[1] >= 2**255: stackargs[1] -= 2**256 stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'EQ': stk.append(1 if stackargs[0] == stackargs[1] else 0) elif op == 'NOT': stk.append(0 if stackargs[0] else 1) elif op == 'AND': stk.append(stackargs[0] & stackargs[1]) elif op == 'OR': stk.append(stackargs[0] | stackargs[1]) elif op == 'XOR': stk.append(stackargs[0] ^ stackargs[1]) elif op == 'BYTE': if stackargs[0] >= 32: stk.append(0) else: stk.append((stackargs[1] / 256**(31 - stackargs[0])) % 256) elif op == 'ADDMOD': stk.append((stackargs[2] + stackargs[1]) % stackargs[0]) elif op == 'MULMOD': stk.append((stackargs[2] * stackargs[1]) % stackargs[0]) elif op == 'SHA3': if len(mem) < ceil32(stackargs[0] + stackargs[1]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[1]) - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + stackargs[1]])) stk.append(utils.big_endian_to_int(utils.sha3(data))) elif op == 'ADDRESS': stk.append(utils.coerce_to_int(msg.to)) elif op == 'BALANCE': stk.append(block.get_balance(utils.coerce_addr_to_hex(stackargs[0]))) elif op == 'ORIGIN': stk.append(utils.coerce_to_int(tx.sender)) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': if stackargs[0] >= len(msg.data): stk.append(0) else: dat = msg.data[stackargs[0]:stackargs[0] + 32] stk.append(utils.big_endian_to_int(dat + '\x00' * (32 - len(dat)))) elif op == 'CALLDATASIZE': stk.append(len(msg.data)) elif op == 'CALLDATACOPY': if len(mem) < ceil32(stackargs[0] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[2]) - len(mem))) for i in range(stackargs[2]): if stackargs[1] + i < len(msg.data): mem[stackargs[0] + i] = ord(msg.data[stackargs[1] + i]) else: mem[stackargs[0] + i] = 0 elif op == 'GASPRICE': stk.append(tx.gasprice) elif op == 'CODECOPY': if len(mem) < ceil32(stackargs[0] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[2]) - len(mem))) for i in range(stackargs[2]): if stackargs[1] + i < len(code): mem[stackargs[0] + i] = ord(code[stackargs[1] + i]) else: mem[stackargs[0] + i] = 0 elif op == 'PREVHASH': stk.append(utils.big_endian_to_int(block.prevhash)) elif op == 'COINBASE': stk.append(utils.big_endian_to_int(block.coinbase.decode('hex'))) elif op == 'TIMESTAMP': stk.append(block.timestamp) elif op == 'NUMBER': stk.append(block.number) elif op == 'DIFFICULTY': stk.append(block.difficulty) elif op == 'GASLIMIT': stk.append(block.gas_limit) elif op == 'POP': pass elif op == 'DUP': # DUP POP POP Debug hint if get_op_data(code, oldpc + 1)[0] == 'POP' and \ get_op_data(code, oldpc + 2)[0] == 'POP': compustate.pc = oldpc + 3 else: stk.append(stackargs[0]) stk.append(stackargs[0]) elif op == 'SWAP': stk.append(stackargs[0]) stk.append(stackargs[1]) elif op == 'MLOAD': if len(mem) < ceil32(stackargs[0] + 32): mem.extend([0] * (ceil32(stackargs[0] + 32) - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + 32])) stk.append(utils.big_endian_to_int(data)) elif op == 'MSTORE': if len(mem) < ceil32(stackargs[0] + 32): mem.extend([0] * (ceil32(stackargs[0] + 32) - len(mem))) v = stackargs[1] for i in range(31, -1, -1): mem[stackargs[0] + i] = v % 256 v /= 256 elif op == 'MSTORE8': if len(mem) < ceil32(stackargs[0] + 1): mem.extend([0] * (ceil32(stackargs[0] + 1) - len(mem))) mem[stackargs[0]] = stackargs[1] % 256 elif op == 'SLOAD': stk.append(block.get_storage_data(msg.to, stackargs[0])) elif op == 'SSTORE': block.set_storage_data(msg.to, stackargs[0], stackargs[1]) elif op == 'JUMP': compustate.pc = stackargs[0] elif op == 'JUMPI': if stackargs[1]: compustate.pc = stackargs[0] elif op == 'PC': stk.append(compustate.pc) 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 = oldpc + 1 + pushnum dat = code[oldpc + 1:oldpc + 1 + pushnum] stk.append(utils.big_endian_to_int(dat)) elif op[:4] == 'DUP': dupnum = int(op[4:]) - 1 stk.extend(reversed(stackargs)) stk.append(stackargs[dupnum]) elif op[:4] == 'SWAP': swapnum = int(op[4:]) stk.append(stackargs[0]) stk.extend(reversed(stackargs[1:-1])) stk.append(stackargs[-1]) elif op == 'CREATE': if len(mem) < ceil32(stackargs[1] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[1] + stackargs[2]) - len(mem))) value = stackargs[0] data = ''.join(map(chr, mem[stackargs[1]:stackargs[1] + stackargs[2]])) pblogger.log('SUB CONTRACT NEW', sender=msg.to, value=value, data=data.encode('hex')) create_msg = Message(msg.to, '', value, compustate.gas, data) addr, gas, code = create_contract(block, tx, create_msg) pblogger.log('SUB CONTRACT OUT', address=addr, code=code) if addr: stk.append(addr) compustate.gas = gas else: stk.append(0) compustate.gas = 0 elif op == 'CALL': if len(mem) < ceil32(stackargs[3] + stackargs[4]): mem.extend([0] * (ceil32(stackargs[3] + stackargs[4]) - len(mem))) if len(mem) < ceil32(stackargs[5] + stackargs[6]): mem.extend([0] * (ceil32(stackargs[5] + stackargs[6]) - len(mem))) gas = stackargs[0] to = utils.encode_int(stackargs[1]) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') value = stackargs[2] data = ''.join(map(chr, mem[stackargs[3]:stackargs[3] + stackargs[4]])) pblogger.log('SUB CALL NEW', sender=msg.to, to=to, value=value, gas=gas, data=data.encode('hex')) call_msg = Message(msg.to, to, value, gas, data) result, gas, data = apply_msg_send(block, tx, call_msg) pblogger.log('SUB CALL OUT', result=result, data=data, length=len(data), expected=stackargs[6]) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), stackargs[6])): mem[stackargs[5] + i] = data[i] elif op == 'RETURN': if len(mem) < ceil32(stackargs[0] + stackargs[1]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[1]) - len(mem))) return mem[stackargs[0]:stackargs[0] + stackargs[1]] elif op == 'POST': if len(mem) < ceil32(stackargs[3] + stackargs[4]): mem.extend([0] * (ceil32(stackargs[3] + stackargs[4]) - len(mem))) gas = stackargs[0] to = utils.encode_int(stackargs[1]) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') value = stackargs[2] data = ''.join(map(chr, mem[stackargs[3]:stackargs[3] + stackargs[4]])) pblogger.log('POST NEW', sender=msg.to, to=to, value=value, gas=gas, data=data.encode('hex')) post_msg = Message(msg.to, to, vaue, gas, data) block.postqueue.append(post_msg) elif op == 'SUICIDE': to = utils.encode_int(stackargs[0]) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') block.transfer_value(msg.to, to, block.get_balance(msg.to)) block.suicides.append(msg.to) return [] for a in stk: assert isinstance(a, (int, long))
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 apply_op(block, tx, msg, processed_code, compustate): if compustate.pc >= len(processed_code): return [] op, in_args, out_args, mem_grabs, fee, opcode = processed_code[compustate.pc] # empty stack error if in_args > len(compustate.stack): pblogger.log('INSUFFICIENT STACK ERROR', op=op, needed=in_args, available=len(compustate.stack)) return [] # out of gas error if fee > compustate.gas: return out_of_gas_exception('base_gas', fee, compustate, op) if pblogger.log_apply_op: if pblogger.log_stack: pblogger.log('STK', stk=list(reversed(compustate.stack))) if pblogger.log_op: log_args = dict(pc=compustate.pc, op=op, stackargs=compustate.stack[-1:-in_args-1:-1], gas=compustate.gas) if op[:4] == 'PUSH': ind = compustate.pc + 1 log_args['value'] = \ utils.bytearray_to_int([x[-1] for x in processed_code[ind: ind + int(op[4:])]]) elif op == 'CALLDATACOPY': log_args['data'] = msg.data.encode('hex') pblogger.log('OP', **log_args) if pblogger.log_memory: for i in range(0, len(compustate.memory), 16): memblk = compustate.memory[i:i+16] memline = ' '.join([chr(x).encode('hex') for x in memblk]) pblogger.log('MEM', mem=memline) # Apply operation compustate.gas -= fee compustate.pc += 1 stk = compustate.stack mem = compustate.memory if op == 'STOP' or op == 'INVALID': return [] elif op == 'ADD': stk.append((stk.pop() + stk.pop()) % TT256) elif op == 'SUB': stk.append((stk.pop() - stk.pop()) % TT256) elif op == 'MUL': stk.append((stk.pop() * stk.pop()) % TT256) 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 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(0 if s1 == 0 else (s0 / s1) % TT256) elif op == 'SMOD': s0, s1 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(0 if s1 == 0 else (s0 % s1) % TT256) elif op == 'EXP': stk.append(pow(stk.pop(), stk.pop(), TT256)) elif op == 'NEG': stk.append(-stk.pop() % TT256) elif 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 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(1 if s0 < s1 else 0) elif op == 'SGT': s0, s1 = to_signed(stk.pop()), 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 == 'NOT': 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 == 'BYTE': s0, s1 = stk.pop(), stk.pop() if s0 >= 32: stk.append(0) else: stk.append((s1 / 256 ** (31 - s0)) % 256) 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 == 'SHA3': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s1): return OUT_OF_GAS 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': stk.append(block.get_balance(utils.coerce_addr_to_hex(stk.pop()))) elif op == 'ORIGIN': stk.append(utils.coerce_to_int(tx.sender)) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': s0 = stk.pop() if s0 >= len(msg.data): stk.append(0) else: dat = msg.data[s0: s0 + 32] stk.append(utils.big_endian_to_int(dat + '\x00' * (32 - len(dat)))) elif op == 'CALLDATASIZE': stk.append(len(msg.data)) elif op == 'CALLDATACOPY': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s2): return OUT_OF_GAS for i in range(s2): if s1 + i < len(msg.data): mem[s0 + i] = ord(msg.data[s1 + i]) else: mem[s0 + i] = 0 elif op == 'GASPRICE': stk.append(tx.gasprice) elif op == 'CODECOPY': s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s2): return OUT_OF_GAS for i in range(s2): if s1 + i < len(processed_code): mem[s0 + i] = processed_code[s1 + i][-1] else: mem[s0 + i] = 0 elif op == 'EXTCODESIZE': stk.append(len(block.get_code(stk.pop()) or '')) elif op == 'EXTCODECOPY': addr, s1, s2, s3 = stk.pop(), stk.pop(), stk.pop(), stk.pop() extcode = block.get_code(addr) or '' if not mem_extend(mem, compustate, op, s1 + s3): return OUT_OF_GAS for i in range(s3): if s2 + i < len(extcode): mem[s1 + i] = ord(extcode[s2 + i]) else: mem[s1 + i] = 0 elif op == 'PREVHASH': stk.append(utils.big_endian_to_int(block.prevhash)) elif op == 'COINBASE': stk.append(utils.big_endian_to_int(block.coinbase.decode('hex'))) elif op == 'TIMESTAMP': stk.append(block.timestamp) elif op == 'NUMBER': stk.append(block.number) elif op == 'DIFFICULTY': stk.append(block.difficulty) elif op == 'GASLIMIT': stk.append(block.gas_limit) elif op == 'POP': stk.pop() elif op == 'MLOAD': s0 = stk.pop() if not mem_extend(mem, compustate, op, s0 + 32): return OUT_OF_GAS 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 OUT_OF_GAS 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 OUT_OF_GAS mem[s0] = s1 % 256 elif op == 'SLOAD': stk.append(block.get_storage_data(msg.to, stk.pop())) elif op == 'SSTORE': s0, s1 = stk.pop(), stk.pop() pre_occupied = GSTORAGE if block.get_storage_data(msg.to, s0) else 0 post_occupied = GSTORAGE if s1 else 0 gascost = GSTORAGE + post_occupied - pre_occupied if compustate.gas < gascost: out_of_gas_exception('sstore trie expansion', gascost, compustate, op) compustate.gas -= gascost block.set_storage_data(msg.to, s0, s1) elif op == 'JUMP': compustate.pc = stk.pop() elif op == 'JUMPI': s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 elif op == 'PC': stk.append(compustate.pc) 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:]) dat = [x[-1] for x in processed_code[compustate.pc: compustate.pc + pushnum]] compustate.pc += pushnum stk.append(utils.bytearray_to_int(dat)) elif op[:3] == 'DUP': depth = int(op[3:]) # DUP POP POP Debug hint is_debug = 1 for i in range(depth): if compustate.pc + i < len(processed_code) and \ processed_code[compustate.pc + i][0] != 'POP': is_debug = 0 break if is_debug: stackargs = [stk.pop() for i in range(depth)] print(' '.join(map(repr, stackargs))) stk.extend(reversed(stackargs)) stk.append(stackargs[-1]) else: 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 == 'CREATE': value, mstart, msz = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart + msz): return OUT_OF_GAS data = ''.join(map(chr, mem[mstart: mstart + msz])) pblogger.log('SUB CONTRACT NEW', sender=msg.to, value=value, data=data.encode('hex')) create_msg = Message(msg.to, '', value, compustate.gas, data) addr, gas, code = create_contract(block, tx, create_msg) pblogger.log('SUB CONTRACT OUT', address=addr, code=code) if addr: stk.append(addr) compustate.gas = gas else: stk.append(0) compustate.gas = 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() new_memsize = max(meminstart + meminsz, memoutstart + memoutsz) if not mem_extend(mem, compustate, op, new_memsize): return OUT_OF_GAS if compustate.gas < gas: return out_of_gas_exception('subcall gas', gas, compustate, op) compustate.gas -= gas to = utils.encode_int(to) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') data = ''.join(map(chr, mem[meminstart: meminstart + meminsz])) pblogger.log('SUB CALL NEW', sender=msg.to, to=to, value=value, gas=gas, data=data.encode('hex')) call_msg = Message(msg.to, to, value, gas, data) result, gas, data = apply_msg_send(block, tx, call_msg) pblogger.log('SUB CALL OUT', result=result, data=data, length=len(data), expected=memoutsz) 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] elif op == 'RETURN': s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s1): return OUT_OF_GAS return mem[s0: s0 + s1] elif op == 'POST': gas, to, value, meminstart, meminsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, meminstart + meminsz): return OUT_OF_GAS if compustate.gas < gas: return out_of_gas_exception('subcall gas', gas, compustate, op) compustate.gas -= gas to = utils.encode_int(to) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') data = ''.join(map(chr, mem[meminstart: meminstart + meminsz])) pblogger.log('POST NEW', sender=msg.to, to=to, value=value, gas=gas, data=data.encode('hex')) post_msg = Message(msg.to, to, value, gas, data) block.postqueue.append(post_msg) elif op == 'CALL_STATELESS': gas, to, value, meminstart, meminsz, memoutstart, memoutsz = \ stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() new_memsize = max(meminstart + meminsz, memoutstart + memoutsz) if not mem_extend(mem, compustate, op, new_memsize): return OUT_OF_GAS if compustate.gas < gas: return out_of_gas_exception('subcall gas', gas, compustate, op) compustate.gas -= gas to = utils.encode_int(to) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') data = ''.join(map(chr, mem[meminstart: meminstart + meminsz])) pblogger.log('SUB CALL NEW', sender=msg.to, to=to, value=value, gas=gas, data=data.encode('hex')) call_msg = Message(msg.to, msg.to, value, gas, data) result, gas, data = apply_msg(block, tx, call_msg, block.get_code(to)) pblogger.log('SUB CALL OUT', result=result, data=data, length=len(data), expected=memoutsz) 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] elif op == 'SUICIDE': to = utils.encode_int(stk.pop()) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') block.transfer_value(msg.to, to, block.get_balance(msg.to)) block.suicides.append(msg.to) return [] for a in stk: assert isinstance(a, (int, long))
def apply_op(block, tx, msg, code, compustate): op, in_args, out_args = get_op_data(code, compustate.pc) # empty stack error if in_args > len(compustate.stack): return [] # out of gas error fee = calcfee(block, tx, msg, compustate, op) if fee > compustate.gas: if debug: print("Out of gas", compustate.gas, "need", fee) print(op, list(reversed(compustate.stack))) return OUT_OF_GAS stackargs = [] for i in range(in_args): stackargs.append(compustate.stack.pop()) if debug: if op[:4] == 'PUSH': start, n = compustate.pc + 1, int(op[4:]) print(op, decode_int(code[start:start + n])) else: print(op, ' '.join(map(str, stackargs))) # Apply operation oldgas = compustate.gas oldpc = compustate.pc compustate.gas -= fee compustate.pc += 1 stk = compustate.stack mem = compustate.memory if op == 'STOP': return [] elif op == 'ADD': stk.append((stackargs[0] + stackargs[1]) % 2 ** 256) elif op == 'SUB': stk.append((stackargs[0] - stackargs[1]) % 2 ** 256) elif op == 'MUL': stk.append((stackargs[0] * stackargs[1]) % 2 ** 256) elif op == 'DIV': if stackargs[1] == 0: return [] stk.append(stackargs[0] / stackargs[1]) elif op == 'MOD': if stackargs[1] == 0: return [] stk.append(stackargs[0] % stackargs[1]) elif op == 'SDIV': if stackargs[1] == 0: return [] if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append((stackargs[0] / stackargs[1]) % 2 ** 256) elif op == 'SMOD': if stackargs[1] == 0: return [] if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append((stackargs[0] % stackargs[1]) % 2 ** 256) elif op == 'EXP': stk.append(pow(stackargs[0], stackargs[1], 2 ** 256)) elif op == 'NEG': stk.append(2 ** 256 - stackargs[0]) elif op == 'LT': stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'GT': stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'EQ': stk.append(1 if stackargs[0] == stackargs[1] else 0) elif op == 'NOT': stk.append(0 if stackargs[0] else 1) elif op == 'AND': stk.append(stackargs[0] & stackargs[1]) elif op == 'OR': stk.append(stackargs[0] | stackargs[1]) elif op == 'XOR': stk.append(stackargs[0] ^ stackargs[1]) elif op == 'BYTE': if stackargs[0] >= 32: stk.append(0) else: stk.append((stackargs[1] / 256 ** stackargs[0]) % 256) elif op == 'SHA3': if len(mem) < stackargs[0] + stackargs[1]: mem.extend([0] * (stackargs[0] + stackargs[1] - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + stackargs[1]])) stk.append(rlp.decode(sha3(data), 256)) elif op == 'ADDRESS': stk.append(msg.to) elif op == 'BALANCE': stk.append(block.get_balance(msg.to)) elif op == 'ORIGIN': stk.append(tx.sender) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': if stackargs[0] >= len(msg.data): stk.append(0) else: dat = msg.data[stackargs[0]:stackargs[0] + 32] stk.append(decode_int(dat + '\x00' * (32 - len(dat)))) elif op == 'CALLDATASIZE': stk.append(len(msg.data)) elif op == 'GASPRICE': stk.append(tx.gasprice) elif op == 'PREVHASH': stk.append(decode_int(block.prevhash)) elif op == 'COINBASE': stk.append(decode_int(block.coinbase.decode('hex'))) elif op == 'TIMESTAMP': stk.append(block.timestamp) elif op == 'NUMBER': stk.append(block.number) elif op == 'DIFFICULTY': stk.append(block.difficulty) elif op == 'GASLIMIT': stk.append(block.gaslimit) elif op == 'POP': pass elif op == 'DUP': stk.append(stackargs[0]) stk.append(stackargs[0]) elif op == 'SWAP': stk.append(stackargs[0]) stk.append(stackargs[1]) elif op == 'MLOAD': if len(mem) < stackargs[0] + 32: mem.extend([0] * (stackargs[0] + 32 - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + 32])) stk.append(decode_int(data)) elif op == 'MSTORE': if len(mem) < stackargs[0] + 32: mem.extend([0] * (stackargs[0] + 32 - len(mem))) v = stackargs[1] for i in range(31, -1, -1): mem[stackargs[0] + i] = v % 256 v /= 256 elif op == 'MSTORE8': if len(mem) < stackargs[0] + 1: mem.extend([0] * (stackargs[0] + 1 - len(mem))) mem[stackargs[0]] = stackargs[1] % 256 elif op == 'SLOAD': stk.append(block.get_storage_data(msg.to, stackargs[0])) elif op == 'SSTORE': block.set_storage_data(msg.to, stackargs[0], stackargs[1]) elif op == 'JUMP': compustate.pc = stackargs[0] elif op == 'JUMPI': if stackargs[1]: compustate.pc = stackargs[0] elif op == 'PC': stk.append(compustate.pc) elif op == 'MSIZE': stk.append(len(mem)) elif op == 'GAS': stk.append(oldgas) elif op[:4] == 'PUSH': pushnum = int(op[4:]) compustate.pc = oldpc + 1 + pushnum dat = code[oldpc + 1: oldpc + 1 + pushnum] stk.append(decode_int(dat)) elif op == 'CREATE': if len(mem) < stackargs[2] + stackargs[3]: mem.extend([0] * (stackargs[2] + stackargs[3] - len(mem))) value = stackargs[0] gas = stackargs[1] data = ''.join(map(chr, mem[stackargs[2]:stackargs[2] + stackargs[3]])) if debug: print("Sub-contract:", msg.to, value, gas, data) create_contract(block, tx, Message(msg.to, '', value, gas, data)) elif op == 'CALL': if len(mem) < stackargs[3] + stackargs[4]: mem.extend([0] * (stackargs[3] + stackargs[4] - len(mem))) if len(mem) < stackargs[5] + stackargs[6]: mem.extend([0] * (stackargs[5] + stackargs[6] - len(mem))) to = encode_int(stackargs[0]) to = (('\x00' * (32 - len(to))) + to)[12:] value = stackargs[1] gas = stackargs[2] data = ''.join(map(chr, mem[stackargs[3]:stackargs[3] + stackargs[4]])) if debug: print("Sub-call:", utils.coerce_addr_to_hex(msg.to), utils.coerce_addr_to_hex(to), value, gas, data) result, gas, data = apply_msg( block, tx, Message(msg.to, to, value, gas, data)) if debug: print("Output of sub-call:", result, data, "length", len(data), "expected", stackargs[6]) for i in range(stackargs[6]): mem[stackargs[5] + i] = 0 if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(len(data)): mem[stackargs[5] + i] = data[i] elif op == 'RETURN': if len(mem) < stackargs[0] + stackargs[1]: mem.extend([0] * (stackargs[0] + stackargs[1] - len(mem))) return mem[stackargs[0]:stackargs[0] + stackargs[1]] elif op == 'SUICIDE': to = encode_int(stackargs[0]) to = (('\x00' * (32 - len(to))) + to)[12:] block.delta_balance(to, block.get_balance(msg.to)) block.state.update(msg.to, '') return []
def apply_op(block, tx, msg, processed_code, compustate): if compustate.pc >= len(processed_code): return [] op, in_args, out_args, mem_grabs, fee, opcode = processed_code[compustate.pc] # empty stack error if in_args > len(compustate.stack): pblogger.log("INSUFFICIENT STACK ERROR", op=op, needed=in_args, available=len(compustate.stack)) return [] # out of gas error if fee > compustate.gas: return out_of_gas_exception("base_gas", fee, compustate, op) if pblogger.log_apply_op: if pblogger.log_stack: pblogger.log("STK", stk=list(reversed(compustate.stack))) if pblogger.log_memory: for i in range(0, len(compustate.memory), 16): memblk = compustate.memory[i : i + 16] memline = " ".join([chr(x).encode("hex") for x in memblk]) pblogger.log("MEM", mem=memline) if pblogger.log_storage: pblogger.log("STORAGE", storage=block.account_to_dict(msg.to)["storage"]) if pblogger.log_op: log_args = dict( pc=compustate.pc, op=op, stackargs=compustate.stack[-1 : -in_args - 1 : -1], gas=compustate.gas ) if op[:4] == "PUSH": ind = compustate.pc + 1 log_args["value"] = utils.bytearray_to_int([x[-1] for x in processed_code[ind : ind + int(op[4:])]]) elif op == "CALLDATACOPY": log_args["data"] = msg.data.encode("hex") pblogger.log("OP", **log_args) # Apply operation compustate.gas -= fee compustate.pc += 1 stk = compustate.stack mem = compustate.memory if op == "STOP" or op == "INVALID": return [] elif op == "ADD": stk.append((stk.pop() + stk.pop()) % TT256) elif op == "SUB": stk.append((stk.pop() - stk.pop()) % TT256) elif op == "MUL": stk.append((stk.pop() * stk.pop()) % TT256) 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 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(0 if s1 == 0 else (s0 / s1) % TT256) elif op == "SMOD": s0, s1 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(0 if s1 == 0 else (s0 % s1) % TT256) elif op == "EXP": stk.append(pow(stk.pop(), stk.pop(), TT256)) elif op == "NEG": stk.append(-stk.pop() % TT256) elif 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 = to_signed(stk.pop()), to_signed(stk.pop()) stk.append(1 if s0 < s1 else 0) elif op == "SGT": s0, s1 = to_signed(stk.pop()), 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 == "NOT": 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 == "BYTE": s0, s1 = stk.pop(), stk.pop() if s0 >= 32: stk.append(0) else: stk.append((s1 / 256 ** (31 - s0)) % 256) 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 == "SHA3": s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s1): return OUT_OF_GAS 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": stk.append(block.get_balance(utils.coerce_addr_to_hex(stk.pop()))) elif op == "ORIGIN": stk.append(utils.coerce_to_int(tx.sender)) elif op == "CALLER": stk.append(utils.coerce_to_int(msg.sender)) elif op == "CALLVALUE": stk.append(msg.value) elif op == "CALLDATALOAD": s0 = stk.pop() if s0 >= len(msg.data): stk.append(0) else: dat = msg.data[s0 : s0 + 32] stk.append(utils.big_endian_to_int(dat + "\x00" * (32 - len(dat)))) elif op == "CALLDATASIZE": stk.append(len(msg.data)) elif op == "CALLDATACOPY": s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s2): return OUT_OF_GAS for i in range(s2): if s1 + i < len(msg.data): mem[s0 + i] = ord(msg.data[s1 + i]) else: mem[s0 + i] = 0 elif op == "GASPRICE": stk.append(tx.gasprice) elif op == "CODECOPY": s0, s1, s2 = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s2): return OUT_OF_GAS for i in range(s2): if s1 + i < len(processed_code): mem[s0 + i] = processed_code[s1 + i][-1] else: mem[s0 + i] = 0 elif op == "EXTCODESIZE": stk.append(len(block.get_code(stk.pop()) or "")) elif op == "EXTCODECOPY": addr, s1, s2, s3 = stk.pop(), stk.pop(), stk.pop(), stk.pop() extcode = block.get_code(addr) or "" if not mem_extend(mem, compustate, op, s1 + s3): return OUT_OF_GAS for i in range(s3): if s2 + i < len(extcode): mem[s1 + i] = ord(extcode[s2 + i]) else: mem[s1 + i] = 0 elif op == "PREVHASH": stk.append(utils.big_endian_to_int(block.prevhash)) elif op == "COINBASE": stk.append(utils.big_endian_to_int(block.coinbase.decode("hex"))) elif op == "TIMESTAMP": stk.append(block.timestamp) elif op == "NUMBER": stk.append(block.number) elif op == "DIFFICULTY": stk.append(block.difficulty) elif op == "GASLIMIT": stk.append(block.gas_limit) elif op == "POP": stk.pop() elif op == "MLOAD": s0 = stk.pop() if not mem_extend(mem, compustate, op, s0 + 32): return OUT_OF_GAS 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 OUT_OF_GAS 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 OUT_OF_GAS mem[s0] = s1 % 256 elif op == "SLOAD": stk.append(block.get_storage_data(msg.to, stk.pop())) elif op == "SSTORE": s0, s1 = stk.pop(), stk.pop() pre_occupied = GSTORAGE if block.get_storage_data(msg.to, s0) else 0 post_occupied = GSTORAGE if s1 else 0 gascost = GSTORAGE + post_occupied - pre_occupied if compustate.gas < gascost: out_of_gas_exception("sstore trie expansion", gascost, compustate, op) compustate.gas -= gascost block.set_storage_data(msg.to, s0, s1) elif op == "JUMP": compustate.pc = stk.pop() elif op == "JUMPI": s0, s1 = stk.pop(), stk.pop() if s1: compustate.pc = s0 elif op == "PC": stk.append(compustate.pc) 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:]) dat = [x[-1] for x in processed_code[compustate.pc : compustate.pc + pushnum]] compustate.pc += pushnum stk.append(utils.bytearray_to_int(dat)) elif op[:3] == "DUP": depth = int(op[3:]) # DUP POP POP Debug hint is_debug = 1 for i in range(depth): if compustate.pc + i < len(processed_code) and processed_code[compustate.pc + i][0] != "POP": is_debug = 0 break if is_debug: stackargs = [stk.pop() for i in range(depth)] print (" ".join(map(repr, stackargs))) stk.extend(reversed(stackargs)) stk.append(stackargs[-1]) else: 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 == "CREATE": value, mstart, msz = stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, mstart + msz): return OUT_OF_GAS data = "".join(map(chr, mem[mstart : mstart + msz])) pblogger.log("SUB CONTRACT NEW", sender=msg.to, value=value, data=data.encode("hex")) create_msg = Message(msg.to, "", value, compustate.gas, data) addr, gas, code = create_contract(block, tx, create_msg) pblogger.log("SUB CONTRACT OUT", address=addr, code=code) if addr: stk.append(addr) compustate.gas = gas else: stk.append(0) compustate.gas = 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(), ) new_memsize = max(meminstart + meminsz, memoutstart + memoutsz) if not mem_extend(mem, compustate, op, new_memsize): return OUT_OF_GAS if compustate.gas < gas: return out_of_gas_exception("subcall gas", gas, compustate, op) compustate.gas -= gas to = utils.encode_int(to) to = (("\x00" * (32 - len(to))) + to)[12:].encode("hex") data = "".join(map(chr, mem[meminstart : meminstart + meminsz])) pblogger.log("SUB CALL NEW", sender=msg.to, to=to, value=value, gas=gas, data=data.encode("hex")) call_msg = Message(msg.to, to, value, gas, data) result, gas, data = apply_msg_send(block, tx, call_msg) pblogger.log("SUB CALL OUT", result=result, data=data, length=len(data), expected=memoutsz) 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] elif op == "RETURN": s0, s1 = stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, s0 + s1): return OUT_OF_GAS return mem[s0 : s0 + s1] elif op == "POST": gas, to, value, meminstart, meminsz = stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop() if not mem_extend(mem, compustate, op, meminstart + meminsz): return OUT_OF_GAS if compustate.gas < gas: return out_of_gas_exception("subcall gas", gas, compustate, op) compustate.gas -= gas to = utils.encode_int(to) to = (("\x00" * (32 - len(to))) + to)[12:].encode("hex") data = "".join(map(chr, mem[meminstart : meminstart + meminsz])) pblogger.log("POST NEW", sender=msg.to, to=to, value=value, gas=gas, data=data.encode("hex")) post_msg = Message(msg.to, to, value, gas, data) block.postqueue.append(post_msg) elif op == "CALL_STATELESS": gas, to, value, meminstart, meminsz, memoutstart, memoutsz = ( stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), stk.pop(), ) new_memsize = max(meminstart + meminsz, memoutstart + memoutsz) if not mem_extend(mem, compustate, op, new_memsize): return OUT_OF_GAS if compustate.gas < gas: return out_of_gas_exception("subcall gas", gas, compustate, op) compustate.gas -= gas to = utils.encode_int(to) to = (("\x00" * (32 - len(to))) + to)[12:].encode("hex") data = "".join(map(chr, mem[meminstart : meminstart + meminsz])) pblogger.log("SUB CALL NEW", sender=msg.to, to=to, value=value, gas=gas, data=data.encode("hex")) call_msg = Message(msg.to, msg.to, value, gas, data) result, gas, data = apply_msg(block, tx, call_msg, block.get_code(to)) pblogger.log("SUB CALL OUT", result=result, data=data, length=len(data), expected=memoutsz) 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] elif op == "SUICIDE": to = utils.encode_int(stk.pop()) to = (("\x00" * (32 - len(to))) + to)[12:].encode("hex") block.transfer_value(msg.to, to, block.get_balance(msg.to)) block.suicides.append(msg.to) return [] for a in stk: assert isinstance(a, (int, long))
def OP_EXTCODESIZE(): stk.append( len(block.get_code(utils.coerce_addr_to_hex(stk.pop())) or ''))
def apply_op(block, tx, msg, code, compustate): op, in_args, out_args, mem_grabs, base_gas = opdata = get_op_data(code, compustate.pc) # empty stack error if in_args > len(compustate.stack): pblogger.log('INSUFFICIENT STACK ERROR', op=op, needed=in_args, available=len(compustate.stack)) return [] # out of gas error fee = calcfee(block, tx, msg, compustate, opdata) if fee > compustate.gas: pblogger.log('OUT OF GAS', needed=fee, available=compustate.gas, op=op, stack=list(reversed(compustate.stack))) return OUT_OF_GAS stackargs = [] for i in range(in_args): stackargs.append(compustate.stack.pop()) if pblogger.log_op: log_args = dict(pc=compustate.pc, op=op, stackargs=stackargs, gas=compustate.gas) if op[:4] == 'PUSH': ind = compustate.pc + 1 log_args['value'] = utils.big_endian_to_int(code[ind: ind + int(op[4:])]) elif op == 'CALLDATACOPY': log_args['data'] = msg.data.encode('hex') pblogger.log('OP', **log_args) if pblogger.log_memory: for i in range(0, len(compustate.memory), 16): memblk = compustate.memory[i:i+16] memline = ' '.join([chr(x).encode('hex') for x in memblk]) pblogger.log('MEM', mem=memline) # Apply operation oldpc = compustate.pc compustate.gas -= fee compustate.pc += 1 stk = compustate.stack mem = compustate.memory if op == 'STOP' or op == 'INVALID': return [] elif op == 'ADD': stk.append((stackargs[0] + stackargs[1]) % 2 ** 256) elif op == 'SUB': stk.append((stackargs[0] - stackargs[1]) % 2 ** 256) elif op == 'MUL': stk.append((stackargs[0] * stackargs[1]) % 2 ** 256) elif op == 'DIV': stk.append(0 if stackargs[1] == 0 else stackargs[0] / stackargs[1]) elif op == 'MOD': stk.append(0 if stackargs[1] == 0 else stackargs[0] % stackargs[1]) elif op == 'SDIV': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(0 if stackargs[1] == 0 else (stackargs[0] / stackargs[1]) % 2 ** 256) elif op == 'SMOD': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(0 if stackargs[1] == 0 else (stackargs[0] % stackargs[1]) % 2 ** 256) elif op == 'EXP': stk.append(pow(stackargs[0], stackargs[1], 2 ** 256)) elif op == 'NEG': stk.append(-stackargs[0] % 2**256) elif op == 'LT': stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'GT': stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'SLT': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(1 if stackargs[0] < stackargs[1] else 0) elif op == 'SGT': if stackargs[0] >= 2 ** 255: stackargs[0] -= 2 ** 256 if stackargs[1] >= 2 ** 255: stackargs[1] -= 2 ** 256 stk.append(1 if stackargs[0] > stackargs[1] else 0) elif op == 'EQ': stk.append(1 if stackargs[0] == stackargs[1] else 0) elif op == 'NOT': stk.append(0 if stackargs[0] else 1) elif op == 'AND': stk.append(stackargs[0] & stackargs[1]) elif op == 'OR': stk.append(stackargs[0] | stackargs[1]) elif op == 'XOR': stk.append(stackargs[0] ^ stackargs[1]) elif op == 'BYTE': if stackargs[0] >= 32: stk.append(0) else: stk.append((stackargs[1] / 256 ** (31 - stackargs[0])) % 256) elif op == 'ADDMOD': stk.append((stackargs[0] + stackargs[1]) % stackargs[2] if stackargs[2] else 0) elif op == 'MULMOD': stk.append((stackargs[0] * stackargs[1]) % stackargs[2] if stackargs[2] else 0) elif op == 'SHA3': if stackargs[1] and len(mem) < ceil32(stackargs[0] + stackargs[1]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[1]) - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + stackargs[1]])) stk.append(utils.big_endian_to_int(utils.sha3(data))) elif op == 'ADDRESS': stk.append(utils.coerce_to_int(msg.to)) elif op == 'BALANCE': stk.append(block.get_balance(utils.coerce_addr_to_hex(stackargs[0]))) elif op == 'ORIGIN': stk.append(utils.coerce_to_int(tx.sender)) elif op == 'CALLER': stk.append(utils.coerce_to_int(msg.sender)) elif op == 'CALLVALUE': stk.append(msg.value) elif op == 'CALLDATALOAD': if stackargs[0] >= len(msg.data): stk.append(0) else: dat = msg.data[stackargs[0]:stackargs[0] + 32] stk.append(utils.big_endian_to_int(dat + '\x00' * (32 - len(dat)))) elif op == 'CALLDATASIZE': stk.append(len(msg.data)) elif op == 'CALLDATACOPY': if stackargs[2] and len(mem) < ceil32(stackargs[0] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[2]) - len(mem))) for i in range(stackargs[2]): if stackargs[1] + i < len(msg.data): mem[stackargs[0] + i] = ord(msg.data[stackargs[1] + i]) else: mem[stackargs[0] + i] = 0 elif op == 'GASPRICE': stk.append(tx.gasprice) elif op == 'CODECOPY': if stackargs[2] and len(mem) < ceil32(stackargs[0] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[2]) - len(mem))) for i in range(stackargs[2]): if stackargs[1] + i < len(code): mem[stackargs[0] + i] = ord(code[stackargs[1] + i]) else: mem[stackargs[0] + i] = 0 elif op == 'PREVHASH': stk.append(utils.big_endian_to_int(block.prevhash)) elif op == 'COINBASE': stk.append(utils.big_endian_to_int(block.coinbase.decode('hex'))) elif op == 'TIMESTAMP': stk.append(block.timestamp) elif op == 'NUMBER': stk.append(block.number) elif op == 'DIFFICULTY': stk.append(block.difficulty) elif op == 'GASLIMIT': stk.append(block.gas_limit) elif op == 'POP': pass elif op == 'SWAP': stk.append(stackargs[0]) stk.append(stackargs[1]) elif op == 'MLOAD': if len(mem) < ceil32(stackargs[0] + 32): mem.extend([0] * (ceil32(stackargs[0] + 32) - len(mem))) data = ''.join(map(chr, mem[stackargs[0]:stackargs[0] + 32])) stk.append(utils.big_endian_to_int(data)) elif op == 'MSTORE': if len(mem) < ceil32(stackargs[0] + 32): mem.extend([0] * (ceil32(stackargs[0] + 32) - len(mem))) v = stackargs[1] for i in range(31, -1, -1): mem[stackargs[0] + i] = v % 256 v /= 256 elif op == 'MSTORE8': if len(mem) < ceil32(stackargs[0] + 1): mem.extend([0] * (ceil32(stackargs[0] + 1) - len(mem))) mem[stackargs[0]] = stackargs[1] % 256 elif op == 'SLOAD': stk.append(block.get_storage_data(msg.to, stackargs[0])) elif op == 'SSTORE': block.set_storage_data(msg.to, stackargs[0], stackargs[1]) elif op == 'JUMP': compustate.pc = stackargs[0] elif op == 'JUMPI': if stackargs[1]: compustate.pc = stackargs[0] elif op == 'PC': stk.append(compustate.pc) 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 = oldpc + 1 + pushnum dat = code[oldpc + 1: oldpc + 1 + pushnum] stk.append(utils.big_endian_to_int(dat)) elif op[:3] == 'DUP': # DUP POP POP Debug hint is_debug = 1 for i in range(len(stackargs)): if get_op_data(code, oldpc + i + 1)[0] != 'POP': is_debug = 0 break if is_debug: print(' '.join(map(repr, stackargs))) pblogger.log('DEBUG', vals=stackargs) compustate.pc = oldpc + 2 + len(stackargs) else: stk.extend(reversed(stackargs)) stk.append(stackargs[-1]) elif op[:4] == 'SWAP': stk.append(stackargs[0]) stk.extend(reversed(stackargs[1:-1])) stk.append(stackargs[-1]) elif op == 'CREATE': if stackargs[2] and len(mem) < ceil32(stackargs[1] + stackargs[2]): mem.extend([0] * (ceil32(stackargs[1] + stackargs[2]) - len(mem))) value = stackargs[0] data = ''.join(map(chr, mem[stackargs[1]:stackargs[1] + stackargs[2]])) pblogger.log('SUB CONTRACT NEW', sender=msg.to, value=value, data=data.encode('hex')) create_msg = Message(msg.to, '', value, compustate.gas, data) addr, gas, code = create_contract(block, tx, create_msg) pblogger.log('SUB CONTRACT OUT', address=addr, code=code) if addr: stk.append(addr) compustate.gas = gas else: stk.append(0) compustate.gas = 0 elif op == 'CALL': if stackargs[4] and len(mem) < ceil32(stackargs[3] + stackargs[4]): mem.extend([0] * (ceil32(stackargs[3] + stackargs[4]) - len(mem))) if stackargs[6] and len(mem) < ceil32(stackargs[5] + stackargs[6]): mem.extend([0] * (ceil32(stackargs[5] + stackargs[6]) - len(mem))) gas = stackargs[0] to = utils.encode_int(stackargs[1]) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') value = stackargs[2] data = ''.join(map(chr, mem[stackargs[3]:stackargs[3] + stackargs[4]])) pblogger.log('SUB CALL NEW', sender=msg.to, to=to, value=value, gas=gas, data=data.encode('hex')) call_msg = Message(msg.to, to, value, gas, data) result, gas, data = apply_msg_send(block, tx, call_msg) pblogger.log('SUB CALL OUT', result=result, data=data, length=len(data), expected=stackargs[6]) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), stackargs[6])): mem[stackargs[5] + i] = data[i] elif op == 'RETURN': if stackargs[1] and len(mem) < ceil32(stackargs[0] + stackargs[1]): mem.extend([0] * (ceil32(stackargs[0] + stackargs[1]) - len(mem))) return mem[stackargs[0]:stackargs[0] + stackargs[1]] elif op == 'POST': if stackargs[4] and len(mem) < ceil32(stackargs[3] + stackargs[4]): mem.extend([0] * (ceil32(stackargs[3] + stackargs[4]) - len(mem))) gas = stackargs[0] to = utils.encode_int(stackargs[1]) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') value = stackargs[2] data = ''.join(map(chr, mem[stackargs[3]:stackargs[3] + stackargs[4]])) pblogger.log('POST NEW', sender=msg.to, to=to, value=value, gas=gas, data=data.encode('hex')) post_msg = Message(msg.to, to, value, gas, data) block.postqueue.append(post_msg) elif op == 'CALL_STATELESS': if stackargs[4] and len(mem) < ceil32(stackargs[3] + stackargs[4]): mem.extend([0] * (ceil32(stackargs[3] + stackargs[4]) - len(mem))) if stackargs[6] and len(mem) < ceil32(stackargs[5] + stackargs[6]): mem.extend([0] * (ceil32(stackargs[5] + stackargs[6]) - len(mem))) gas = stackargs[0] to = utils.encode_int(stackargs[1]) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') value = stackargs[2] data = ''.join(map(chr, mem[stackargs[3]:stackargs[3] + stackargs[4]])) pblogger.log('SUB CALL NEW', sender=msg.to, to=to, value=value, gas=gas, data=data.encode('hex')) call_msg = Message(msg.to, msg.to, value, gas, data) result, gas, data = apply_msg(block, tx, call_msg, block.get_code(to)) pblogger.log('SUB CALL OUT', result=result, data=data, length=len(data), expected=stackargs[6]) if result == 0: stk.append(0) else: stk.append(1) compustate.gas += gas for i in range(min(len(data), stackargs[6])): mem[stackargs[5] + i] = data[i] elif op == 'SUICIDE': to = utils.encode_int(stackargs[0]) to = (('\x00' * (32 - len(to))) + to)[12:].encode('hex') block.transfer_value(msg.to, to, block.get_balance(msg.to)) block.suicides.append(msg.to) return [] for a in stk: assert isinstance(a, (int, long))
def apply_transaction(block, tx): def rp(actual, target): return '%r, actual:%r target:%r' % (tx, actual, target) # (1) The transaction signature is valid; if not tx.sender: raise UnsignedTransaction(tx) # (2) the transaction nonce is valid (equivalent to the # sender account's current nonce); acctnonce = block.get_nonce(tx.sender) if acctnonce != tx.nonce: raise InvalidNonce(rp(tx.nonce, acctnonce)) # (3) the gas limit is no smaller than the intrinsic gas, # g0, used by the transaction; intrinsic_gas_used = GTXDATA * len(tx.data) + GTXCOST if tx.startgas < intrinsic_gas_used: raise InsufficientStartGas(rp(tx.startgas, intrinsic_gas_used)) # (4) the sender account balance contains at least the # cost, v0, required in up-front payment. total_cost = tx.value + tx.gasprice * tx.startgas if block.get_balance(tx.sender) < total_cost: raise InsufficientBalance( rp(block.get_balance(tx.sender), total_cost)) # check offered gas price is enough if tx.gasprice < block.min_gas_price: raise GasPriceTooLow(rp(tx.gasprice, block.min_gas_price)) # check block gas limit if block.gas_used + tx.startgas > block.gas_limit: BlockGasLimitReached( rp(block.gas_used + tx.startgas, block.gas_limit)) logger_debug(' ') logger_debug('#'*40 + ' NEW TRANSACTION ' + '#'*40) logger_debug(' ') logger_debug('initial: %s', str(block.account_to_dict(tx.sender))) # start transacting ################# block.increment_nonce(tx.sender) # buy startgas success = block.transfer_value(tx.sender, block.coinbase, tx.gasprice * tx.startgas) assert success logger_debug('tx: %s', str(tx.to_dict())) logger_debug('snapshot: %s', str(block.account_to_dict(tx.sender))) message_gas = tx.startgas - intrinsic_gas_used message = Message(tx.sender, tx.to, tx.value, message_gas, tx.data) # MESSAGE if tx.to and tx.to != CREATE_CONTRACT_ADDRESS: result, gas_remained, data = apply_msg_send(block, tx, message) else: # CREATE result, gas_remained, data = create_contract(block, tx, message) if result > 0: result = utils.coerce_addr_to_hex(result) assert gas_remained >= 0 logger.debug( 'applied tx, result %r gas remained %r data/code %r', result, gas_remained, ''.join(map(chr, data)).encode('hex')) # logger.debug(json.dumps(block.to_dict(), indent=2)) if not result: # 0 = OOG failure in both cases logger_debug('tx out of gas') logger_debug('d %s %s', tx.startgas, gas_remained) block.gas_used += tx.startgas output = OUT_OF_GAS else: logger_debug('tx successful') gas_used = tx.startgas - gas_remained # sell remaining gas block.transfer_value( block.coinbase, tx.sender, tx.gasprice * gas_remained) block.gas_used += gas_used if tx.to: output = ''.join(map(chr, data)) else: output = result block.commit_state() logger_debug('post: %s', str(block.account_to_dict(tx.sender))) suicides = block.suicides block.suicides = [] for s in suicides: block.del_account(s) block.add_transaction_to_list(tx) success = output is not OUT_OF_GAS return success, output if success else ''