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 list_unspents(request): data = list() best_height = builder.best_block.height for address, height, txhash, txindex, coin_id, amount in get_unspents_iter(): data.append({ 'address': address, 'height': height, 'confirmed': None if height is None else best_height - height, 'txhash': hexlify(txhash).decode(), 'txindex': txindex, 'coin_id': coin_id, 'amount': amount}) return web_base.json_res(data)
def update_unspents_txs(): global unspents_txs c = 50 while previous_block is None and 0 < c: sleep(0.2) c -= 1 previous_height = previous_block.height proof_txs = list() all_num = 0 for address, height, txhash, txindex, coin_id, amount in get_unspents_iter( ): if height is None: continue if coin_id != 0: continue if not (previous_height + 1 > height + C.MATURE_HEIGHT): continue if not is_address(address, prefix=V.BLOCK_PREFIX): continue if amount < 100000000: continue if staking_limit < all_num: logging.debug("Unspents limit reached, skip by {} limits.".format( staking_limit)) break all_num += 1 proof_tx = TX( tx={ 'type': C.TX_POS_REWARD, 'inputs': [(txhash, txindex)], 'outputs': [(address, 0, 0)], 'gas_price': 0, 'gas_amount': 0, 'message_type': C.MSG_NONE, 'message': b'' }) proof_tx.height = previous_height + 1 proof_tx.pos_amount = amount proof_txs.append(proof_tx) unspents_txs = proof_txs return all_num, len(proof_txs)
def fill_inputs_outputs(tx, cur, fee_coin_id=0, additional_gas=0, dust_percent=0.8, utxo_cashe=None): assert tx.gas_price > 0, "Gas params is none zero." # outputsの合計を取得 output_coins = Balance() for address, coin_id, amount in tx.outputs.copy(): if address == DUMMY_REDEEM_ADDRESS: # 償還Outputは再構築するので消す tx.outputs.remove((address, coin_id, amount)) continue output_coins[coin_id] += amount # 一時的にfeeの概算 fee_coins = Balance(coin_id=fee_coin_id, amount=tx.gas_price * tx.gas_amount) # 必要なだけinputsを取得 tx.inputs.clear() need_coins = output_coins + fee_coins input_coins = Balance() input_address = set() f_dust_skipped = False if utxo_cashe is None: utxo_iter = get_unspents_iter(cur) utxo_cashe = list() f_put_cashe = True else: utxo_iter = utxo_cashe f_put_cashe = False for address, height, txhash, txindex, coin_id, amount in utxo_iter: if f_put_cashe: utxo_cashe.append( (address, height, txhash, txindex, coin_id, amount)) if coin_id not in need_coins: continue elif need_coins[coin_id] * dust_percent > amount: f_dust_skipped = True continue need_coins[coin_id] -= amount input_coins[coin_id] += amount input_address.add(address) tx.inputs.append((txhash, txindex)) if need_coins.is_all_minus_amount(): break else: if f_dust_skipped and dust_percent > 0.00001: new_dust_percent = round(dust_percent * 0.7, 6) logging.debug("Retry by lower dust percent. {}=>{}".format( dust_percent, new_dust_percent)) return fill_inputs_outputs(tx=tx, cur=cur, fee_coin_id=fee_coin_id, additional_gas=additional_gas, dust_percent=new_dust_percent, utxo_cashe=utxo_cashe) elif len(tx.inputs) > 255: raise BlockChainError( 'Too many inputs, unspent tx\'s amount is too small.') else: raise BlockChainError( 'Insufficient balance. inputs={} needs={}'.format( input_coins, need_coins)) # redeemを計算 redeem_coins = input_coins - output_coins - fee_coins for coin_id, amount in redeem_coins: tx.outputs.append((DUMMY_REDEEM_ADDRESS, coin_id, amount)) # Feeをチェックし再計算するか決める tx.serialize() need_gas_amount = tx.size + len( input_address) * C.SIGNATURE_GAS + additional_gas if 0 <= tx.gas_amount - need_gas_amount < 10000: # input/outputを混ぜる return input_address else: # insufficient gas logging.debug("Retry calculate tx fee. [{}=>{}+{}={}]".format( tx.gas_amount, tx.size + len(input_address) * C.SIGNATURE_GAS, additional_gas, need_gas_amount)) tx.gas_amount = need_gas_amount return fill_inputs_outputs(tx=tx, cur=cur, fee_coin_id=fee_coin_id, additional_gas=additional_gas, dust_percent=dust_percent, utxo_cashe=utxo_cashe)
def fill_inputs_outputs(tx, target_address=None, cur=None, signature_num=None, fee_coin_id=0, additional_gas=0, dust_percent=0.8, utxo_cashe=None, depth=0): if MAX_RECURSIVE_DEPTH < depth: raise BlockChainError('over max recursive depth on filling inputs_outputs!') # outputsの合計を取得 output_coins = Balance() for address, coin_id, amount in tx.outputs.copy(): if address == DUMMY_REDEEM_ADDRESS: # 償還Outputは再構築するので消す tx.outputs.remove((address, coin_id, amount)) continue output_coins[coin_id] += amount # 一時的にfeeの概算 fee_coins = Balance(coin_id=fee_coin_id, amount=tx.gas_price * tx.gas_amount) # 必要なだけinputsを取得 tx.inputs.clear() need_coins = output_coins + fee_coins input_coins = Balance() input_address = set() f_dust_skipped = False if utxo_cashe is None: if target_address: utxo_iter = get_unspents_iter(target_address=target_address) elif cur: utxo_iter = get_my_unspents_iter(cur=cur) else: raise Exception('target_address and cur is None?') cashe = list() utxo_cashe = [cashe, utxo_iter] else: cashe, utxo_iter = utxo_cashe for is_cashe, (address, height, txhash, txindex, coin_id, amount) in sum_utxo_iter(cashe, utxo_iter): if not is_cashe: cashe.append((address, height, txhash, txindex, coin_id, amount)) if coin_id not in need_coins: continue if need_coins[coin_id] * dust_percent > amount: f_dust_skipped = True continue need_coins[coin_id] -= amount input_coins[coin_id] += amount input_address.add(address) tx.inputs.append((txhash, txindex)) if need_coins.is_all_minus_amount(): break else: if f_dust_skipped and dust_percent > 0.00001: new_dust_percent = round(dust_percent * 0.7, 6) log.debug("Retry by lower dust percent. {}=>{}".format(dust_percent, new_dust_percent)) return fill_inputs_outputs( tx=tx, target_address=target_address, cur=cur, signature_num=signature_num, fee_coin_id=fee_coin_id, additional_gas=additional_gas, dust_percent=new_dust_percent, utxo_cashe=utxo_cashe, depth=depth+1) elif len(tx.inputs) > 255: raise BlockChainError('TX inputs is too many num={}'.format(len(tx.inputs))) else: raise BlockChainError('Insufficient balance. inputs={} needs={}'.format(input_coins, need_coins)) # redeemを計算 redeem_coins = input_coins - output_coins - fee_coins for coin_id, amount in redeem_coins: tx.outputs.append((DUMMY_REDEEM_ADDRESS, coin_id, amount)) # Feeをチェックし再計算するか決める tx.serialize() if signature_num is None: need_gas_amount = tx.size + additional_gas + len(input_address) * C.SIGNATURE_GAS else: need_gas_amount = tx.size + additional_gas + signature_num * C.SIGNATURE_GAS if tx.gas_amount > need_gas_amount: # swap overflowed gas, gas_amount => redeem_output swap_amount = (tx.gas_amount - need_gas_amount) * tx.gas_price for index, (address, coin_id, amount) in enumerate(tx.outputs): if address != DUMMY_REDEEM_ADDRESS: continue elif coin_id != fee_coin_id: continue else: tx.outputs[index] = (address, coin_id, amount + swap_amount) break else: raise BlockChainError('cannot swap overflowed gas amount={}'.format(swap_amount)) # success swap tx.gas_amount = need_gas_amount tx.serialize() return input_address elif tx.gas_amount < need_gas_amount: # retry insufficient gas log.info("retry calculate fee gasBefore={} gasNext={}".format(tx.gas_amount, need_gas_amount)) tx.gas_amount = need_gas_amount return fill_inputs_outputs( tx=tx, target_address=target_address, cur=cur, signature_num=signature_num, fee_coin_id=fee_coin_id, additional_gas=additional_gas, dust_percent=dust_percent, utxo_cashe=utxo_cashe, depth=depth+1) else: # tx.gas_amount == need_gas_amount return input_address