def get_balance(self, confirm=6): assert confirm < builder.cashe_limit - builder.batch_size, 'Too few cashe size.' assert builder.best_block, 'Not DataBase init.' # DataBase balance = self.db_balance.copy() with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() # Memory limit_height = builder.best_block.height - confirm for block in builder.best_chain: for tx in block.txs: move_log = read_txhash2log(tx.hash, cur) if move_log is None: if tx.hash in self.memory_movement: move_log = self.memory_movement[tx.hash] if move_log: for user, coins in move_log.movement.items(): for coin_id, amount in coins: if limit_height < block.height: if amount < 0: balance.add_coins(user, coin_id, amount) else: balance.add_coins(user, coin_id, amount) # Unconfirmed for tx in list(tx_builder.unconfirmed.values()): move_log = read_txhash2log(tx.hash, cur) if move_log is None: if tx.hash in self.memory_movement: move_log = self.memory_movement[tx.hash] if move_log: for user, coins in move_log.movement.items(): for coin_id, amount in coins: if amount < 0: balance.add_coins(user, coin_id, amount) return balance
async def contract_transfer(request): start = time() post = await web_base.content_type_json_check(request) try: c_address = post['c_address'] c_method = post['c_method'] c_args = post['c_args'] send_pairs = post.get('send_pairs', None) sender_name = post.get('from', C.ANT_NAME_UNKNOWN) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() sender = read_name2user(sender_name, cur) tx = create_contract_transfer_tx(c_address=c_address, cur=cur, c_method=c_method, c_args=c_args, send_pairs=send_pairs, sender=sender) if not send_newtx(new_tx=tx, outer_cur=cur): raise Exception('Failed to send new tx.') db.commit() return web_base.json_res({ 'hash': hexlify(tx.hash).decode(), 'gas_amount': tx.gas_amount, 'gas_price': tx.gas_price, 'fee': tx.gas_amount * tx.gas_price, 'time': round(time() - start, 3) }) except Exception: return web_base.error_res()
async def change_mint_tx(request): post = await web_base.content_type_json_check(request) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() try: user_name = post.get('account', C.ANT_NAME_UNKNOWN) sender = read_name2user(user_name, cur) mint, mintcoin_tx = change_mintcoin( mint_id=int(post['mint_id']), cur=cur, amount=int(post.get('amount', 0)), message=post.get('message', None), image=post.get('image', None), additional_issue=bool(post['additional_issue']) if 'additional_issue' in post else None, sender=sender) if not send_newtx(new_tx=mintcoin_tx, outer_cur=cur): raise BaseException('Failed to send new tx.') db.commit() data = mintcoin_tx.getinfo() return web_base.json_res({ 'txhash': data['hash'], 'mintcoin': mint.getinfo() }) except BaseException: return web_base.error_res()
async def validate_unconfirmed(request): start = time() post = await web_base.content_type_json_check(request) try: txhash = unhexlify(post['hash'].encode()) tx = tx_builder.get_tx(txhash=txhash) if tx is None or tx.height is not None: return web_base.error_res('You cannot validate tx. {}'.format(tx)) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() new_tx = create_signed_tx_as_validator(tx=tx) assert tx is not new_tx, 'tx={}, new_tx={}'.format( id(tx), id(new_tx)) if not send_newtx(new_tx=new_tx, outer_cur=cur): raise Exception('Failed to send new tx.') db.commit() return web_base.json_res({ 'hash': hexlify(new_tx.hash).decode(), 'gas_amount': new_tx.gas_amount, 'gas_price': new_tx.gas_price, 'fee': new_tx.gas_amount * new_tx.gas_price, 'time': round(time() - start, 3) }) except Exception: return web_base.error_res()
async def issue_mint_tx(request): start = time() post = await web_base.content_type_json_check(request) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() try: user_name = post.get('from', C.ANT_NAME_UNKNOWN) sender = read_name2user(user_name, cur) mint_id, tx = issue_mintcoin( name=post['name'], unit=post['unit'], digit=post.get('digit', 8), amount=post['amount'], cur=cur, description=post.get('description', None), image=post.get('image', None), additional_issue=post.get('additional_issue', True), sender=sender) if not send_newtx(new_tx=tx, outer_cur=cur): raise BaseException('Failed to send new tx.') db.commit() return web_base.json_res({ 'hash': hexlify(tx.hash).decode(), 'gas_amount': tx.gas_amount, 'gas_price': tx.gas_price, 'fee': tx.gas_amount * tx.gas_price, 'time': round(time() - start, 3), 'mint_id': mint_id }) except BaseException: return web_base.error_res()
async def change_mint_tx(request): start = time() post = await web_base.content_type_json_check(request) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() try: user_name = post.get('from', C.ANT_NAME_UNKNOWN) sender = read_name2user(user_name, cur) tx = change_mintcoin(mint_id=post['mint_id'], cur=cur, amount=post.get('amount'), description=post.get('description'), image=post.get('image'), setting=post.get('setting'), new_address=post.get('new_address'), sender=sender) if not send_newtx(new_tx=tx, outer_cur=cur): raise BaseException('Failed to send new tx.') db.commit() return web_base.json_res({ 'hash': hexlify(tx.hash).decode(), 'gas_amount': tx.gas_amount, 'gas_price': tx.gas_price, 'fee': tx.gas_amount * tx.gas_price, 'time': round(time() - start, 3) }) except BaseException: return web_base.error_res()
async def send_many_user(request): start = time.time() if P.F_NOW_BOOTING: return web.Response(text='Now booting...', status=403) post = await web_base.content_type_json_check(request) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() try: user_name = post.get('from', C.ANT_NAME_UNKNOWN) user_id = read_name2user(user_name, cur) send_pairs = list() for address, coin_id, amount in post['pairs']: send_pairs.append((address, int(coin_id), int(amount))) message = post.get('message', None) if message: msg_type = C.MSG_PLAIN msg_body = message.encode() else: msg_type = C.MSG_NONE msg_body = b'' new_tx = send_many(user_id, send_pairs, cur, msg_type=msg_type, msg_body=msg_body) if not send_newtx(new_tx=new_tx, outer_cur=cur): raise BaseException('Failed to send new tx.') db.commit() return web_base.json_res({ 'txhash': hexlify(new_tx.hash).decode(), 'time': round(time.time() - start, 3) }) except Exception as e: db.rollback() return web_base.error_res()
async def contract_create(request): post = await web_base.content_type_json_check(request) with closing(create_db(V.DB_ACCOUNT_PATH, f_on_memory=True)) as db: cur = db.cursor() try: # バイナリをピックルしオブジェクトに戻す c_bin = unhexlify(post['hex'].encode()) c_cs = { k.encode(errors='ignore'): v.encode(errors='ignore') for k, v in post.get('c_cs', dict()).items() } binary2contract(c_bin) # can compile? sender_name = post.get('account', C.ANT_NAME_UNKNOWN) sender_id = read_name2user(sender_name, cur) c_address, c_tx = create_contract_tx(c_bin, cur, sender_id, c_cs) if not send_newtx(new_tx=c_tx, outer_cur=cur): raise BaseException('Failed to send new tx.') db.commit() data = { 'txhash': hexlify(c_tx.hash).decode(), 'c_address': c_address, 'time': c_tx.time, 'fee': { 'gas_price': c_tx.gas_price, 'gas_amount': c_tx.gas_amount, 'total': c_tx.gas_price * c_tx.gas_amount } } return web_base.json_res(data) except BaseException: return web_base.error_res()
async def validator_edit(request): start = time() post = await web_base.content_type_json_check(request) c_address = post.get('c_address', None) new_address = post.get('new_address', None) flag = int(post.get('flag', F_NOP)) sig_diff = int(post.get('sig_diff', 0)) try: with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() if c_address is None: c_address = create_new_user_keypair(name=C.ANT_NAME_CONTRACT, cur=cur) tx = create_validator_edit_tx(c_address=c_address, new_address=new_address, flag=flag, sig_diff=sig_diff) if not send_newtx(new_tx=tx, outer_cur=cur): raise Exception('Failed to send new tx.') db.commit() return web_base.json_res({ 'hash': hexlify(tx.hash).decode(), 'gas_amount': tx.gas_amount, 'gas_price': tx.gas_price, 'fee': tx.gas_amount * tx.gas_price, 'time': round(time() - start, 3) }) except Exception: return web_base.error_res()
def get_unspents_iter(outer_cur=None, best_chain=None): target_address = set() with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = outer_cur or db.cursor() for (uuid, address, user) in read_pooled_address_iter(cur): target_address.add(address) return get_utxo_iter(target_address=target_address, best_block=None, best_chain=best_chain)
def _callback(data_list): if isinstance(data_list[0], str): logging.error("Callback error, {}".format(data_list[0])) return with closing(create_db(V.DB_ACCOUNT_PATH)) as db: insert_keypairs(data_list, db.cursor()) db.commit() logging.debug("Generate {} keypairs.".format(len(data_list)))
def message2signature(raw, address): # sign by address with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() uuid, sk, pk = read_address2keypair(address, cur) if sk is None: raise BlockChainError('Not found address {}'.format(address)) return pk, sign(msg=raw, sk=sk, pk=pk)
def im_a_validator(best_block=None): validator_cks, required_num = get_validator_info(best_block) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() for address in validator_cks: if read_address2user(address, cur): return address return None
def check_related_address(address_list): r = list() with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() for address in address_list: user = read_address2user(address=address, cur=cur) if user: r.append((read_user2name(user, cur), address)) return r
async def list_balance(request): confirm = int(request.query.get('confirm', 6)) users = user_account.get_balance(confirm) data = dict() with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() for user, balance in users.items(): data[read_user2name(user, cur)] = balance.coins return web_base.json_res(data)
async def new_address(request): with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() user_name = request.query.get('account', C.ANT_NAME_UNKNOWN) user_id = read_name2user(user_name, cur) address = create_new_user_keypair(user_name, cur) db.commit() if user_id == C.ANT_CONTRACT: address = convert_address(address, V.BLOCK_CONTRACT_PREFIX) return web_base.json_res({'account': user_name, 'user_id': user_id, 'address': address})
def _debug(sql, path, explain=True): with closing(create_db(path)) as db: db.set_trace_callback(sql_info) cur = db.cursor() f = cur.execute(('explain query plan ' if explain else '') + sql) if explain: print(f.fetchone()[-1]) else: c = 0 for d in f.fetchall(): print(c, ':', ', '.join(map(str, d))) c += 1
def init(self): with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() memory_sum = UserCoins() for move_log in read_log_iter(cur): # logに記録されてもBlockに取り込まれていないならTXは存在せず if builder.db.read_tx(move_log.txhash): memory_sum += move_log.movement else: logging.warning("It's unknown log {}".format(move_log)) # delete_log(move_log.txhash, cur) self.db_balance += memory_sum
async def lock_database(request): with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() if is_locked_database(cur): return web.Response(text='Already locked database.', status=400) new_key = AESCipher.create_key() change_encrypt_key(new_key, cur) V.ENCRYPT_KEY = new_key if is_locked_database(cur): return web.Response(text='Failed unlock check filed.', status=400) db.commit() return web_base.json_res({'key': new_key})
async def get_keypair(request): with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() try: address = request.query['address'] uuid, sk, pk = read_address2keypair(address, cur) return web_base.json_res({ 'uuid': uuid, 'address': address, 'private_key': sk, 'public_key': pk}) except BlockChainError as e: return web.Response(text=str(e), status=400)
async def unlock_database(request): post = await web_base.content_type_json_check(request) old_key = V.ENCRYPT_KEY V.ENCRYPT_KEY = post.get('password', None) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: if is_locked_database(db.cursor()): V.ENCRYPT_KEY = old_key return web.json_response({ 'result': False, 'status': 'locked' }, status=400) else: return web_base.json_res({'result': True, 'status': 'unlocked'})
def get_movement_iter(self, start=0, f_dict=False): count = 0 with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() # Unconfirmed for tx in sorted(tx_builder.unconfirmed.values(), key=lambda x: x.time, reverse=True): move_log = read_txhash2log(tx.hash, cur) if move_log is None: if tx.hash in self.memory_movement: move_log = self.memory_movement[tx.hash] else: if tx.hash in self.memory_movement: move_log.pointer = self.memory_movement[ tx.hash].pointer if move_log: if count >= start: if f_dict: yield move_log.get_dict_data(cur) else: yield move_log.get_tuple_data() count += 1 # Memory for block in reversed(builder.best_chain): for tx in block.txs: move_log = read_txhash2log(tx.hash, cur) if move_log is None: if tx.hash in self.memory_movement: move_log = self.memory_movement[tx.hash] else: if tx.hash in self.memory_movement: move_log.pointer = self.memory_movement[ tx.hash].pointer if move_log: if count >= start: if f_dict: yield move_log.get_dict_data(cur) else: yield move_log.get_tuple_data() count += 1 # DataBase for move_log in read_log_iter(cur, start - count): # TRANSFERなど はDBとMemoryの両方に存在する if move_log.txhash in self.memory_movement: continue elif f_dict: yield move_log.get_dict_data(cur) else: yield move_log.get_tuple_data()
async def list_account_address(request): with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() user_name = request.query.get('account', C.ANT_NAME_UNKNOWN) user_id = read_name2user(user_name, cur) address_list = list() for uuid, address, user in read_pooled_address_iter(cur): if user_id == user: if user == C.ANT_CONTRACT: address_list.append(convert_address(ck=address, prefix=V.BLOCK_CONTRACT_PREFIX)) else: address_list.append(address) return web_base.json_res({ 'account': user_name, 'user_id': user_id, 'address': address_list})
def get_dict_data(self, outer_cur=None): with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = outer_cur or db.cursor() movement = { read_user2name(user, cur): coins.coins for user, coins in self.movement.items() } return { 'txhash': hexlify(self.txhash).decode(), 'height': self.height, 'on_memory': self.on_memory, 'type': C.txtype2name[self.type], 'movement': movement, 'time': self.time + V.BLOCK_GENESIS_TIME }
def move_balance(self, _from, _to, coins, outer_cur=None): assert isinstance(coins, CoinObject), 'coins is CoinObject.' with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = outer_cur or db.cursor() try: # DataBaseに即書き込む(Memoryに入れない) movements = UserCoins() movements[_from] -= coins movements[_to] += coins txhash = insert_log(movements, cur) if outer_cur is None: db.commit() self.db_balance += movements return txhash except BaseException: db.rollback()
def move_balance(self, _from, _to, coins, outer_cur=None): assert isinstance(coins, Balance), 'coins is Balance.' with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = outer_cur or db.cursor() try: # DataBaseに即書き込む(Memoryに入れない) movements = Accounting() movements[_from] -= coins movements[_to] += coins txhash = insert_log(movements, cur) if outer_cur is None: db.commit() self.db_balance += movements return txhash except Exception: logging.error("Failed move_balance,", exc_info=True) db.rollback()
def init(self, f_delete=False): assert f_delete is False, 'Unsafe function!' with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() memory_sum = Accounting() for move_log in read_log_iter(cur): # logに記録されてもBlockに取り込まれていないならTXは存在せず if builder.db.read_tx(move_log.txhash): memory_sum += move_log.movement else: logging.debug("It's unknown log {}".format(move_log)) if f_delete: delete_log(move_log.txhash, cur) if f_delete: logging.warning("Delete user's old unconfirmed tx.") db.commit() self.db_balance += memory_sum
async def move_many(request): try: post = await web_base.content_type_json_check(request) ant_from = post.get('from', C.ANT_NAME_UNKNOWN) ant_to = post['to'] coins = CoinObject() for k, v in post['coins'].items(): coins[int(k)] += int(v) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() _from = read_name2user(ant_from, cur) _to = read_name2user(ant_to, cur) txhash = user_account.move_balance(_from, _to, coins, cur) db.commit() return web_base.json_res({ 'txhash': hexlify(txhash).decode(), 'from_id': _from, 'to_id': _to}) except Exception as e: return web.Response(text=str(e), status=400)
async def change_password(request): post = await web_base.content_type_json_check(request) with closing(create_db(V.DB_ACCOUNT_PATH)) as db: db.isolation_level = 'EXCLUSIVE' cur = db.cursor() old_key = V.ENCRYPT_KEY V.ENCRYPT_KEY = post.get('old', None) if is_locked_database(cur): V.ENCRYPT_KEY = old_key return web.Response(text='Failed to unlock database first of all.', status=400) new_key = post.get('new', None) change_encrypt_key(new_key, cur) V.ENCRYPT_KEY = new_key if is_locked_database(cur): V.ENCRYPT_KEY = old_key return web.Response( text='Unlock success, but failed to change key.', status=400) db.commit() return web_base.json_res({'result': True})
def new_batch_apply(self, batched_blocks): with closing(create_db(V.DB_ACCOUNT_PATH)) as db: cur = db.cursor() for block in batched_blocks: for tx in block.txs: move_log = read_txhash2log(tx.hash, cur) if move_log: # User操作の記録 self.db_balance += move_log.movement if tx.hash in self.memory_movement: del self.memory_movement[tx.hash] # logging.debug("Already recoded log {}".format(tx)) elif tx.hash in self.memory_movement: # db_balanceに追加 _type, movement, _time = self.memory_movement[tx.hash].get_tuple_data() self.db_balance += movement # memory_movementから削除 del self.memory_movement[tx.hash] # insert_log insert_log(movement, cur, _type, _time, tx.hash) db.commit()