Example #1
0
def create_conclude_tx(c_address, start_tx, send_pairs=None, c_storage=None):
    assert isinstance(start_tx, TX)
    assert send_pairs is None or isinstance(send_pairs, list)
    assert c_storage is None or isinstance(c_storage, dict)
    message = bjson.dumps((c_address, start_tx.hash, c_storage),
                          compress=False)
    v = get_validator_object(c_address=c_address)
    send_pairs = send_pairs or list()
    tx = TX(
        tx={
            'type': C.TX_CONCLUDE_CONTRACT,
            'time': start_tx.time,
            'deadline': start_tx.deadline,
            'gas_price': start_tx.gas_price,
            'gas_amount': 0,
            'outputs': [tuple(s) for s in send_pairs],
            'message_type': C.MSG_BYTE,
            'message': message
        })
    extra_gas = C.SIGNATURE_GAS * v.require
    tx.gas_amount = tx.size + extra_gas
    # fill unspents
    fill_contract_inputs_outputs(tx=tx,
                                 c_address=c_address,
                                 additional_gas=extra_gas)
    # replace dummy address
    replace_redeem_dummy_address(tx=tx, replace_by=c_address)
    tx.serialize()
    if v.index == -1:
        raise BlockChainError(
            'Not init validator address. {}'.format(c_address))
    if setup_contract_signature(tx, v.validators) == 0:
        raise BlockChainError('Cannot sign, you are not validator.')
    return tx
Example #2
0
def contract_fill(c: Contract, best_block=None, best_chain=None, stop_txhash=None):
    # database
    c_iter = builder.db.read_contract_iter(c_address=c.c_address, start_idx=c.db_index)
    for index, start_hash, finish_hash, (c_method, c_args, c_storage) in c_iter:
        if start_hash == stop_txhash or finish_hash == stop_txhash:
            return
        c.update(db_index=index, start_hash=start_hash, finish_hash=finish_hash,
                 c_method=c_method, c_args=c_args, c_storage=c_storage)
    # memory
    if best_chain:
        _best_chain = None
    elif best_block and best_block == builder.best_block:
        _best_chain = builder.best_chain
    else:
        dummy, _best_chain = builder.get_best_chain(best_block=best_block)
    for block in reversed(best_chain or _best_chain):
        for tx in block.txs:
            if tx.hash == stop_txhash:
                return
            if tx.type != C.TX_CONCLUDE_CONTRACT:
                continue
            c_address, start_hash, c_storage = decode(tx.message)
            if c_address != c.c_address:
                continue
            if start_hash == stop_txhash:
                return
            start_tx = tx_builder.get_tx(txhash=start_hash)
            dummy, c_method, redeem_address, c_args = decode(start_tx.message)
            index = start_tx2index(start_tx=start_tx)
            c.update(db_index=index, start_hash=start_hash, finish_hash=tx.hash,
                     c_method=c_method, c_args=c_args, c_storage=c_storage)
    # unconfirmed (check validator condition satisfied)
    if best_block is None:
        unconfirmed = list()
        for conclude_tx in tuple(tx_builder.unconfirmed.values()):
            if conclude_tx.hash == stop_txhash:
                break
            if conclude_tx.type != C.TX_CONCLUDE_CONTRACT:
                continue
            c_address, start_hash, c_storage = decode(conclude_tx.message)
            if c_address != c.c_address:
                continue
            if start_hash == stop_txhash:
                break
            start_tx = tx_builder.get_tx(txhash=start_hash)
            if start_tx.height is None:
                continue
            sort_key = start_tx2index(start_tx=start_tx)
            unconfirmed.append((c_address, start_tx, conclude_tx, c_storage, sort_key))

        v = get_validator_object(c_address=c.c_address, best_block=best_block,
                                 best_chain=best_chain, stop_txhash=stop_txhash)
        for c_address, start_tx, conclude_tx, c_storage, sort_key in sorted(unconfirmed, key=lambda x: x[4]):
            if len(conclude_tx.signature) < v.require:
                continue  # ignore unsatisfied ConcludeTXs
            dummy, c_method, redeem_address, c_args = decode(start_tx.message)
            c.update(db_index=sort_key, start_hash=start_tx.hash, finish_hash=conclude_tx.hash,
                     c_method=c_method, c_args=c_args, c_storage=c_storage)
Example #3
0
async def validator_info(request):
    try:
        c_address = request.query['c_address']
        f_confirmed = bool(request.query.get('confirmed', False))
        best_block = builder.best_block if f_confirmed else None
        v = get_validator_object(c_address=c_address, best_block=best_block)
        return web_base.json_res(v.info)
    except Exception as e:
        logging.error(e)
        return web_base.error_res()
Example #4
0
def create_signed_tx_as_validator(tx: TX):
    assert tx.type in (C.TX_VALIDATOR_EDIT, C.TX_CONCLUDE_CONTRACT)
    assert tx.hash in tx_builder.unconfirmed
    copied_tx = deepcopy(tx)
    # sign as another validator
    c_address, *dummy = bjson.loads(copied_tx.message)
    # validator object
    stop_txhash = copied_tx.hash if copied_tx.type == C.TX_VALIDATOR_EDIT else None
    v = get_validator_object(c_address=c_address, stop_txhash=stop_txhash)
    if setup_contract_signature(copied_tx, v.validators) == 0:
        raise BlockChainError('Cannot sign, you are not validator.')
    return copied_tx
Example #5
0
def get_validator_by_contract_info(c_address,
                                   start_tx=None,
                                   start_hash=None,
                                   best_block=None,
                                   best_chain=None,
                                   stop_txhash=None):
    c = get_contract_object(c_address=c_address,
                            best_block=best_block,
                            best_chain=best_chain,
                            stop_txhash=stop_txhash)
    if c.version > -1:
        v = get_validator_object(v_address=c.v_address,
                                 best_block=best_block,
                                 best_chain=best_chain,
                                 stop_txhash=stop_txhash)
        if v.version > -1:
            return v
        else:
            raise BlockChainError('ValidatorTX is not init. {}'.format(
                v.v_address))
    elif start_tx or start_hash:
        if start_tx is None:
            start_tx = tx_builder.get_tx(txhash=start_hash)
        raw_args = start_tx.encoded_message()
        if len(raw_args) != 4:
            raise BlockChainError('Not correct args count {}'.format(raw_args))
        c_address, c_method, redeem_address, c_args = raw_args
        if c_method != M_INIT:
            raise BlockChainError('StartTX method is not INIT')
        if len(c_args) != 4:
            raise BlockChainError('Not correct c_args count {}'.format(c_args))
        c_bin, v_address, c_extra_imports, c_settings = c_args
        return get_validator_object(v_address=v_address,
                                    best_block=best_block,
                                    best_chain=best_chain,
                                    stop_txhash=stop_txhash)
    else:
        raise BlockChainError('ContractTX is not init. {}'.format(c_address))
Example #6
0
def create_validator_edit_tx(v_address,
                             cur,
                             new_address=None,
                             flag=F_NOP,
                             sig_diff=0,
                             gas_price=None,
                             retention=10800):
    assert not (flag == F_NOP and sig_diff == 0), 'No edit info'
    if new_address is None and flag != F_NOP:
        raise BlockChainError('No cosigner edit, but flag is not NOP')
    # validator object
    v = get_validator_object(v_address=v_address)
    if v.version == -1:
        if new_address is None or flag != F_ADD or sig_diff != 1:
            raise BlockChainError('Not correct info')
    else:
        next_require = v.require + sig_diff
        next_validator_num = len(v.validators)
        if flag == F_ADD:
            next_validator_num += 1
        elif flag == F_REMOVE:
            next_validator_num -= 1
        if not (0 < next_require <= next_validator_num):
            raise BlockChainError('ReqError, 0 < {} <= {}'.format(
                next_require, next_validator_num))
    # tx create
    message = msgpack.packb((v_address, new_address, flag, sig_diff),
                            use_bin_type=True)
    tx = TX.from_dict(
        tx={
            'type': C.TX_VALIDATOR_EDIT,
            'gas_price': gas_price or V.COIN_MINIMUM_PRICE,
            'gas_amount': 0,
            'message_type': C.MSG_MSGPACK,
            'message': message
        })
    tx.gas_amount = tx.size
    tx.update_time(retention)
    # fill unspents
    additional_gas = C.VALIDATOR_EDIT_GAS + v.require * C.SIGNATURE_GAS
    input_address = fill_inputs_outputs(tx=tx,
                                        cur=cur,
                                        additional_gas=additional_gas)
    assert len(input_address & set(v.validators)) == 0, 'Not implemented?'
    # replace dummy address
    replace_redeem_dummy_address(tx=tx, cur=cur)
    setup_signature(tx, input_address)
    if v.version > -1 and setup_contract_signature(tx, v.validators) == 0:
        raise BlockChainError('Cannot sign, you are not validator')
    return tx
Example #7
0
def create_validator_edit_tx(c_address,
                             new_address=None,
                             flag=F_NOP,
                             sig_diff=0,
                             gas_price=None,
                             retention=10800):
    assert not (flag == F_NOP and sig_diff == 0), 'No edit info.'
    if new_address is None and flag != F_NOP:
        raise BlockChainError('No cosigner edit, but flag is not NOP.')
    # validator object
    v = get_validator_object(c_address=c_address)
    if v.version == -1:
        if new_address is None or flag != F_ADD or sig_diff != 1:
            raise BlockChainError('Not correct info.')
    else:
        next_require = v.require + sig_diff
        next_validator_num = len(v.validators)
        if flag == F_ADD:
            next_validator_num += 1
        elif flag == F_REMOVE:
            next_validator_num -= 1
        if not (0 < next_require <= next_validator_num):
            raise BlockChainError('ReqError, 0 < {} <= {}'.format(
                next_require, next_validator_num))
    # tx create
    message = bjson.dumps((c_address, new_address, flag, sig_diff),
                          compress=False)
    tx = TX(
        tx={
            'type': C.TX_VALIDATOR_EDIT,
            'gas_price': gas_price or V.COIN_MINIMUM_PRICE,
            'gas_amount': 0,
            'message_type': C.MSG_BYTE,
            'message': message
        })
    extra_gas = C.VALIDATOR_EDIT_GAS + C.SIGNATURE_GAS * v.require
    tx.gas_amount = tx.size + extra_gas
    tx.update_time(retention)
    # fill unspents
    fill_contract_inputs_outputs(tx=tx,
                                 c_address=c_address,
                                 additional_gas=extra_gas)
    # replace dummy address
    replace_redeem_dummy_address(tx=tx, replace_by=c_address)
    tx.serialize()
    if len(v.validators) > 0 and setup_contract_signature(tx,
                                                          v.validators) == 0:
        raise BlockChainError('Cannot sign, you are not validator.')
    return tx
Example #8
0
async def validator_info(request):
    try:
        c_address = request.query['c_address']
        f_confirmed = bool(request.query.get('confirmed', False))
        stop_hash = request.query.get('stophash', None)
        if stop_hash:
            stop_hash = a2b_hex(stop_hash)
        best_block = builder.best_block if f_confirmed else None
        v = get_validator_object(c_address=c_address,
                                 best_block=best_block,
                                 stop_txhash=stop_hash)
        return web_base.json_res(v.info)
    except Exception as e:
        logging.error(e)
        return web_base.error_res()
Example #9
0
def create_signed_tx_as_validator(tx: TX):
    tx = deepcopy(tx)
    if tx.type == C.TX_VALIDATOR_EDIT:
        v_address, new_address, flag, sig_diff = tx.encoded_message()
        v = get_validator_object(v_address=v_address, stop_txhash=tx.hash)
    elif tx.type == C.TX_CONCLUDE_CONTRACT:
        c_address, start_hash, c_storage = tx.encoded_message()
        v = get_validator_by_contract_info(c_address=c_address,
                                           start_hash=start_hash)
    else:
        raise BlockChainError('Not found tx type {}'.format(tx))
    # sign and check how many add signs
    if setup_contract_signature(tx, v.validators) == 0:
        raise BlockChainError(
            'Cannot sign, you are not validator or already signed')
    return tx
Example #10
0
def signature_check(tx, include_block):
    require_cks = set()
    checked_cks = set()
    signed_cks = set(tx.verified_list)
    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()))
        if len(input_tx.outputs) <= txindex:
            raise BlockChainError('txindex is over range {}<={}'.format(
                len(input_tx.outputs), txindex))
        address, coin_id, amount = input_tx.outputs[txindex]
        if address in checked_cks:
            continue
        elif is_address(ck=address, hrp=V.BECH32_HRP, ver=C.ADDR_NORMAL_VER):
            require_cks.add(address)
        elif is_address(ck=address, hrp=V.BECH32_HRP,
                        ver=C.ADDR_VALIDATOR_VER):
            v_before = get_validator_object(v_address=address,
                                            best_block=include_block,
                                            stop_txhash=tx.hash)
            if v_before.version == -1:
                raise BlockChainError('Not init validator {}'.format(address))
            if len(signed_cks & v_before.validators) < v_before.require:
                raise BlockChainError(
                    'Don\'t satisfy required signature {}<{}'.format(
                        len(signed_cks & v_before.validators),
                        v_before.require))
            require_cks.update(v_before.validators)
        elif is_address(ck=address, hrp=V.BECH32_HRP, ver=C.ADDR_CONTRACT_VER):
            raise BlockChainError(
                'Not allow ContractAddress include in normal Transfer. {}'.
                format(address, tx))
        else:
            raise BlockChainError('Not common address {} {}'.format(
                address, tx))
        # success check
        checked_cks.add(address)

    if not (0 < len(require_cks) < 256):
        raise BlockChainError('require signature is over range num={}'.format(
            len(require_cks)))
    if require_cks != signed_cks:
        raise BlockChainError('Signature verification failed. [{}={}]'.format(
            require_cks, signed_cks))
Example #11
0
def create_conclude_tx(c_address,
                       start_tx,
                       redeem_address,
                       send_pairs=None,
                       c_storage=None,
                       emulate_gas=0):
    assert isinstance(start_tx, TX)
    assert send_pairs is None or isinstance(send_pairs, list)
    assert c_storage is None or isinstance(c_storage, dict)
    assert isinstance(emulate_gas, int)
    message = bjson.dumps((c_address, start_tx.hash, c_storage),
                          compress=False)
    v = get_validator_object(c_address=c_address)
    send_pairs = send_pairs or list()
    tx = TX(
        tx={
            'type': C.TX_CONCLUDE_CONTRACT,
            'time': start_tx.time,
            'deadline': start_tx.deadline,
            'gas_price': start_tx.gas_price,
            'gas_amount': 0,
            'outputs': [tuple(s) for s in send_pairs],
            'message_type': C.MSG_BYTE,
            'message': message
        })
    extra_gas = C.SIGNATURE_GAS * v.require
    tx.gas_amount = tx.size + extra_gas
    # fill unspents
    fill_contract_inputs_outputs(tx=tx,
                                 c_address=c_address,
                                 additional_gas=extra_gas)
    # replace dummy address
    replace_redeem_dummy_address(tx=tx, replace_by=c_address)
    # fix redeem fees
    if send_pairs:
        # conclude_txで使用したGasを、ユーザーから引いてコントラクトに戻す処理
        conclude_fee = (emulate_gas + tx.gas_amount) * tx.gas_price
        fee_coin_id = 0
        f_finish_add = f_finish_sub = False
        for index, (address, coin_id, amount) in enumerate(tx.outputs):
            if coin_id != fee_coin_id:
                continue
            elif not f_finish_add and address == c_address:
                f_finish_add = True
                tx.outputs[index] = (address, coin_id, amount + conclude_fee)
            elif not f_finish_sub and address == redeem_address:
                f_finish_sub = True
                tx.outputs[index] = (address, coin_id, amount - conclude_fee)
            else:
                pass
        if not (f_finish_add and f_finish_sub):
            raise BlockChainError(
                'Cannot move conclude fee, add={} sub={}'.format(
                    f_finish_add, f_finish_sub))
        logging.debug("Move conclude fee {}:{}".format(fee_coin_id,
                                                       conclude_fee))
    tx.serialize()
    if v.version == -1:
        raise BlockChainError(
            'Not init validator address. {}'.format(c_address))
    if setup_contract_signature(tx, v.validators) == 0:
        raise BlockChainError('Cannot sign, you are not validator.')
    return tx
Example #12
0
def _update_unconfirmed_info():
    with unconfirmed_lock:
        s = time()
        # sort unconfirmed txs
        unconfirmed_txs = sorted(tx_builder.unconfirmed.values(),
                                 key=lambda x: x.time)
        unconfirmed_txs = sorted(unconfirmed_txs,
                                 key=lambda x: x.gas_price,
                                 reverse=True)

        # reject tx (input tx is unconfirmed)
        limit_height = builder.best_block.height - C.MATURE_HEIGHT
        best_block, best_chain = builder.get_best_chain()
        used_pairs = set()
        for tx in unconfirmed_txs.copy():
            if tx.height is not None:
                if tx.hash in tx_builder.unconfirmed:
                    del tx_builder.unconfirmed[tx.hash]
                unconfirmed_txs.remove(tx)
                continue
            if Debug.F_STICKY_TX_REJECTION and tx.hash in sticky_failed_txhash:
                unconfirmed_txs.remove(tx)
                continue
            # inputs check
            for txhash, txindex in tx.inputs:
                input_tx = tx_builder.get_tx(txhash)
                if input_tx is None:
                    unconfirmed_txs.remove(tx)
                    break
                elif input_tx.height is None:
                    unconfirmed_txs.remove(tx)
                    break
                elif input_tx.type in (C.TX_POS_REWARD, C.TX_POW_REWARD) and \
                        input_tx.height > limit_height:
                    unconfirmed_txs.remove(tx)
                    break
                elif is_usedindex(txhash, txindex, tx.hash, best_block,
                                  best_chain):
                    unconfirmed_txs.remove(tx)
                    break
                # check inputs used same unconfirmed_txs
                input_pair = (txhash, txindex)
                if input_pair in used_pairs:
                    unconfirmed_txs.remove(tx)
                    break
                used_pairs.add(input_pair)

        # contract tx
        for tx in unconfirmed_txs.copy():
            if tx.type == C.TX_CONCLUDE_CONTRACT:
                try:
                    c_address, start_hash, c_storage = bjson.loads(tx.message)
                except Exception:
                    unconfirmed_txs.remove(tx)  # failed decode bjson
                    continue
                start_tx = tx_builder.get_tx(txhash=start_hash)
                if start_tx is None or start_tx.height is None:
                    unconfirmed_txs.remove(tx)  # start tx is confirmed
                    continue
                v = get_validator_object(c_address=c_address,
                                         best_block=best_block,
                                         best_chain=best_chain)
                signed_cks = get_signed_cks(tx)
                accept_cks = signed_cks & set(v.validators)
                if v.require > len(accept_cks):
                    unconfirmed_txs.remove(tx)
                    continue
            elif tx.type == C.TX_VALIDATOR_EDIT:
                try:
                    c_address, address, flag, sig_diff = bjson.loads(
                        tx.message)
                except Exception:
                    unconfirmed_txs.remove(tx)  # failed decode bjson
                    continue
                v = get_validator_object(c_address=c_address,
                                         best_block=best_block,
                                         best_chain=best_chain)
                signed_cks = get_signed_cks(tx)
                accept_cks = signed_cks & set(v.validators)
                if v.require > len(accept_cks):
                    unconfirmed_txs.remove(tx)
                    continue
            else:
                pass

        # limit per tx's in block
        if Debug.F_LIMIT_INCLUDE_TX_IN_BLOCK:
            unconfirmed_txs = unconfirmed_txs[:Debug.
                                              F_LIMIT_INCLUDE_TX_IN_BLOCK]

        update_unconfirmed_txs(unconfirmed_txs)
        logging.debug("Update unconfirmed={}/{} {}Sec".format(
            len(unconfirmed_txs), len(tx_builder.unconfirmed),
            round(time() - s, 3)))