Exemplo n.º 1
0
Arquivo: account.py Projeto: kmn/bc4py
def read_txhash2log(txhash, cur):
    d = cur.execute(
        """
        SELECT `type`,`user`,`coin_id`,`amount`,`time` FROM `log` WHERE `hash`=?
    """, (txhash, )).fetchall()
    if len(d) == 0:
        return None
    movement = Accounting()
    _type = _time = None
    for _type, user, coin_id, amount, _time in d:
        movement.add_coins(user, coin_id, amount)
    return MoveLog(txhash, _type, movement, _time, False)
Exemplo n.º 2
0
Arquivo: basic.py Projeto: kmn/bc4py
def calc_return_balance(start_tx, c_address, redeem_address):
    """ Calc redeem balance """
    balance = Accounting()
    for address, coin_id, amount in start_tx.outputs:
        if address == c_address:
            balance[redeem_address][coin_id] += amount
    return balance
Exemplo n.º 3
0
def calc_tx_movement(tx, c_address, redeem_address, emulate_gas):
    """ Calc tx inner movement """
    account = Accounting()
    for txhash, txindex in tx.inputs:
        input_tx = tx_builder.get_tx(txhash=txhash)
        address, coin_id, amount = input_tx.outputs[txindex]
        account[address][coin_id] -= amount
    account[redeem_address][0] += (tx.gas_amount+emulate_gas) * tx.gas_price
    account[c_address][0] -= emulate_gas * tx.gas_price
    for address, coin_id, amount in tx.outputs:
        account[address][coin_id] += amount
    return account
Exemplo n.º 4
0
def read_txhash2movelog(txhash, cur):
    """read MoveLog by txhash"""
    d = cur.execute(
        """
        SELECT `type`,`user`,`coin_id`,`amount`,`time` FROM `log` WHERE `hash`=?
    """, (txhash, )).fetchall()
    if len(d) == 0:
        return None
    movement = Accounting()
    ntype = ntime = None
    for ntype, user, coin_id, amount, ntime in d:
        movement[user][coin_id] += amount
    return MoveLog(txhash, ntype, movement, ntime)
Exemplo n.º 5
0
Arquivo: builder.py Projeto: kmn/bc4py
 def move_balance(self, _from, _to, coins, outer_cur=None):
     assert isinstance(coins, Balance), 'coins is Balance.'
     with closing(create_db(V.DB_ACCOUNT_PATH)) as db:
         cur = outer_cur or db.cursor()
         try:
             # DataBaseに即書き込む(Memoryに入れない)
             movements = Accounting()
             movements[_from] -= coins
             movements[_to] += coins
             txhash = insert_log(movements, cur)
             if outer_cur is None:
                 db.commit()
             self.db_balance += movements
             return txhash
         except Exception:
             logging.error("Failed move_balance,", exc_info=True)
             db.rollback()
Exemplo n.º 6
0
Arquivo: builder.py Projeto: kmn/bc4py
 def init(self, f_delete=False):
     assert f_delete is False, 'Unsafe function!'
     with closing(create_db(V.DB_ACCOUNT_PATH)) as db:
         cur = db.cursor()
         memory_sum = Accounting()
         for move_log in read_log_iter(cur):
             # logに記録されてもBlockに取り込まれていないならTXは存在せず
             if builder.db.read_tx(move_log.txhash):
                 memory_sum += move_log.movement
             else:
                 logging.debug("It's unknown log {}".format(move_log))
                 if f_delete:
                     delete_log(move_log.txhash, cur)
         if f_delete:
             logging.warning("Delete user's old unconfirmed tx.")
             db.commit()
         self.db_balance += memory_sum
Exemplo n.º 7
0
Arquivo: builder.py Projeto: kmn/bc4py
 def affect_new_tx(self, tx, outer_cur=None):
     with closing(create_db(V.DB_ACCOUNT_PATH)) as db:
         cur = outer_cur or db.cursor()
         movement = Accounting()
         # send_from_applyで登録済み
         if tx.hash in self.memory_movement:
             return
         # memory_movementに追加
         for txhash, txindex in tx.inputs:
             input_tx = tx_builder.get_tx(txhash)
             address, coin_id, amount = input_tx.outputs[txindex]
             user = read_address2user(address, cur)
             if user is not None:
                 movement.add_coins(user, coin_id, -1 * amount)
         for address, coin_id, amount in tx.outputs:
             user = read_address2user(address, cur)
             if user is not None:
                 movement.add_coins(user, coin_id, amount)
         # check
         if len(movement) == 0:
             return  # 無関係である
         move_log = MoveLog(tx.hash, tx.type, movement, tx.time, True, tx)
         self.memory_movement[tx.hash] = move_log
         logging.debug("Affect account new tx. {}".format(tx))
Exemplo n.º 8
0
Arquivo: builder.py Projeto: kmn/bc4py
 def __init__(self):
     self.db_balance = Accounting()
     # {txhash: (_type, movement, _time),..}
     self.memory_movement = dict()
Exemplo n.º 9
0
Arquivo: builder.py Projeto: kmn/bc4py
class UserAccount:
    def __init__(self):
        self.db_balance = Accounting()
        # {txhash: (_type, movement, _time),..}
        self.memory_movement = dict()

    def init(self, f_delete=False):
        assert f_delete is False, 'Unsafe function!'
        with closing(create_db(V.DB_ACCOUNT_PATH)) as db:
            cur = db.cursor()
            memory_sum = Accounting()
            for move_log in read_log_iter(cur):
                # logに記録されてもBlockに取り込まれていないならTXは存在せず
                if builder.db.read_tx(move_log.txhash):
                    memory_sum += move_log.movement
                else:
                    logging.debug("It's unknown log {}".format(move_log))
                    if f_delete:
                        delete_log(move_log.txhash, cur)
            if f_delete:
                logging.warning("Delete user's old unconfirmed tx.")
                db.commit()
            self.db_balance += memory_sum

    def get_balance(self, confirm=6):
        assert confirm < builder.cashe_limit - builder.batch_size, 'Too few cashe size.'
        assert builder.best_block, 'Not DataBase init.'
        # DataBase
        balance = self.db_balance.copy()
        with closing(create_db(V.DB_ACCOUNT_PATH)) as db:
            cur = db.cursor()
            # Memory
            limit_height = builder.best_block.height - confirm
            for block in builder.best_chain:
                for tx in block.txs:
                    move_log = read_txhash2log(tx.hash, cur)
                    if move_log is None:
                        if tx.hash in self.memory_movement:
                            move_log = self.memory_movement[tx.hash]
                    if move_log:
                        for user, coins in move_log.movement.items():
                            for coin_id, amount in coins:
                                if limit_height < block.height:
                                    if amount < 0:
                                        balance.add_coins(user, coin_id, amount)
                                else:
                                    balance.add_coins(user, coin_id, amount)
            # Unconfirmed
            for tx in list(tx_builder.unconfirmed.values()):
                move_log = read_txhash2log(tx.hash, cur)
                if move_log is None:
                    if tx.hash in self.memory_movement:
                        move_log = self.memory_movement[tx.hash]
                if move_log:
                    for user, coins in move_log.movement.items():
                        for coin_id, amount in coins:
                            if amount < 0:
                                balance.add_coins(user, coin_id, amount)
        return balance

    def move_balance(self, _from, _to, coins, outer_cur=None):
        assert isinstance(coins, Balance), 'coins is Balance.'
        with closing(create_db(V.DB_ACCOUNT_PATH)) as db:
            cur = outer_cur or db.cursor()
            try:
                # DataBaseに即書き込む(Memoryに入れない)
                movements = Accounting()
                movements[_from] -= coins
                movements[_to] += coins
                txhash = insert_log(movements, cur)
                if outer_cur is None:
                    db.commit()
                self.db_balance += movements
                return txhash
            except Exception:
                logging.error("Failed move_balance,", exc_info=True)
                db.rollback()

    def get_movement_iter(self, start=0, f_dict=False):
        count = 0
        with closing(create_db(V.DB_ACCOUNT_PATH)) as db:
            cur = db.cursor()
            # Unconfirmed
            for tx in sorted(tx_builder.unconfirmed.values(), key=lambda x: x.time, reverse=True):
                move_log = read_txhash2log(tx.hash, cur)
                if move_log is None:
                    if tx.hash in self.memory_movement:
                        move_log = self.memory_movement[tx.hash]
                else:
                    if tx.hash in self.memory_movement:
                        move_log.pointer = self.memory_movement[tx.hash].pointer
                if move_log:
                    if count >= start:
                        if f_dict:
                            yield move_log.get_dict_data(cur)
                        else:
                            yield move_log.get_tuple_data()
                    count += 1
            # Memory
            for block in reversed(builder.best_chain):
                for tx in block.txs:
                    move_log = read_txhash2log(tx.hash, cur)
                    if move_log is None:
                        if tx.hash in self.memory_movement:
                            move_log = self.memory_movement[tx.hash]
                    else:
                        if tx.hash in self.memory_movement:
                            move_log.pointer = self.memory_movement[tx.hash].pointer
                    if move_log:
                        if count >= start:
                            if f_dict:
                                yield move_log.get_dict_data(cur)
                            else:
                                yield move_log.get_tuple_data()
                        count += 1
            # DataBase
            for move_log in read_log_iter(cur, start - count):
                # TRANSFERなど はDBとMemoryの両方に存在する
                if move_log.txhash in self.memory_movement:
                    continue
                elif f_dict:
                    yield move_log.get_dict_data(cur)
                else:
                    yield move_log.get_tuple_data()

    def new_batch_apply(self, batched_blocks):
        with closing(create_db(V.DB_ACCOUNT_PATH)) as db:
            cur = db.cursor()
            for block in batched_blocks:
                for tx in block.txs:
                    move_log = read_txhash2log(tx.hash, cur)
                    if move_log:
                        # User操作の記録
                        self.db_balance += move_log.movement
                        if tx.hash in self.memory_movement:
                            del self.memory_movement[tx.hash]
                        # logging.debug("Already recoded log {}".format(tx))
                    elif tx.hash in self.memory_movement:
                        # db_balanceに追加
                        _type, movement, _time = self.memory_movement[tx.hash].get_tuple_data()
                        self.db_balance += movement
                        # memory_movementから削除
                        del self.memory_movement[tx.hash]
                        # insert_log
                        insert_log(movement, cur, _type, _time, tx.hash)
            db.commit()

    def affect_new_tx(self, tx, outer_cur=None):
        with closing(create_db(V.DB_ACCOUNT_PATH)) as db:
            cur = outer_cur or db.cursor()
            movement = Accounting()
            # send_from_applyで登録済み
            if tx.hash in self.memory_movement:
                return
            # memory_movementに追加
            for txhash, txindex in tx.inputs:
                input_tx = tx_builder.get_tx(txhash)
                address, coin_id, amount = input_tx.outputs[txindex]
                user = read_address2user(address, cur)
                if user is not None:
                    movement.add_coins(user, coin_id, -1 * amount)
            for address, coin_id, amount in tx.outputs:
                user = read_address2user(address, cur)
                if user is not None:
                    movement.add_coins(user, coin_id, amount)
            # check
            if len(movement) == 0:
                return  # 無関係である
            move_log = MoveLog(tx.hash, tx.type, movement, tx.time, True, tx)
            self.memory_movement[tx.hash] = move_log
            logging.debug("Affect account new tx. {}".format(tx))
Exemplo 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
Exemplo 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
Exemplo 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
Exemplo n.º 13
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