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 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 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 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 web_page(request): page_path = request.match_info.get('page_path', "index.md") try: req_path = page_path.split("/") abs_path = os.path.join(base_path, *req_path) if page_path.endswith('.md'): markdown_title = req_path[-1] markdown_body = open(abs_path, mode='r', encoding='utf8').read() markdown_body = markdown_body.replace('\\', '\\\\').replace( '\"', '\\\"').replace("\n", "\\n") return web.Response(text=markdown_template.replace( '{:title}', markdown_title, 1).replace('{:body}', markdown_body, 1), headers=utils.CONTENT_TYPE_HTML) elif not os.path.exists(abs_path): return web.Response(text="Not found page. {}".format(req_path[-1]), status=404) elif os.path.isfile(abs_path): return web.Response(body=open(abs_path, mode='rb').read(), headers=utils.CONTENT_TYPE_HTML) else: return web.Response(body=open(os.path.join(abs_path, 'index.html'), mode='rb').read(), headers=utils.CONTENT_TYPE_HTML) except Exception: return utils.error_res()
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 move_one(request): try: post = await utils.content_type_json_check(request) ant_from = post.get('from', C.account2name[C.ANT_UNKNOWN]) ant_to = post['to'] coin_id = int(post.get('coin_id', 0)) amount = int(post['amount']) coins = Balance(coin_id, amount) with create_db(V.DB_ACCOUNT_PATH, f_strict=True) as db: cur = db.cursor() _from = read_name2userid(ant_from, cur) _to = read_name2userid(ant_to, cur) txhash = user_account.move_balance(_from, _to, coins, cur) db.commit() return utils.json_res({ 'txhash': txhash.hex(), 'from_id': _from, 'to_id': _to }) 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()
async def contract_update(request): start = time() post = await utils.content_type_json_check(request) try: c_address = post['c_address'] c_extra_imports = post.get('extra_imports', None) if 'hex' in post: c_bin = a2b_hex(post['hex']) args = ("start_tx", "c_address", "c_storage", "redeem_address" ) # dummy data binary2contract(b=c_bin, extra_imports=c_extra_imports, args=args) # can compile? else: c_bin = None c_settings = post.get('settings', None) 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_update_tx(c_address=c_address, cur=cur, c_bin=c_bin, c_extra_imports=c_extra_imports, c_settings=c_settings, 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 network_info(request): try: networks = list() data = { 'p2p_ver': p2p_python.__version__, 'status': V.P2P_OBJ.core.get_server_header(), 'networks': networks } for user in V.P2P_OBJ.core.user: info = { 'number': user.number, 'endpoint': "{}:{}".format(*user.get_host_port()), 'neers': ["{}:{}".format(*conn) for conn in user.neers], 'sock_type': user.sock_type, 'score': user.score, 'warn': user.warn } info.update(user.header.getinfo()) networks.append(info) return utils.json_res(data) except Exception: 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 create_raw_tx(request): # [version=1] [type=TRANSFER] [time=now] [deadline=now+10800] # [inputs:list()] [outputs:list()] # [gas_price=MINIMUM_PRICE] [gas_amount=MINIMUM_AMOUNT] # [message_type=None] [message=None] post = await utils.content_type_json_check(request) try: publish_time = post.get('time', int(time() - V.BLOCK_GENESIS_TIME)) deadline_time = post.get('deadline', publish_time + 10800) message_type = post.get('message_type', C.MSG_NONE) message = type2message(message_type, post.get('message')) inputs = list() input_address = set() for txhash, txindex in post.get('inputs', list()): txhash = a2b_hex(txhash) inputs.append((txhash, txindex)) input_tx = tx_builder.get_tx(txhash) address, coin_id, amount = input_tx.outputs[txindex] input_address.add(address) tx = TX.from_dict( tx={ 'version': post.get('version', __chain_version__), 'type': post.get('type', C.TX_TRANSFER), 'time': publish_time, 'deadline': deadline_time, 'inputs': inputs, 'outputs': post.get('outputs', list()), 'gas_price': post.get('gas_price', V.COIN_MINIMUM_PRICE), 'gas_amount': 0, 'message_type': message_type, 'message': message }) require_gas = tx.size + len(input_address) * C.SIGNATURE_GAS tx.gas_amount = post.get('gas_amount', require_gas) tx.serialize() return utils.json_res({'tx': tx.getinfo(), 'hex': tx.b.hex()}) except Exception: return utils.error_res()