Ejemplo n.º 1
0
def amount_check(tx, payfee_coin_id):
    # Inputs
    input_coins = Balance()
    for txhash, txindex in tx.inputs:
        input_tx = tx_builder.get_tx(txhash)
        if input_tx is None:
            raise BlockChainError('Not found input tx {}'.format(txhash.hex()))
        address, coin_id, amount = input_tx.outputs[txindex]
        input_coins[coin_id] += amount

    # Outputs
    output_coins = Balance()
    for address, coin_id, amount in tx.outputs:
        if amount <= 0:
            raise BlockChainError('Input amount is more than 0')
        output_coins[coin_id] += amount

    # Fee
    fee_coins = Balance(coin_id=payfee_coin_id,
                        amount=tx.gas_price * tx.gas_amount)

    # Check all plus amount
    remain_amount = input_coins - output_coins - fee_coins
    if not remain_amount.is_empty():
        raise BlockChainError(
            '77 Don\'t match input/output. {}={}-{}-{}'.format(
                remain_amount, input_coins, output_coins, fee_coins))
Ejemplo n.º 2
0
async def sendtoaddress(*args, **kwargs):
    """
    Send an amount to a given address.

    Arguments:
        1. "address"            (string, required) The bitcoin address to send to.
        2. "amount"             (numeric or string, required) The amount in BTC to send. eg 0.1
        3. "comment"            (string, optional) A comment used to store what the transaction is for.
                                     This is not part of the transaction, just kept in your wallet.
        4. "comment_to"         (string, optional) A comment to store the name of the person or organization
                                     to which you're sending the transaction. This is not part of the
                                     transaction, just kept in your wallet.
        5. subtractfeefromamount  (boolean, optional, default=false) The fee will be deducted from the amount being sent.
                                     The recipient will receive less bitcoins than you enter in the amount field.

    Result:
        "txid"                  (string) The transaction id.
    """
    if len(args) < 2:
        raise ValueError('too few arguments num={}'.format(len(args)))
    address, amount, *options = args
    if not is_address(address, V.BECH32_HRP, 0):
        raise ValueError('address is invalid')
    amount = int(amount * pow(10, V.COIN_DIGIT))
    _comment = str(
        options[0]) if 0 < len(options) else None  # do not use by Yiimp
    _comment_to = str(
        options[1]) if 1 < len(options) else None  # do not use by Yiimp
    subtract_fee_amount = bool(options[2]) if 2 < len(options) else False

    # execute send
    error = None
    from_id = C.ANT_UNKNOWN
    coin_id = 0
    coins = Balance(coin_id, amount)
    with create_db(V.DB_ACCOUNT_PATH) as db:
        cur = db.cursor()
        try:
            new_tx = send_from(from_id,
                               address,
                               coins,
                               cur,
                               subtract_fee_amount=subtract_fee_amount)
            if send_newtx(new_tx=new_tx, outer_cur=cur):
                db.commit()
            else:
                error = 'Failed to send new tx'
                db.rollback()
        except Exception as e:
            error = str(e)
            log.debug("sendtoaddress", exc_info=True)
            db.rollback()

    # submit result
    if error:
        raise ValueError(error)
    return new_tx.hash.hex()
Ejemplo n.º 3
0
def input_output_digest(tx):
    require_cks = set()
    coins = Balance()
    # inputs
    for txhash, txindex in tx.inputs:
        input_tx = tx_builder.get_tx(txhash=txhash)
        if input_tx is None:
            raise BlockChainError('input tx is None. {}:{}'.format(hexlify(txhash).decode(), txindex))
        address, coin_id, amount = input_tx.outputs[txindex]
        require_cks.add(address)
        coins[coin_id] += amount
    # outputs
    for address, coin_id, amount in tx.outputs:
        coins[coin_id] -= amount
    # fee
    coins[0] -= tx.gas_amount * tx.gas_price
    return require_cks, coins
Ejemplo n.º 4
0
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()
Ejemplo n.º 5
0
async def move_many(request):
    try:
        post = await utils.content_type_json_check(request)
        ant_from = post.get('from', C.account2name[C.ANT_UNKNOWN])
        ant_to = post['to']
        coins = Balance()
        for k, v in post['coins'].items():
            coins[int(k)] += int(v)
        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 as e:
        return web.Response(text=str(e), status=400)
Ejemplo n.º 6
0
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()
Ejemplo n.º 7
0
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 = Balance()
        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)
Ejemplo n.º 8
0
async def send_from_user(request):
    start = 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:
            from_name = post.get('from', C.ANT_NAME_UNKNOWN)
            from_id = read_name2user(from_name, cur)
            to_address = post['address']
            coin_id = int(post.get('coin_id', 0))
            amount = int(post['amount'])
            coins = Balance(coin_id, 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_from(from_id,
                               to_address,
                               coins,
                               cur,
                               msg_type=msg_type,
                               msg_body=msg_body)
            if not send_newtx(new_tx=new_tx, outer_cur=cur):
                raise BlockChainError('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 as e:
            db.rollback()
            return web_base.error_res()
Ejemplo n.º 9
0
Archivo: utils.py Proyecto: kmn/bc4py
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)
Ejemplo n.º 10
0
def send_many(sender,
              send_pairs,
              cur,
              fee_coin_id=0,
              gas_price=None,
              msg_type=C.MSG_NONE,
              msg_body=b'',
              subtract_fee_from_amount=False,
              retention=10800):
    assert isinstance(sender, int), 'Sender is user id'
    assert 0 < len(send_pairs), 'Empty send_pairs'
    # send_pairs check
    movements = Accounting()
    send_coins = Balance()
    outputs = list()
    coins = Balance()
    for address, coin_id, amount in send_pairs:
        assert isinstance(address, str)
        assert isinstance(coin_id, int) and isinstance(amount, int), 'CoinID, amount is int'
        coins[coin_id] += amount
        outputs.append((address, coin_id, amount))
        user = read_address2userid(address=address, cur=cur)
        if user is not None:
            movements[user][coin_id] += amount  # send to myself
    movements[sender] -= coins
    # movements[C.ANT_OUTSIDE] += coins
    # tx
    now = int(time() - V.BLOCK_GENESIS_TIME)
    tx = TX.from_dict(
        tx={
            'version': __chain_version__,
            'type': C.TX_TRANSFER,
            'time': now,
            'deadline': now + retention,
            'inputs': list(),
            'outputs': outputs,
            'gas_price': gas_price or V.COIN_MINIMUM_PRICE,
            'gas_amount': 1,
            'message_type': msg_type,
            'message': msg_body
        })
    tx.gas_amount = tx.size + C.SIGNATURE_GAS
    # fill unspents
    input_address = fill_inputs_outputs(tx=tx, cur=cur, fee_coin_id=fee_coin_id)
    # subtract fee from amount
    if subtract_fee_from_amount:
        if fee_coin_id != 0:
            raise BlockChainError('subtract_fee option require fee_coin_id=0')
        subtract_fee = subtract_fee_from_user_balance(tx)
        # fee returns to sender's balance
        movements[sender][0] += subtract_fee
        send_coins[0] -= subtract_fee
    fee_coins = Balance(coin_id=fee_coin_id, amount=tx.gas_price * tx.gas_amount)
    # check enough balance account have
    for address, coin_id, amount in send_pairs:
        send_coins[coin_id] += amount
    check_enough_amount(sender=sender, send_coins=send_coins, fee_coins=fee_coins, cur=cur)
    # replace dummy address
    replace_redeem_dummy_address(tx, cur)
    # setup signature
    tx.serialize()
    setup_signature(tx, input_address)
    movements[sender] -= fee_coins
    # movements[C.ANT_OUTSIDE] += fee_coins
    insert_movelog(movements, cur, tx.type, tx.time, tx.hash)
    return tx
Ejemplo n.º 11
0
def change_mintcoin(mint_id,
                    cur,
                    amount=None,
                    description=None,
                    image=None,
                    setting=None,
                    new_address=None,
                    gas_price=None,
                    sender=C.ANT_UNKNOWN,
                    retention=10800):
    assert amount or description or image or setting or new_address
    params = dict()
    if description:
        params['description'] = description
    if image:
        params['image'] = image
    if new_address:
        params['address'] = new_address
    if len(params) == 0:
        params = None
    if not params and not setting and not amount:
        raise BlockChainError('No update found.')
    m_before = get_mintcoin_object(coin_id=mint_id)
    if m_before.version == -1:
        raise BlockChainError('Not init mintcoin. {}'.format(m_before))
    result = check_mintcoin_new_format(m_before=m_before,
                                       new_params=params,
                                       new_setting=setting)
    if isinstance(result, str):
        raise BlockChainError('check_mintcoin_new_format(): {}'.format(result))
    msg_body = bjson.dumps((mint_id, params, setting), compress=False)
    tx = TX(
        tx={
            'type': C.TX_MINT_COIN,
            'gas_price': gas_price or V.COIN_MINIMUM_PRICE,
            'gas_amount': 1,
            'message_type': C.MSG_BYTE,
            'message': msg_body
        })
    if amount:
        tx.outputs.append((MINTCOIN_DUMMY_ADDRESS, 0, amount))
        send_coins = Balance(0, amount)
        minting_coins = Balance(mint_id, amount)
    else:
        send_coins = Balance(0, 0)
        minting_coins = Balance(0, 0)
    tx.update_time(retention)
    additional_gas = C.MINTCOIN_GAS + C.SIGNATURE_GAS  # for mint_coin user signature
    tx.gas_amount = tx.size + C.SIGNATURE_GAS + additional_gas
    tx.serialize()
    # fill unspents
    fee_coin_id = 0
    input_address = fill_inputs_outputs(tx=tx,
                                        cur=cur,
                                        fee_coin_id=fee_coin_id,
                                        additional_gas=additional_gas)
    input_address.add(m_before.address)
    fee_coins = Balance(coin_id=fee_coin_id,
                        amount=tx.gas_price * tx.gas_amount)
    # check amount
    check_enough_amount(sender=sender,
                        send_coins=send_coins,
                        fee_coins=fee_coins)
    # replace dummy address
    replace_redeem_dummy_address(tx=tx, cur=cur)
    # replace dummy mint_id
    replace_mint_dummy_address(tx=tx,
                               mint_address=m_before.address,
                               mint_id=mint_id,
                               f_raise=False)
    # setup signature
    tx.serialize()
    setup_signature(tx=tx, input_address=input_address)
    # movement
    movements = Accounting()
    movements[sender] += minting_coins
    movements[C.ANT_OUTSIDE] -= minting_coins
    movements[sender] -= fee_coins
    movements[C.ANT_OUTSIDE] += fee_coins
    insert_log(movements, cur, tx.type, tx.time, tx.hash)
    return tx
Ejemplo n.º 12
0
def issue_mintcoin(name,
                   unit,
                   digit,
                   amount,
                   cur,
                   description=None,
                   image=None,
                   additional_issue=True,
                   change_address=True,
                   gas_price=None,
                   sender=C.ANT_UNKNOWN,
                   retention=10800):
    mint_id = get_new_coin_id()
    sender_name = read_user2name(user=sender, cur=cur)
    mint_address = create_new_user_keypair(name=sender_name, cur=cur)
    params = {
        "name": name,
        "unit": unit,
        "digit": digit,
        "address": mint_address,
        "description": description,
        "image": image
    }
    setting = {
        "additional_issue": additional_issue,
        "change_address": change_address
    }
    m_before = get_mintcoin_object(coin_id=mint_id)
    result = check_mintcoin_new_format(m_before=m_before,
                                       new_params=params,
                                       new_setting=setting)
    if isinstance(result, str):
        raise BlockChainError('check_mintcoin_new_format(): {}'.format(result))
    msg_body = bjson.dumps((mint_id, params, setting), compress=False)
    tx = TX(
        tx={
            'type': C.TX_MINT_COIN,
            'inputs': list(),
            'outputs': [(MINTCOIN_DUMMY_ADDRESS, 0, amount)],
            'gas_price': gas_price or V.COIN_MINIMUM_PRICE,
            'gas_amount': 1,
            'message_type': C.MSG_BYTE,
            'message': msg_body
        })
    tx.update_time(retention)
    additional_gas = C.MINTCOIN_GAS
    tx.gas_amount = tx.size + C.SIGNATURE_GAS + additional_gas
    tx.serialize()
    # fill unspents
    fee_coin_id = 0
    input_address = fill_inputs_outputs(tx=tx,
                                        cur=cur,
                                        fee_coin_id=fee_coin_id,
                                        additional_gas=additional_gas)
    # input_address.add(mint_address)
    fee_coins = Balance(coin_id=fee_coin_id,
                        amount=tx.gas_price * tx.gas_amount)
    # check amount
    check_enough_amount(sender=sender,
                        send_coins=Balance(0, amount),
                        fee_coins=fee_coins)
    # replace dummy address
    replace_redeem_dummy_address(tx=tx, cur=cur)
    # replace dummy mint_id
    replace_mint_dummy_address(tx=tx,
                               mint_address=mint_address,
                               mint_id=mint_id,
                               f_raise=True)
    # setup signature
    tx.serialize()
    setup_signature(tx=tx, input_address=input_address)
    # movement
    movements = Accounting()
    minting_coins = Balance(mint_id, amount)
    movements[sender] += minting_coins
    movements[C.ANT_OUTSIDE] -= minting_coins
    movements[sender] -= fee_coins
    movements[C.ANT_OUTSIDE] += fee_coins
    insert_log(movements, cur, tx.type, tx.time, tx.hash)
    return mint_id, tx
Ejemplo n.º 13
0
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
Ejemplo n.º 14
0
def send_many(sender,
              send_pairs,
              cur,
              fee_coin_id=0,
              gas_price=None,
              msg_type=C.MSG_NONE,
              msg_body=b'',
              f_balance_check=True,
              retention=10800):
    assert isinstance(sender, int), 'Sender is user id.'
    assert 0 < len(send_pairs), 'Empty send_pairs.'
    # send_pairs check
    movements = Accounting()
    outputs = list()
    coins = Balance()
    for address, coin_id, amount in send_pairs:
        assert isinstance(
            address,
            str) and len(address) == 40, 'Recipient is 40 letter string.'
        assert isinstance(coin_id, int) and isinstance(
            amount, int), 'CoinID, amount is int.'
        coins[coin_id] += amount
        outputs.append((address, coin_id, amount))
    movements[sender] -= coins
    movements[C.ANT_OUTSIDE] += coins
    # tx
    now = int(time.time() - V.BLOCK_GENESIS_TIME)
    tx = TX(
        tx={
            'version': __chain_version__,
            'type': C.TX_TRANSFER,
            'time': now,
            'deadline': now + retention,
            'inputs': list(),
            'outputs': outputs,
            'gas_price': gas_price or V.COIN_MINIMUM_PRICE,
            'gas_amount': 1,
            'message_type': msg_type,
            'message': msg_body
        })
    tx.gas_amount = tx.size + C.SIGNATURE_GAS
    # fill unspents
    input_address = fill_inputs_outputs(tx, cur, fee_coin_id, additional_gas=0)
    # account check
    fee_coins = Balance(coin_id=fee_coin_id,
                        amount=tx.gas_price * tx.gas_amount)
    if f_balance_check:
        # 残高が十分にあるかチェック
        send_coins = Balance()
        for address, coin_id, amount in send_pairs:
            send_coins[coin_id] += amount
        check_enough_amount(sender, send_coins, fee_coins)
    if sender in (C.ANT_OUTSIDE, C.ANT_RESERVED):
        # 内部アカウントは不可
        raise BlockChainError('Not allowed inner account.')
    # replace dummy address
    replace_redeem_dummy_address(tx, cur)
    # setup signature
    tx.serialize()
    setup_signature(tx, input_address)
    movements[sender] -= fee_coins
    movements[C.ANT_OUTSIDE] += fee_coins
    insert_log(movements, cur, tx.type, tx.time, tx.hash)
    return tx