示例#1
0
文件: utils.py 项目: yoosofan/bc4py
def amount_check(tx, payfee_coin_id):
    # Inputs
    input_coins = CoinObject()
    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(
                hexlify(txhash).decode()))
        address, coin_id, amount = input_tx.outputs[txindex]
        input_coins[coin_id] += amount

    # Outputs
    output_coins = CoinObject()
    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 = CoinObject(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_all_plus_amount():
        raise BlockChainError(
            'There are minus amount coins. {}={}-{}-{}'.format(
                remain_amount, input_coins, output_coins, fee_coins))
示例#2
0
def start_contract_tx(c_address,
                      c_method,
                      cur,
                      c_args=None,
                      outputs=None,
                      sender=C.ANT_UNKNOWN,
                      gas_price=None,
                      additional_gas_amount=None,
                      retention=10800):
    # TXを作成
    now = int(time.time()) - V.BLOCK_GENESIS_TIME
    c_redeem = create_new_user_keypair(C.ANT_NAME_UNKNOWN, cur)
    message = bjson.dumps((c_address, c_method, c_args or tuple(), c_redeem),
                          compress=False)
    tx = TX(
        tx={
            'version': __chain_version__,
            'type': C.TX_START_CONTRACT,
            'time': now,
            'deadline': now + retention,
            'inputs': list(),
            'outputs': outputs or list(),
            'gas_price': gas_price or V.COIN_MINIMUM_PRICE,
            'gas_amount': 1,
            'message_type': C.MSG_BYTE,
            'message': message
        })
    check_output_format(tx.outputs)
    tx.gas_amount = tx.getsize() + 96
    tx.serialize()
    # fill unspents
    fee_coin_id = 0
    input_address = fill_inputs_outputs(
        tx, cur, fee_coin_id, additional_gas_amount
        or V.CONTRACT_MINIMUM_AMOUNT)
    fee_coins = CoinObject(fee_coin_id, tx.gas_price * tx.gas_amount)
    movements = UserCoins()
    movements[sender] -= fee_coins
    movements[C.ANT_OUTSIDE] += fee_coins
    # account check
    send_coins = CoinObject()
    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
示例#3
0
def create_contract_tx(c_bin,
                       cur,
                       sender=C.ANT_UNKNOWN,
                       c_cs=None,
                       gas_price=None,
                       retention=10800):
    assert isinstance(c_bin, bytes), 'contract is bytes code.'
    assert isinstance(sender, int), 'Sender is id.'
    if c_cs:
        for k, v in c_cs.items():
            assert isinstance(k, bytes), 'Key is bytes.'
            assert isinstance(v, bytes), 'Value is bytes.'
    # TXを作成
    now = int(time.time()) - V.BLOCK_GENESIS_TIME
    ck = create_new_user_keypair(C.ANT_NAME_CONTRACT, cur)
    c_address = convert_address(ck, V.BLOCK_CONTRACT_PREFIX)
    message = bjson.dumps((c_address, c_bin, c_cs), compress=False)
    tx = TX(
        tx={
            'version': __chain_version__,
            'type': C.TX_CREATE_CONTRACT,
            'time': now,
            'deadline': now + retention,
            'inputs': list(),
            'outputs': list(),
            'gas_price': gas_price or V.COIN_MINIMUM_PRICE,
            'gas_amount': 1,
            'message_type': C.MSG_BYTE,
            'message': message
        })
    tx.gas_amount = tx.getsize() + C.CONTRACT_CREATE_FEE + 96
    # fill unspents
    fee_coin_id = 0
    input_address = fill_inputs_outputs(tx, cur, fee_coin_id,
                                        C.CONTRACT_CREATE_FEE)
    fee_coins = CoinObject(fee_coin_id, tx.gas_price * tx.gas_amount)
    movements = UserCoins()
    movements[sender] -= fee_coins
    movements[C.ANT_OUTSIDE] += fee_coins
    # account check
    send_coins = CoinObject()
    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 c_address, tx
示例#4
0
def input_output_digest(tx):
    require_cks = set()
    coins = CoinObject()
    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
    coins[0] -= tx.gas_amount * tx.gas_price
    for address, coin_id, amount in tx.outputs:
        coins[coin_id] -= amount
    return require_cks, coins
示例#5
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 = 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)
示例#6
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 = CoinObject(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 BaseException('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()
示例#7
0
def change_mintcoin(mint_id,
                    cur,
                    amount=0,
                    message=None,
                    additional_issue=None,
                    image=None,
                    sender=C.ANT_UNKNOWN):
    mint_old = get_mintcoin(mint_id)
    assert mint_old, 'Not defined MintCoin {}'.format(mint_id)
    mint_new = MintCoinObject(None)
    mint_new.version = mint_old.version + 1
    mint_new.coin_id = mint_id
    mint_new.amount = amount
    mint_new.additional_issue = additional_issue
    mint_address = get_address(mint_old.owner, prefix=V.BLOCK_PREFIX)
    uuid, sk, pk = read_address2keypair(mint_address, cur)
    mint_new.owner = pk
    mint_new.image = image
    mint_new.message = message
    # マージチェック
    mint_new.marge(mint_old)
    # Message内署名
    mint_new.generate_sign(sk)
    mint_new.serialize()
    mint_new.check_param()
    mint_new.check_sign()
    logging.info("New Mintcoin skeleton created coin_id={}".format(
        mint_new.coin_id))
    # movement
    movements = UserCoins()
    minting_coins = CoinObject(mint_id, amount)
    movements[sender] += minting_coins
    movements[C.ANT_OUTSIDE] -= minting_coins
    # TXを作成する
    base_coin_id = 0
    now = int(time.time()) - V.BLOCK_GENESIS_TIME
    tx = TX(
        tx={
            'version':
            __chain_version__,
            'type':
            C.TX_MINT_COIN,
            'time':
            now,
            'deadline':
            now + 10800,
            'inputs':
            list(),
            'outputs': [(MINTCOIN_DUMMY_ADDRESS, base_coin_id,
                         amount)] if 0 < amount else list(),
            'gas_price':
            V.COIN_MINIMUM_PRICE,
            'gas_amount':
            1,
            'message_type':
            C.MSG_BYTE,
            'message':
            mint_new.binary
        })
    tx.gas_amount = tx.getsize() + 96 + C.MINTCOIN_FEE
    tx.serialize()
    fee_coin_id = 0
    input_address = fill_inputs_outputs(tx, cur, fee_coin_id, C.MINTCOIN_FEE)
    fee_coins = CoinObject(fee_coin_id, tx.gas_price * tx.gas_amount)
    # check amount
    check_enough_amount(sender, CoinObject(base_coin_id, amount), fee_coins)
    # replace dummy address
    replace_redeem_dummy_address(tx, cur)
    # replace dummy mint_id
    if amount > 0:
        replace_mint_dummy_address(tx, mint_address, mint_id)
    # 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 mint_new, tx
示例#8
0
def issue_mintcoin(name,
                   unit,
                   amount,
                   digit,
                   cur,
                   gas_price=None,
                   message='',
                   additional_issue=True,
                   image=None,
                   sender=C.ANT_UNKNOWN):
    mint = MintCoinObject(None)
    new_mint_id = get_new_coin_id()
    mint.version = 0
    mint.coin_id = new_mint_id
    mint.name = name
    mint.unit = unit
    mint.digit = digit
    mint.supply_before = 0
    mint.amount = amount
    mint.additional_issue = additional_issue
    new_mint_address = create_new_user_keypair(C.ANT_NAME_UNKNOWN, cur)
    uuid, sk, pk = read_address2keypair(new_mint_address, cur)
    mint.owner = pk
    mint.image = image
    mint.message = message
    # Message内署名
    mint.generate_sign(sk)
    mint.serialize()
    mint.check_param()
    mint.check_sign()
    logging.info("New Mintcoin skeleton created coin_id={}".format(
        mint.coin_id))
    # movement
    movements = UserCoins()
    minting_coins = CoinObject(new_mint_id, amount)
    movements[sender] += minting_coins
    movements[C.ANT_OUTSIDE] -= minting_coins
    # TXを作成する
    base_coin_id = 0
    now = int(time.time()) - V.BLOCK_GENESIS_TIME
    tx = TX(
        tx={
            'version': __chain_version__,
            'type': C.TX_MINT_COIN,
            'time': now,
            'deadline': now + 10800,
            'inputs': list(),
            'outputs': [(MINTCOIN_DUMMY_ADDRESS, base_coin_id, amount)],
            'gas_price': gas_price or V.COIN_MINIMUM_PRICE,
            'gas_amount': 1,
            'message_type': C.MSG_BYTE,
            'message': mint.binary
        })
    tx.gas_amount = tx.getsize() + 96 + C.MINTCOIN_FEE
    tx.serialize()
    # fill unspents
    fee_coin_id = 0
    input_address = fill_inputs_outputs(tx, cur, fee_coin_id, C.MINTCOIN_FEE)
    fee_coins = CoinObject(fee_coin_id, tx.gas_price * tx.gas_amount)
    # check amount
    check_enough_amount(sender, CoinObject(base_coin_id, amount), fee_coins)
    # replace dummy address
    replace_redeem_dummy_address(tx, cur)
    # replace dummy mint_id
    replace_mint_dummy_address(tx, new_mint_address, new_mint_id)
    # 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 mint, tx
示例#9
0
def fill_inputs_outputs(finish_tx,
                        c_address,
                        start_hash,
                        redeem_gas,
                        dust_percent=0.8,
                        utxo_cashe=None):
    assert finish_tx.gas_price > 0, "Gas params is none zero."
    # outputsの合計を取得
    output_coins = CoinObject()
    for address, coin_id, amount in finish_tx.outputs.copy():
        if address == DUMMY_REDEEM_ADDRESS:
            # 償還Outputは再構築するので消す
            finish_tx.outputs.remove((address, coin_id, amount))
            continue
        output_coins[coin_id] += amount
    # 一時的にfeeの概算
    fee_coins = CoinObject(coin_id=0,
                           amount=finish_tx.gas_price * finish_tx.gas_amount)
    # 必要なだけinputsを取得
    finish_tx.inputs.clear()
    need_coins = output_coins + fee_coins
    input_coins = CoinObject()
    f_dust_skipped = False
    if utxo_cashe is None:
        utxo_iter = get_utxo_iter({c_address})
        utxo_cashe = list()
        f_put_cashe = True
    else:
        utxo_iter = utxo_cashe
        f_put_cashe = False
    for dummy, height, txhash, txindex, coin_id, amount in utxo_iter:
        if f_put_cashe:
            utxo_cashe.append(
                (dummy, 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
        finish_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(finish_tx, c_address, start_hash,
                                       redeem_gas, new_dust_percent)
        elif len(finish_tx.inputs) > 255:
            logging.debug(
                'Too many inputs, unspent tx\'s amount is too small.')
            return False
        else:
            # 失敗に変更
            logging.debug('Insufficient balance. inputs={} needs={}'.format(
                input_coins, need_coins))
            return False
    # redeemを計算
    redeem_coins = input_coins - output_coins - fee_coins
    for coin_id, amount in redeem_coins:
        finish_tx.outputs.append((DUMMY_REDEEM_ADDRESS, coin_id, amount))
    # Feeをチェックし再計算するか決める
    finish_tx.serialize()
    need_gas_amount = finish_tx.getsize() - redeem_gas
    if finish_tx.gas_amount == need_gas_amount:
        return True
    elif need_gas_amount > 0:
        # FINISH_TXのみGasは負の値
        # Gas使いすぎ,失敗に変更
        logging.debug(
            'Too match gas used. need_gas={}'.format(need_gas_amount))
        return False
    else:
        # insufficient gas
        logging.debug("Retry calculate tx fee. [{}=>{}+{}={}]".format(
            finish_tx.gas_amount, finish_tx.getsize(), redeem_gas,
            need_gas_amount))
        finish_tx.gas_amount = need_gas_amount
        return fill_inputs_outputs(finish_tx, c_address, start_hash,
                                   redeem_gas, dust_percent, utxo_cashe)
示例#10
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 = UserCoins()
    outputs = list()
    coins = CoinObject()
    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 = CoinObject(coin_id=fee_coin_id,
                           amount=tx.gas_price * tx.gas_amount)
    if f_balance_check:
        # 残高が十分にあるかチェック
        send_coins = CoinObject()
        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
示例#11
0
def check_tx_mint_coin(tx, include_block):
    if not (0 < len(tx.inputs) and 0 < len(tx.outputs) <= 2):
        raise BlockChainError('Input and output is more than 1.')
    elif tx.message_type != C.MSG_BYTE:
        raise BlockChainError('TX_MINT_COIN message is bytes.')
    elif include_block and 0 == include_block.txs.index(tx):
        raise BlockChainError('tx index is not proof tx.')
    elif tx.gas_amount < tx.getsize() + C.MINTCOIN_FEE:
        raise BlockChainError('Insufficient gas amount [{}<{}+{}]'.format(
            tx.gas_amount, tx.getsize(), C.MINTCOIN_FEE))

    coins = CoinObject()
    for txhash, txindex in tx.inputs:
        input_tx = tx_builder.get_tx(txhash)
        if input_tx is None:
            raise BlockChainError('Not found BaseTX {} of {}'.format(
                hexlify(txhash).decode(), tx))
        address, coin_id, amount = input_tx.outputs[txindex]
        coins[coin_id] += amount
        if coin_id != 0:
            raise BlockChainError(
                'TX_MINT_COIN inputs are all coinID 0. {}'.format(coin_id))

    for address, coin_id, amount in tx.outputs:
        coins[coin_id] -= amount
    payfee_coin_id = 0
    coins[payfee_coin_id] -= tx.gas_amount * tx.gas_price

    if sum(coins.values()) < 0:
        print(tx.getinfo())
        raise BlockChainError('mintcoin amount sum is {}'.format(
            sum(coins.values())))

    mint = MintCoinObject(txhash=tx.hash, binary=tx.message)
    mint_id = get_mint_id_from_tx(coins)
    if mint_id:
        # 追加発行あり
        if set(coins.keys()) != {0, mint_id}:
            raise BlockChainError('Allowed two coin_ids [{}!={}]'.format(
                set(coins.keys()), {0, mint_id}))
        if mint_id != mint.coin_id:
            raise BlockChainError('Differ coin_id [{}!={}]'.format(
                mint_id, mint.coin_id))
        elif coins[mint_id] * -1 != mint.amount:
            raise BlockChainError('Differ amount [{}!={}]'.format(
                coins[mint_id] * -1, mint.amount))
    else:
        # 追加発行なし
        if set(coins.keys()) != {0}:
            raise BlockChainError('Allowed one coin_id [{}!={}]'.format(
                set(coins.keys()), {0}))
        if mint.amount != 0:
            raise BlockChainError('No amount [{}!=0]'.format(mint.amount))
        mint_id = mint.coin_id

    try:
        # 読み込んでおかしなところがなければOK
        old_mint = get_mintcoin(mint_id=mint_id, best_block=include_block)
        if include_block is None:
            mint.marge(old_mint)
    except MintCoinError as e:
        raise BlockChainError('Failed check mint coin "{}"'.format(e))
示例#12
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 = CoinObject(0, amount)
        minting_coins = CoinObject(mint_id, amount)
    else:
        send_coins = CoinObject(0, 0)
        minting_coins = CoinObject(0, 0)
    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(m_before.address)
    fee_coins = CoinObject(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 = UserCoins()
    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
示例#13
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 = CoinObject(coin_id=fee_coin_id,
                           amount=tx.gas_price * tx.gas_amount)
    # check amount
    check_enough_amount(sender=sender,
                        send_coins=CoinObject(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 = UserCoins()
    minting_coins = CoinObject(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
示例#14
0
文件: utils.py 项目: yoosofan/bc4py
def fill_inputs_outputs(tx,
                        cur,
                        fee_coin_id=0,
                        additional_fee=0,
                        dust_percent=0.8,
                        utxo_cashe=None):
    assert tx.gas_price > 0, "Gas params is none zero."
    # outputsの合計を取得
    output_coins = CoinObject()
    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 = CoinObject(coin_id=fee_coin_id,
                           amount=tx.gas_price * tx.gas_amount)
    # 必要なだけinputsを取得
    tx.inputs.clear()
    need_coins = output_coins + fee_coins
    input_coins = CoinObject()
    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, cur, fee_coin_id, additional_fee,
                                       new_dust_percent, 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.getsize() + len(input_address) * 96 + additional_fee
    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.getsize() + len(input_address) * 96, additional_fee,
            need_gas_amount))
        tx.gas_amount = need_gas_amount
        return fill_inputs_outputs(tx, cur, fee_coin_id, additional_fee,
                                   dust_percent, utxo_cashe)