async def sign_raw_tx(request): post = await utils.content_type_json_check(request) try: binary = a2b_hex(post['hex']) other_pairs = dict() for sk in post.get('pairs', list()): sk = a2b_hex(sk) keypair: PyKeyPair = PyKeyPair.from_secret_key(sk) r, s = keypair.get_single_sign(binary) pk = keypair.get_public_key() ck = get_address(pk=pk, hrp=V.BECH32_HRP, ver=C.ADDR_NORMAL_VER) other_pairs[ck] = (pk, r, s) tx = TX.from_binary(binary=binary) for txhash, txindex in tx.inputs: input_tx = tx_builder.get_tx(txhash) address, coin_id, amount = input_tx.outputs[txindex] try: tx.signature.append( sign_message_by_address(raw=tx.b, address=address)) except BlockChainError: if address not in other_pairs: raise BlockChainError( 'Not found secret key "{}"'.format(address)) tx.signature.append(other_pairs[address]) data = tx.getinfo() return utils.json_res({ 'hash': data['hash'], 'signature': data['signature'], 'hex': tx.b.hex() }) except Exception: return utils.error_res()
async def system_private_info(request): try: data = { 'system_ver': __version__, 'api_ver': __api_version__, 'chain_ver': __chain_version__, 'branch': V.BRANCH_NAME, 'message': __message__, 'booting': P.F_NOW_BOOTING, 'connections': len(V.P2P_OBJ.core.user), 'unconfirmed': [txhash.hex() for txhash in tx_builder.unconfirmed.keys()], 'directory': V.DB_HOME_DIR, 'generate_threads': [str(s) for s in generating_threads], 'access_time': int(time()), 'start_time': start_time } return utils.json_res(data) except Exception: return utils.error_res()
async def get_mintcoin_info(request): try: mint_id = int(request.query.get('mint_id', 0)) m = get_mintcoin_object(coin_id=mint_id) return utils.json_res(m.info) except Exception: return utils.error_res()
async def conclude_contract(request): start = time() post = await utils.content_type_json_check(request) try: start_hash = a2b_hex(post['start_hash']) start_tx = tx_builder.get_tx(txhash=start_hash) if start_tx is None: return utils.error_res('Not found start_tx {}'.format( post['start_hash'])) c_address, c_method, redeem_address, c_args = start_tx.encoded_message( ) send_pairs = post.get('send_pairs', None) c_storage = post.get('storage', None) tx = create_conclude_tx(c_address=c_address, start_tx=start_tx, redeem_address=redeem_address, send_pairs=send_pairs, c_storage=c_storage) if not send_newtx(new_tx=tx): raise Exception('Failed to send new tx') return utils.json_res({ 'hash': tx.hash.hex(), '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 utils.error_res()
async def list_account_address(request): with create_db(V.DB_ACCOUNT_PATH) as db: cur = db.cursor() user_name = request.query.get('account', C.account2name[C.ANT_UNKNOWN]) user_id = read_name2userid(user_name, cur) address_list = list() for uuid, address, user in read_pooled_address_iter(cur): if user_id == user: if user == C.ANT_VALIDATOR: address_list.append( convert_address(ck=address, hrp=V.BECH32_HRP, ver=C.ADDR_VALIDATOR_VER)) elif user == C.ANT_CONTRACT: address_list.append( convert_address(ck=address, hrp=V.BECH32_HRP, ver=C.ADDR_CONTRACT_VER)) else: address_list.append(address) return utils.json_res({ 'account': user_name, 'user_id': user_id, 'address': address_list })
async def list_unspents(request): if not chain_builder.db.db_config['addrindex']: return utils.error_res('address isn\'t full indexed') try: best_height = chain_builder.best_block.height page = int(request.query.get('page', 0)) limit = min(100, int(request.query.get('limit', 25))) start = page * limit finish = (page + 1) * limit - 1 f_next_page = False target_address = request.query['address'] unspents_iter = get_unspents_iter( target_address=set(target_address.split(','))) data = list() for index, (address, height, txhash, txindex, coin_id, amount) in enumerate(unspents_iter): if finish < index: f_next_page = True break if index < start: continue data.append({ 'address': address, 'height': height, 'confirmed': None if height is None else best_height - height, 'txhash': txhash.hex(), 'txindex': txindex, 'coin_id': coin_id, 'amount': amount }) return utils.json_res({'data': data, 'next': f_next_page}) except Exception: return utils.error_res()
async def contract_transfer(request): start = time() post = await utils.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.account2name[C.ANT_UNKNOWN]) with create_db(V.DB_ACCOUNT_PATH) as db: cur = db.cursor() sender = read_name2userid(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 utils.json_res({ 'hash': tx.hash.hex(), '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 utils.error_res()
async def validator_edit(request): start = time() post = await utils.content_type_json_check(request) v_address = post.get('v_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 create_db(V.DB_ACCOUNT_PATH) as db: cur = db.cursor() if v_address is None: v_address = generate_new_address_by_userid( user=C.ANT_VALIDATOR, cur=cur) tx = create_validator_edit_tx(v_address=v_address, cur=cur, 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 utils.json_res({ 'hash': tx.hash.hex(), '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 utils.error_res()
async def validate_unconfirmed(request): start = time() post = await utils.content_type_json_check(request) try: txhash = a2b_hex(post['hash']) tx = tx_builder.get_tx(txhash=txhash) if tx is None or tx.height is not None: return utils.error_res('You cannot validate tx. {}'.format(tx)) with 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 utils.json_res({ 'hash': new_tx.hash.hex(), '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 utils.error_res()
async def issue_mint_tx(request): start = time() post = await utils.content_type_json_check(request) with create_db(V.DB_ACCOUNT_PATH, f_strict=True) as db: cur = db.cursor() try: user_name = post.get('from', C.account2name[C.ANT_UNKNOWN]) sender = read_name2userid(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 BlockChainError('Failed to send new tx') db.commit() return utils.json_res({ 'hash': tx.hash.hex(), '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 Exception: return utils.error_res()
async def change_mint_tx(request): start = time() post = await utils.content_type_json_check(request) with create_db(V.DB_ACCOUNT_PATH, f_strict=True) as db: cur = db.cursor() try: user_name = post.get('from', C.account2name[C.ANT_UNKNOWN]) sender = read_name2userid(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 BlockChainError('Failed to send new tx') db.commit() return utils.json_res({ 'hash': tx.hash.hex(), '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 utils.error_res()
async def create_wallet(request): try: post = await utils.content_type_json_check(request) passphrase = str(post.get('passphrase', '')) length = int(post.get('length', 256)) if length not in length_list: return utils.error_res('length is {}'.format(length_list)) mnemonic = Mnemonic(language).generate(length) seed = Mnemonic.to_seed(mnemonic, passphrase) root = Bip32.from_entropy(seed) bip = root.child_key(44 + BIP32_HARDEN).child_key(C.BIP44_COIN_TYPE) # keystone.json format return utils.json_res({ 'mnemonic': mnemonic, 'passphrase': passphrase, 'account_secret_key': bip.extended_key(True), 'account_public_key': bip.extended_key(False), 'path': bip.path, 'comment': 'You must recode "mnemonic" and "passphrase" and remove after. ' 'You can remove "account_secret_key" but you cannot sign and create new account', }) except Exception: return utils.error_res()
async def get_tx_by_hash(request): try: f_pickled = request.query.get('pickle', False) txhash = request.query.get('hash') txhash = a2b_hex(txhash) tx = tx_builder.get_tx(txhash) if tx is None: return web.Response(text="Not found tx", status=400) if f_pickled: tx = pickle.dumps(tx) return utils.json_res(b64encode(tx).decode()) data = tx.getinfo() data['hex'] = tx.b.hex() return utils.json_res(data) except Exception as e: return utils.error_res()
async def get_block_by_height(request): f_pickled = request.query.get('pickle', False) with_tx_info = request.query.get('txinfo', 'false') try: height = int(request.query['height']) except Exception as e: return web.Response(text="Height is not specified", status=400) blockhash = chain_builder.get_block_hash(height) if blockhash is None: return web.Response(text="Not found height", status=400) block = chain_builder.get_block(blockhash) if f_pickled: block = pickle.dumps(block) return utils.json_res(b64encode(block).decode()) data = block.getinfo(with_tx_info == 'true') data['hex'] = block.b.hex() return utils.json_res(data)
async def list_balance(request): confirm = int(request.query.get('confirm', 6)) data = dict() with create_db(V.DB_ACCOUNT_PATH) as db: cur = db.cursor() users = user_account.get_balance(confirm=confirm, outer_cur=cur) for user, balance in users.items(): data[read_userid2name(user, cur)] = dict(balance) return utils.json_res(data)
async def source_compile(request): post = await utils.content_type_json_check(request) # Warning: do not execute unknown source code! try: c_obj = path2contract(path=post['path']) c_bin = contract2binary(c_obj) c_dis = contract2dis(c_obj) return utils.json_res({'hex': c_bin.hex(), 'dis': c_dis}) except Exception: return utils.error_res()
async def get_block_by_hash(request): try: f_pickled = request.query.get('pickle', False) with_tx_info = request.query.get('txinfo', 'false') blockhash = request.query.get('hash') if blockhash is None: return web.Response(text="Not found height", status=400) blockhash = a2b_hex(blockhash) block = chain_builder.get_block(blockhash) if block is None: return web.Response(text="Not found block", status=400) if f_pickled: block = pickle.dumps(block) return utils.json_res(b64encode(block).decode()) data = block.getinfo(with_tx_info == 'true') data['size'] = block.size data['hex'] = block.b.hex() return utils.json_res(data) except Exception as e: return utils.error_res()
async def get_validator_history(request): try: v_address = request.query['v_address'] data = list() # database for index, new_address, flag, txhash, sig_diff in chain_builder.db.read_validator_iter( v_address=v_address): data.append({ 'index': index, 'height': index // 0xffffffff, 'new_address': new_address, 'flag': flag, 'txhash': txhash.hex(), 'sig_diff': sig_diff }) # memory for block in reversed(chain_builder.best_chain): for tx in block.txs: if tx.type != C.TX_VALIDATOR_EDIT: continue _c_address, new_address, flag, sig_diff = tx.encoded_message() if _c_address != v_address: continue index = validator_tx2index(tx=tx) data.append({ 'index': index, 'height': tx.height, 'new_address': new_address, 'flag': flag, 'txhash': tx.hash.hex(), 'sig_diff': sig_diff }) # unconfirmed for tx in sorted(tx_builder.unconfirmed.values(), key=lambda x: x.create_time): if tx.type != C.TX_VALIDATOR_EDIT: continue _c_address, new_address, flag, sig_diff = tx.encoded_message() if _c_address != v_address: continue data.append({ 'index': None, 'height': None, 'new_address': new_address, 'flag': flag, 'txhash': tx.hash.hex(), 'sig_diff': sig_diff }) return utils.json_res(data) except Exception as e: log.error(e) return utils.error_res()
async def contract_storage(request): try: c_address = request.query['c_address'] f_confirmed = bool(request.query.get('confirmed', False)) stop_hash = request.query.get('stophash', None) if stop_hash: stop_hash = a2b_hex(stop_hash) f_pickle = bool(request.query.get('pickle', False)) best_block = chain_builder.best_block if f_confirmed else None c = get_contract_object(c_address=c_address, best_block=best_block, stop_txhash=stop_hash) if c is None: return utils.json_res({}) elif f_pickle: storage = b64encode(pickle.dumps(c.storage)).decode() else: storage = {decode(k): decode(v) for k, v in c.storage.items()} return utils.json_res(storage) except Exception as e: log.error(e) return utils.error_res()
async def chain_fork_info(request): try: main_chain = [block.getinfo() for block in chain_builder.best_chain] orphan_chain = [ block.getinfo() for block in chain_builder.chain.values() if block not in chain_builder.best_chain ] data = { 'main': main_chain, 'orphan': sorted(orphan_chain, key=lambda x: x['height']), 'root': chain_builder.root_block.getinfo() } return utils.json_res(data) except Exception: return utils.error_res()
async def validator_info(request): try: v_address = request.query['v_address'] f_confirmed = bool(request.query.get('confirmed', False)) stop_hash = request.query.get('stophash', None) if stop_hash: stop_hash = a2b_hex(stop_hash) best_block = chain_builder.best_block if f_confirmed else None v = get_validator_object(v_address=v_address, best_block=best_block, stop_txhash=stop_hash) return utils.json_res(v.info) except Exception as e: log.error(e) return utils.error_res()
async def get_keypair(request): try: with create_db(V.DB_ACCOUNT_PATH) as db: cur = db.cursor() address = request.query['address'] uuid, keypair, path = read_address2keypair(address, cur) return utils.json_res({ 'uuid': uuid, 'address': address, 'private_key': keypair.get_secret_key().hex(), 'public_key': keypair.get_public_key().hex(), 'path': path }) except Exception: return utils.error_res()
async def list_transactions(request): page = int(request.query.get('page', 0)) limit = int(request.query.get('limit', 25)) data = list() f_next_page = False start = page * limit for tx_dict in user_account.get_movement_iter(start=page, f_dict=True): if limit == 0: f_next_page = True break tx_dict['index'] = start data.append(tx_dict) start += 1 limit -= 1 return utils.json_res({'txs': data, 'next': f_next_page})
async def get_mintcoin_history(request): try: mint_id = int(request.query.get('mint_id', 0)) data = list() for index, txhash, params, setting in chain_builder.db.read_coins_iter( coin_id=mint_id): data.append({ 'index': index, 'txhash': txhash.hex(), 'params': params, 'setting': setting }) return utils.json_res(data) except Exception: return utils.error_res()
async def watching_info(request): try: # You need to enable watching option! return utils.json_res([{ 'hash': txhash.hex(), 'type': tx.type, 'tx': str(tx), 'time': time, 'c_address': c_address, 'related': related_list, 'args': tuple(map(decode, args)), } for txhash, (time, tx, related_list, c_address, *args) in watching_tx.items()]) except Exception as e: log.error(e) return utils.error_res()
async def chain_info(request): try: best_height = chain_builder.best_block.height best_block = chain_builder.best_block old_block_height = chain_builder.best_chain[0].height - 1 old_block_hash = chain_builder.get_block_hash(old_block_height).hex() data = {'best': best_block.getinfo()} difficulty = dict() for consensus, ratio in V.BLOCK_CONSENSUSES.items(): name = C.consensus2name[consensus] bits, target = get_bits_by_hash(previous_hash=best_block.hash, consensus=consensus) target = float(target) block_time = round(V.BLOCK_TIME_SPAN / ratio * 100) diff = round(DEFAULT_TARGET / target, 8) bias = get_bias_by_hash(previous_hash=best_block.previous_hash, consensus=consensus) difficulty[name] = { 'number': consensus, 'bits': bits.to_bytes(4, 'big').hex(), 'diff': round(diff, 8), 'bias': round(bias, 8), 'fixed_diff': round(diff / bias, 8), 'hashrate(kh/s)': round((MAX_256_FLOAT / target) / block_time / 1000, 3) } data['mining'] = difficulty data['size'] = best_block.size data['checkpoint'] = { 'height': old_block_height, 'blockhash': old_block_hash } data['money_supply'] = GompertzCurve.calc_total_supply(best_height) data['total_supply'] = GompertzCurve.k if F_ADD_CASHE_INFO: data['cashe'] = { 'get_bits_by_hash': str(get_bits_by_hash.cache_info()), 'get_bias_by_hash': str(get_bias_by_hash.cache_info()) } return utils.json_res(data) except Exception: return utils.error_res()
async def list_private_unspents(request): data = list() best_height = chain_builder.best_block.height with create_db(V.DB_ACCOUNT_PATH) as db: cur = db.cursor() for address, height, txhash, txindex, coin_id, amount in get_my_unspents_iter( cur): data.append({ 'address': address, 'height': height, 'confirmed': None if height is None else best_height - height, 'txhash': txhash.hex(), 'txindex': txindex, 'coin_id': coin_id, 'amount': amount }) return utils.json_res(data)
async def send_from_user(request): start = time() if P.F_NOW_BOOTING: return web.Response(text='Now booting', status=403) post = await utils.content_type_json_check(request) with create_db(V.DB_ACCOUNT_PATH, f_strict=True) as db: cur = db.cursor() try: from_name = post.get('from', C.account2name[C.ANT_UNKNOWN]) from_id = read_name2userid(from_name, cur) to_address = post['address'] coin_id = int(post.get('coin_id', 0)) amount = int(post['amount']) coins = Balance(coin_id, amount) if 'hex' in post: msg_type = C.MSG_BYTE msg_body = a2b_hex(post['hex']) elif 'message' in post: msg_type = post.get('message_type', C.MSG_PLAIN) msg_body = type2message(msg_type, post['message']) else: msg_type = C.MSG_NONE msg_body = b'' new_tx = send_from(from_id, to_address, coins, cur, msg_type=msg_type, msg_body=msg_body) if 'R' in post: new_tx.R = a2b_hex(post['R']) if not send_newtx(new_tx=new_tx, outer_cur=cur): raise BlockChainError('Failed to send new tx') db.commit() return utils.json_res({ 'hash': new_tx.hash.hex(), '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 as e: db.rollback() return utils.error_res()
async def import_private_key(request): try: post = await utils.content_type_json_check(request) sk = a2b_hex(post['private_key']) ck = post['address'] name = post.get('account', C.account2name[C.ANT_UNKNOWN]) keypair: PyKeyPair = PyKeyPair.from_secret_key(sk) check_ck = get_address(pk=keypair.get_public_key(), hrp=V.BECH32_HRP, ver=C.ADDR_NORMAL_VER) if ck != check_ck: return utils.error_res('Don\'t match, {}!={}'.format(ck, check_ck)) with create_db(V.DB_ACCOUNT_PATH) as db: cur = db.cursor() user = read_name2userid(name=name, cur=cur) insert_keypair_from_outside(sk=sk, ck=ck, user=user, cur=cur) db.commit() return utils.json_res({'status': True}) except Exception: return utils.error_res()
async def broadcast_tx(request): start = time() post = await utils.content_type_json_check(request) try: binary = a2b_hex(post['hex']) new_tx = TX.from_binary(binary=binary) new_tx.signature = [(a2b_hex(pk), a2b_hex(sig)) for pk, sig in post['signature']] if 'R' in post: new_tx.R = a2b_hex(post['R']) if not send_newtx(new_tx=new_tx): raise BlockChainError('Failed to send new tx') return utils.json_res({ 'hash': new_tx.hash.hex(), '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 utils.error_res()