def coerce_to_int(x): if isinstance(x, int): return x elif len(x) == 40: return rlp.big_endian_to_int(binascii.unhexlify(x)) else: if type(x) != bytes: x = bytes(x, 'ascii') # For addresses. return rlp.big_endian_to_int(x)
def get_storage_data(self, contract_id, key=None): cursor = self.db.cursor() if key == None: cursor.execute('''SELECT * FROM storage WHERE contract_id = ? ''', (contract_id,)) storages = list(cursor) return storages # print('prekey', key) key = key.to_bytes(32, byteorder='big') cursor.execute('''SELECT * FROM storage WHERE contract_id = ? AND key = ?''', (contract_id, key)) storages = list(cursor) # print('key', key) if not storages: return 0 value = storages[0]['value'] value = rlp.big_endian_to_int(value) return value
def apply_op(db, block, tx, msg, processed_code, compustate): # Does not include paying opfee. if compustate.pc >= len(processed_code): return [] op, in_args, out_args, mem_grabs, fee, opcode = processed_code[compustate.pc] # print('APPLYING OP', op) # print('INARGS', in_args) # print('COMPUSTATE.STACK', compustate.stack) # empty stack error if in_args > len(compustate.stack): logger.debug('INSUFFICIENT STACK ERROR (op: {}, needed: {}, available: {})'.format(op, in_args, len(compustate.stack))) return [] # out of gas error if fee > compustate.gas: return out_of_gas_exception('base_gas', fee, compustate, op) pblogger.log('STK', stk=list(reversed(compustate.stack))) for i in range(0, len(compustate.memory), 16): memblk = compustate.memory[i:i+16] # logger.debug('MEM {}'.format(memprint(memblk))) # logger.debug('\tSTORAGE\n\t\t' + '\n\t\t'.join(['{}: {}'.format(utils.hexprint(storage['key']), utils.hexprint(storage['value'])) for storage in block.get_storage_data(msg.to)])) # Log operation log_args = dict(pc=str(compustate.pc), op=op, stackargs=compustate.stack[-1:-in_args-1:-1], # stack=list(reversed(compustate.stack)), 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'] = binascii.hexlify(msg.data) # log('OP', log_args) 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 = bytes(mem[s0: s0 + s1]) stk.append(rlp.big_endian_to_int(utils.sha3(data))) elif op == 'ADDRESS': stk.append(utils.coerce_to_int(msg.to)) elif op == 'BALANCE': addr = stk.pop() addr = utils.coerce_to_hex(addr) stk.append(block.get_balance(addr)) elif op == 'ASSET_BALANCE': addr, asset_id = stk.pop(), stk.pop() addr = utils.coerce_to_hex(addr) asset_name = util.asset_name(asset_id) stk.append(block.get_balance(addr, asset=asset_name)) elif op == 'SEND': # TODO: You can’t send SCH to a contract address. addr, quantity, asset_id = stk.pop(), stk.pop(), stk.pop() asset_name = util.asset_name(asset_id) # TODO: Check balance first. block.transfer_value(tx, msg.to, addr, quantity, asset=asset_name) 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(rlp.big_endian_to_int(dat + b'\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(rlp.big_endian_to_int(block.prevhash)) # elif op == 'COINBASE': # stk.append(rlp.big_endian_to_int(binascii.unhexlify(block.coinbase))) 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 = bytes(mem[s0: s0 + 32]) stk.append(rlp.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)] 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 = bytes(mem[mstart: mstart + msz]) # log('SUB CONTRACT NEW', {'sender': msg.to, 'value': value, 'data': util.hexlify(data)}) pblogger.log('SUB CONTRACT NEW', sender=msg.to, value=value, data=util.hexlify(data)) create_msg = Message(msg.to, '', value, compustate.gas, data) address, gas, code = create_contract(db, block, tx, create_msg) # log('SUB CONTRACT OUT', {'address': address, 'code': block.get_code(address)}) addr = utils.coerce_to_int(address) 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': # TODO: Check that this allows for the sending of SHP to Shellparty addresses, as well as contract addresses. 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 = util.hexlify(((b'\x00' * (32 - len(to))) + to)[12:]) data = bytes(mem[meminstart: meminstart + meminsz]) # log('SUB CALL NEW', {'sender': msg.to, 'to': to, 'value': value, 'gas': gas, 'data': util.hexlify(data)}) pblogger.log('SUB CALL NEW', sender=msg.to, to=to, value=value, gas=gas, data=util.hexlify(data)) call_msg = Message(msg.to, to, value, gas, data) result, gas, data = apply_msg_send(db, block, tx, call_msg) # log('SUB CALL OUT', {'result': result, 'data': data, 'length': data, 'expected': memoutsz}) 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 = util.hexlify(((b'\x00' * (32 - len(to))) + to)[12:]) data = bytes(mem[meminstart: meminstart + meminsz]) post_dict = {'sender': msg.to, 'to': to, 'value': value, 'gas': gas, 'data': util.hexlify(data)} log('POST NEW', post_dict) 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 = util.hexlify(((b'\x00' * (32 - len(to))) + to)[12:]) data = bytes(mem[meminstart: meminstart + meminsz]) # logger.debug('SUB CALL NEW (sender: {}, to: {}, value: {}, gas: {}, data: {})'.format(msg.to, to, value, gas, util.hexlify(data))) pblogger.log('SUB CALL NEW', sender=msg.to, to=msg.to, value=value, gas=gas, data=util.hexlify(data)) call_msg = Message(msg.to, msg.to, value, gas, data) result, gas, data = apply_msg(db, block, tx, call_msg, block.get_code(to)) # logger.debug('SUB CALL OUT (result: {}, data: {}, length: {}, expected: {}'.format(result, data, len(data), memoutsz)) 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 = binascii.hexlify(((b'\x00' * (32 - len(to))) + to)[12:]) block.transfer_value(tx, msg.to, to, block.get_balance(msg.to)) block.suicides_append(msg.to) return [] for a in stk: assert isinstance(a, int)