Exemple #1
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))
Exemple #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()
Exemple #3
0
 def __init__(self, v_address):
     assert is_address(ck=v_address,
                       hrp=V.BECH32_HRP,
                       ver=C.ADDR_VALIDATOR_VER)
     self.v_address = v_address
     self.validators = list()
     self.require = 0
     self.db_index = None
     self.version = -1
     self.txhash = None
Exemple #4
0
async def validateaddress(*args, **kwargs):
    """
    Arguments:
        1. "address"     (string, required) The bitcoin address to validate

    Result:
        {
          "isvalid" : true|false,       (boolean) If the address is valid or not. If not, this is the only property returned.
          "address" : "address",        (string) The bitcoin address validated
          "ismine" : true|false,        (boolean) If the address is yours or not
          "pubkey" : "publickeyhex",    (string, optional) The hex value of the raw public key, for single-key addresses (possibly embedded in P2SH or P2WSH)
          "account" : "account"         (string) DEPRECATED. The account associated with the address, "" is the default account
          "hdkeypath" : "keypath"       (string, optional) The HD keypath if the key is HD and available
        }
    """
    if len(args) == 0:
        raise ValueError('no argument found')
    address = args[0]
    with create_db(V.DB_ACCOUNT_PATH) as db:
        cur = db.cursor()
        user_id = read_address2userid(address=address, cur=cur)
        if user_id is None:
            return {
                "isvalid": is_address(address, V.BECH32_HRP, 0),
                "address": address,
                "ismine": False,
                "pubkey": None,
                "account": None,
                "hdkeypath": None,
            }
        else:
            account = read_userid2name(user=user_id, cur=cur)
            account = "" if account == C.ANT_UNKNOWN else account
            _, keypair, path = read_address2keypair(address=address, cur=cur)
            return {
                "isvalid": is_address(address, V.BECH32_HRP, 0),
                "address": address,
                "ismine": True,
                "pubkey": keypair.get_public_key().hex(),
                "account": account,
                "hdkeypath": path,
            }
Exemple #5
0
 def __init__(self, c_address):
     assert is_address(ck=c_address,
                       hrp=V.BECH32_HRP,
                       ver=C.ADDR_CONTRACT_VER)
     self.c_address = c_address
     self.v_address = None
     self.version = -1
     self.db_index = None
     self.binary = None
     self.extra_imports = None
     self.storage = None
     self.settings = None
     self.start_hash = None
     self.finish_hash = None
Exemple #6
0
def update_unspents_txs(time_limit=0.2):
    global unspents_txs
    c = 100
    while previous_block is None:
        if c < 0:
            raise Exception('Timeout on update unspents')
        sleep(0.1)
        c -= 1
    s = time()
    previous_height = previous_block.height
    proof_txs = list()
    all_num = 0
    with create_db(V.DB_ACCOUNT_PATH) as db:
        cur = db.cursor()
        for address, height, txhash, txindex, coin_id, amount in get_my_unspents_iter(
                cur):
            if time() - s > time_limit:
                break
            if height is None:
                continue
            if coin_id != 0:
                continue
            if not (previous_height + 1 > height + C.MATURE_HEIGHT):
                continue
            if not is_address(
                    ck=address, hrp=V.BECH32_HRP, ver=C.ADDR_NORMAL_VER):
                continue
            if amount < 100000000:
                continue
            if staking_limit < all_num:
                log.debug("Unspents limit reached, skip by {} limits".format(
                    staking_limit))
                break
            all_num += 1
            proof_tx = TX.from_dict(
                tx={
                    'type': C.TX_POS_REWARD,
                    'inputs': [(txhash, txindex)],
                    'outputs': [(address, 0, 0)],
                    'gas_price': 0,
                    'gas_amount': 0,
                    'message_type': C.MSG_NONE,
                    'message': b''
                })
            proof_tx.height = previous_height + 1
            proof_tx.pos_amount = amount
            proof_txs.append(proof_tx)
    unspents_txs = proof_txs
    return all_num, len(proof_txs)
Exemple #7
0
def objective_tx_signature_check(target_address, extra_tx: TX, v: Validator,
                                 include_block: Block):
    necessary_cks = set(v.validators)
    necessary_num = v.require
    checked_cks = set()
    for txhash, txindex in extra_tx.inputs:
        input_tx = tx_builder.get_tx(txhash)
        if input_tx is None:
            raise BlockChainError('1 Not found input {}:{}'.format(
                txhash.hex(), txindex))
        if len(input_tx.outputs) < txindex:
            raise BlockChainError('2 Not found input {}:{}'.format(
                txhash.hex(), txindex))
        address, coin_id, amount = input_tx.outputs[txindex]
        if address in checked_cks:
            continue
        elif address == target_address:
            continue
        elif is_address(ck=address, hrp=V.BECH32_HRP, ver=C.ADDR_NORMAL_VER):
            if address not in necessary_cks:
                necessary_num += 1
                necessary_cks.add(address)
        elif is_address(ck=address, hrp=V.BECH32_HRP,
                        ver=C.ADDR_VALIDATOR_VER):
            raise BlockChainError('Not allowed {}'.format(address))
        elif is_address(ck=address, hrp=V.BECH32_HRP, ver=C.ADDR_CONTRACT_VER):
            raise BlockChainError('Not allowed {}'.format(address))
        else:
            raise BlockChainError(
                'Not found address format {}'.format(address))
        checked_cks.add(address)

    signed_cks = set(extra_tx.verified_list)
    accept_cks = signed_cks & necessary_cks
    reject_cks = signed_cks - necessary_cks
    if len(reject_cks) > 0:
        raise BlockChainError(
            'Unrelated signature include, reject={}'.format(reject_cks))
    elif include_block:
        # check satisfy require?
        if len(accept_cks) < necessary_num:
            raise BlockChainError(
                'Not satisfied require signature. [signed={}, accepted={}, require={}]'
                .format(signed_cks, accept_cks, necessary_num))
    else:
        # check can marge?
        original_tx = tx_builder.get_tx(txhash=extra_tx.hash)
        if original_tx is None:
            # accept the tx first
            if 0 < necessary_num and len(accept_cks) == 0:
                raise BlockChainError(
                    'No acceptable signature. signed={}'.format(signed_cks))
            if len(accept_cks) > necessary_num:
                # accept signature more than required
                log.debug('Too many signatures, accept={} req={}'.format(
                    accept_cks, necessary_num))
        else:
            # need to marge signature
            if original_tx.height is not None:
                raise BlockChainError('Already included tx. height={}'.format(
                    original_tx.height))
            if necessary_num == 0:
                raise BlockChainError('Don\t need to marge signature')
            original_cks = set(original_tx.verified_list)
            accept_new_cks = (signed_cks - original_cks) & necessary_cks
            if len(accept_new_cks) == 0:
                raise BlockChainError(
                    'No new acceptable cks. ({} - {}) & {}'.format(
                        signed_cks, original_cks, necessary_cks))
            if len(accept_new_cks) + len(original_cks) > necessary_num:
                # accept signature more than required
                log.debug(
                    'Too many signatures, new={} original={} req={}'.format(
                        accept_new_cks, original_cks, necessary_num))
Exemple #8
0
def check_tx_validator_edit(tx: TX, include_block: Block):
    # common check
    if not (len(tx.inputs) > 0 and len(tx.inputs) > 0):
        raise BlockChainError('No inputs or outputs')
    elif tx.message_type != C.MSG_MSGPACK:
        raise BlockChainError('validator_edit_tx is MSG_MSGPACK')
    # message
    try:
        v_address, new_address, flag, sig_diff = tx.encoded_message()
    except Exception as e:
        raise BlockChainError('BjsonError: {}'.format(e))
    # check new_address
    v_before = get_validator_object(v_address=v_address,
                                    best_block=include_block,
                                    stop_txhash=tx.hash)
    if new_address:
        if not is_address(
                ck=new_address, hrp=V.BECH32_HRP, ver=C.ADDR_NORMAL_VER):
            raise BlockChainError('new_address is not normal')
        elif flag == F_NOP:
            raise BlockChainError('input new_address, but NOP')
    if v_before.version == -1:
        # create validator for the first time
        if new_address is None:
            raise BlockChainError('Not setup new_address')
        elif flag != F_ADD:
            raise BlockChainError('Need to add new_address flag')
        elif sig_diff != 1:
            raise BlockChainError('sig_diff is 1')
    else:
        # edit already created validator
        next_validator_num = len(v_before.validators)  # Note: Add/Remove after
        next_require_num = v_before.require + sig_diff
        if flag == F_ADD:
            if new_address is None:
                raise BlockChainError('Not setup new_address')
            elif new_address in v_before.validators:
                raise BlockChainError('Already included new_address')
            next_validator_num += 1
        elif flag == F_REMOVE:
            if new_address is None:
                raise BlockChainError('Not setup new_address')
            elif new_address not in v_before.validators:
                raise BlockChainError('Not include new_address')
            elif len(v_before.validators) < 2:
                raise BlockChainError('validator is now only {}'.format(
                    len(v_before.validators)))
            next_validator_num -= 1
        elif flag == F_NOP:
            if new_address is not None:
                raise BlockChainError('Input new_address?')
        else:
            raise BlockChainError('unknown flag {}'.format(flag))
        # sig_diff check
        if not (0 < next_require_num <= next_validator_num):
            raise BlockChainError('sig_diff check failed, 0 < {} <= {}'.format(
                next_require_num, next_validator_num))
    required_gas_check(tx=tx, v=v_before, extra_gas=C.VALIDATOR_EDIT_GAS)
    objective_tx_signature_check(target_address=v_address,
                                 extra_tx=tx,
                                 v=v_before,
                                 include_block=include_block)
Exemple #9
0
def check_tx_contract_conclude(tx: TX, include_block: Block):
    # common check
    if not (len(tx.inputs) > 0 and len(tx.inputs) > 0):
        raise BlockChainError('No inputs or outputs')
    elif tx.message_type != C.MSG_MSGPACK:
        raise BlockChainError('validator_edit_tx is MSG_MSGPACK')
    try:
        c_address, start_hash, c_storage = tx.encoded_message()
    except Exception as e:
        raise BlockChainError('EncodeMessageError: {}'.format(e))
    if not isinstance(c_address, str):
        raise BlockChainError('1. Not correct format. {}'.format(c_address))
    if not is_address(ck=c_address, hrp=V.BECH32_HRP, ver=C.ADDR_CONTRACT_VER):
        raise BlockChainError(
            '2 ValidatorAddress format is not correct {}'.format(c_address))
    if not (isinstance(start_hash, bytes) and len(start_hash) == 32):
        raise BlockChainError('2. Not correct format. {}'.format(start_hash))
    if not (c_storage is None or isinstance(c_storage, dict)):
        raise BlockChainError('3. Not correct format. {}'.format(c_storage))
    # check already created conclude tx
    finish_hash = get_conclude_hash_from_start(c_address=c_address,
                                               start_hash=start_hash,
                                               best_block=include_block)
    if finish_hash and finish_hash != tx.hash:
        raise BlockChainError('Already start_hash used. {}'.format(
            finish_hash.hex()))
    # check start tx
    start_tx = tx_builder.get_tx(txhash=start_hash)
    if start_tx is None:
        raise BlockChainError('Not found start tx. {}'.format(
            start_hash.hex()))
    if start_tx.height is None:
        raise BlockChainError('Start tx is unconfirmed. {}'.format(start_tx))
    if start_tx.type != C.TX_TRANSFER:
        raise BlockChainError('Start tx is TRANSFER, not {}'.format(
            C.txtype2name.get(start_tx.type, None)))
    if start_tx.message_type != C.MSG_MSGPACK:
        raise BlockChainError('Start tx is MSG_MSGPACK, not {}'.format(
            C.msg_type2name.get(start_tx.message_type, None)))
    if start_tx.time != tx.time or start_tx.deadline != tx.deadline:
        raise BlockChainError(
            'time of conclude_tx and start_tx is same, {}!={}'.format(
                start_tx.time, tx.time))
    try:
        c_start_address, c_method, redeem_address, c_args = start_tx.encoded_message(
        )
    except Exception as e:
        raise BlockChainError('BjsonError: {}'.format(e))
    if c_address != c_start_address:
        raise BlockChainError(
            'Start tx\'s contract address is different. {}!={}'.format(
                c_address, c_start_address))
    if not isinstance(c_method, str):
        raise BlockChainError('c_method is string. {}'.format(c_method))
    if not (isinstance(c_args, tuple) or isinstance(c_args, list)):
        raise BlockChainError('4. Not correct format. {}'.format(c_args))
    # contract check
    c_before = get_contract_object(c_address=c_address,
                                   best_block=include_block,
                                   stop_txhash=tx.hash)
    # contract index check
    if c_before.version != -1:
        new_index = start_tx2index(start_tx=start_tx)
        if not (c_before.db_index < new_index):
            raise BlockChainError('The index is old on execute order, '
                                  'before={} new={}'.format(
                                      c_before.db_index, new_index))
        # check:  Do not skip old contract?
        if include_block:
            c_my_before = get_contract_object(c_address=c_address,
                                              best_block=None,
                                              stop_txhash=tx.hash)
            if c_my_before.version != c_before.version or c_my_before.db_index != c_before.db_index:
                raise BlockChainError(
                    'Block skip old ConcludeTX, idx={} my={} block={}'.format(
                        new_index, c_my_before, c_before))
    else:
        pass  # init ConcludeTX, no action
    # c_method check, init, update and others..
    if c_method == M_INIT:
        if len(c_args) != 4:
            raise BlockChainError('c_args is 4 items')
        if c_before.version != -1:
            raise BlockChainError('Already created contract. {}'.format(
                c_before.version))
        c_bin, v_address, c_extra_imports, c_settings = c_args
        if not isinstance(c_bin, bytes):
            raise BlockChainError('5. Not correct format. {}'.format(c_args))
        if not isinstance(v_address, str):
            raise BlockChainError(
                '1 ValidatorAddress format is not correct {}'.format(
                    v_address))
        if not is_address(
                ck=v_address, hrp=V.BECH32_HRP, ver=C.ADDR_VALIDATOR_VER):
            raise BlockChainError(
                '2 ValidatorAddress format is not correct {}'.format(
                    v_address))
        if not (c_extra_imports is None or isinstance(c_extra_imports, tuple)
                or isinstance(c_extra_imports, list)):
            raise BlockChainError(
                '6. Not correct format. {}'.format(c_extra_imports))
        if not (c_settings is None or isinstance(c_settings, dict)):
            raise BlockChainError(
                '7. Not correct format. {}'.format(c_settings))
    elif c_method == M_UPDATE:
        if len(c_args) != 3:
            raise BlockChainError('c_args is 3 items')
        if c_before.version == -1:
            raise BlockChainError('Not created contract')
        c_bin, c_extra_imports, c_settings = c_args
        v_address = c_before.v_address
        if not (c_bin is None or isinstance(c_bin, bytes)):
            raise BlockChainError('8. Not correct format. {}'.format(c_args))
        if not (c_extra_imports is None or isinstance(c_extra_imports, tuple)):
            raise BlockChainError(
                '9. Not correct format. {}'.format(c_extra_imports))
        if not (c_settings is None or isinstance(c_settings, dict)):
            raise BlockChainError(
                '10. Not correct format. {}'.format(c_settings))
        if not (c_bin or c_extra_imports or c_settings):
            raise BlockChainError('No change found. {}, {}, {}'.format(
                c_bin, c_extra_imports, c_settings))
    else:
        v_address = c_before.v_address  # user oriented data
    # validator check
    v = get_validator_object(v_address=v_address,
                             best_block=include_block,
                             stop_txhash=tx.hash)
    if v.require == 0:
        raise BlockChainError('At least 1 validator required. {}'.format(
            v.require))
    required_gas_check(tx=tx, v=v, extra_gas=0)
    objective_tx_signature_check(target_address=c_address,
                                 extra_tx=tx,
                                 v=v,
                                 include_block=include_block)