Beispiel #1
0
async def getwork(*args, **kwargs):
    """
    https://en.bitcoin.it/wiki/Getwork
    Result:
        1. "data"       (hex string, required) block data
        2. "target"     (hex string, required) little endian hash target
    """
    if len(args) == 0:
        now = int(time() - V.BLOCK_GENESIS_TIME)
        for block in getwork_cashe.values():
            if block.previous_hash != chain_builder.best_block.hash:
                continue
            if now - block.time < 10:
                mining_block = block
                break
        else:
            mining_block = await get_mining_block(int(kwargs['password']))
            getwork_cashe[mining_block.merkleroot] = mining_block
            mining_block.bits2target()
        # Pre-processed SHA-2 input chunks
        data = mining_block.b  # 80 bytes
        data += a2b_hex('800000000000000000000000000000000000000000000000'
                        '000000000000000000000000000000000000000000000280')  # 48+80=128bytes
        new_data = b''
        for i in range(0, 128, 4):
            new_data += data[i:i + 4][::-1]
        if extra_target:
            return {"data": new_data.hex(), "target": extra_target.to_bytes(32, 'big').hex()}
        else:
            return {"data": new_data.hex(), "target": mining_block.target_hash.hex()}
    else:
        data = a2b_hex(args[0])
        new_data = b''
        for i in range(0, 128, 4):
            new_data += data[i:i + 4][::-1]
        block = Block.from_binary(binary=new_data[:80])
        if block.previous_hash != chain_builder.best_block.hash:
            return 'PreviousHash don\'t match'
        if block.merkleroot in getwork_cashe:
            block.txs.extend(getwork_cashe[block.merkleroot].txs)
            result = await submitblock(block, **kwargs)
            if result is None:
                return True
            elif extra_target and block.pow_check(extra_target=extra_target):
                return True
            else:
                log.debug("GetWorkReject by \"{}\"".format(result))
                return result
        else:
            log.debug("GetWorkReject by \"Not found merkleroot.\"")
            return 'Not found merkleroot'
Beispiel #2
0
def fill_newblock_info(data):
    new_block: Block = Block.from_binary(binary=data['binary'])
    log.debug("fill newblock height={} newblock={}".format(
        data.get('height'), new_block.hash.hex()))
    proof: TX = data['proof']
    new_block.txs.append(proof)
    new_block.flag = data['block_flag']
    my_block = chain_builder.get_block(new_block.hash)
    if my_block:
        raise BlockChainError('Already inserted block {}'.format(my_block))
    before_block = chain_builder.get_block(new_block.previous_hash)
    if before_block is None:
        log.debug("Cannot find beforeBlock, try to ask outside node")
        # not found beforeBlock, need to check other node have the the block
        new_block.inner_score *= 0.70  # unknown previousBlock, score down
        before_block = make_block_by_node(blockhash=new_block.previous_hash,
                                          depth=0)
    new_height = before_block.height + 1
    proof.height = new_height
    new_block.height = new_height
    # work check
    # TODO: correct position?
    if not new_block.pow_check():
        raise BlockChainError('Proof of work is not satisfied')
    # Append general txs
    for txhash in data['txs'][1:]:
        tx = tx_builder.get_tx(txhash)
        if tx is None:
            new_block.inner_score *= 0.75  # unknown tx, score down
            log.debug("Unknown tx, try to download")
            r = ask_node(cmd=DirectCmd.TX_BY_HASH,
                         data={'txhash': txhash},
                         f_continue_asking=True)
            if isinstance(r, str):
                raise BlockChainError(
                    'Failed unknown tx download "{}"'.format(r))
            tx: TX = r
            tx.height = None
            check_tx(tx, include_block=None)
            tx_builder.put_unconfirmed(tx)
            log.debug("Success unknown tx download {}".format(tx))
        tx.height = new_height
        new_block.txs.append(tx)
    return new_block
Beispiel #3
0
def load_boot_file(url=None):
    normal_path = os.path.join(os.path.abspath(os.path.dirname(__file__)),
                               'boot.json')
    extra_path = os.path.join(V.DB_HOME_DIR, 'boot.json')
    if url:
        data = requests.get(url=url).json()
    elif os.path.exists(normal_path):
        with open(normal_path, mode='r') as fp:
            data = json.load(fp)
    elif os.path.exists(extra_path):
        with open(extra_path, mode='r') as fp:
            data = json.load(fp)
    else:
        raise FileNotFoundError('Cannot find boot.json "{}" or "{}" ?'.format(
            normal_path, extra_path))
    # load from exist boot.json
    genesis_block = Block.from_binary(binary=a2b_hex(data['genesis_binary']))
    assert genesis_block.hash == a2b_hex(data['genesis_hash'])
    genesis_block.flag = C.BLOCK_GENESIS
    genesis_block.height = 0
    for tx_dct in data['txs']:
        tx = TX.from_binary(binary=a2b_hex(tx_dct['binary']))
        assert tx.hash == a2b_hex(tx_dct['hash'])
        tx.height = 0
        genesis_block.txs.append(tx)
    connections = data['connections']
    network_ver = data['network_ver']
    if isinstance(data['params'], dict):
        # new type boot.json
        params = data['params']
        params['consensus'] = {
            int(k): v
            for k, v in params['consensus'].items()
        }
    elif isinstance(data['params'], str):
        # old type boot.json
        params = original_mpk.unpackb(a2b_hex(data['params']),
                                      raw=True,
                                      encoding='utf8')
    else:
        raise Exception('Unknown type params')
    return genesis_block, params, network_ver, connections
Beispiel #4
0
def object_hook(dct):
    if isinstance(dct, dict) and '_bc4py_class_' in dct:
        if dct['_bc4py_class_'] == 'Block':
            block = Block.from_binary(binary=dct['binary'])
            block.height = dct['height']
            block.flag = dct['flag']
            block.txs.extend(object_hook(tx) for tx in dct['txs'])
            for tx in block.txs:
                tx.height = block.height
            return block
        elif dct['_bc4py_class_'] == 'TX':
            tx = TX.from_binary(binary=dct['binary'])
            tx.height = dct['height']
            tx.signature.extend(tuple(sig) for sig in dct['signature'])
            tx.R = dct['R']
            return tx
        else:
            raise Exception('Not found class name "{}"'.format(
                dct['_bc4py_class_']))
    else:
        return dct
Beispiel #5
0
async def submitblock(*args, **kwargs):
    """
    Attempts to submit new block to network.
    See https://en.bitcoin.it/wiki/BIP_0022 for full specification.

    Arguments
        1. "hexdata"        (string, required) the hex-encoded block data to submit
        2. "dummy"          (optional) dummy value, for compatibility with BIP22. This value is ignored.

    Result:
        null if success
        string if failed
    """
    if len(args) == 0:
        raise ValueError('no argument found')
    block_hex_or_obj = args[0]
    if isinstance(block_hex_or_obj, str):
        block_bin = a2b_hex(block_hex_or_obj)
        # Block
        mined_block = Block.from_binary(binary=block_bin[:80])
        if mined_block.previous_hash != chain_builder.best_block.hash:
            return 'PreviousHash don\'t match'
        previous_block = chain_builder.get_block(mined_block.previous_hash)
        mined_block.height = previous_block.height + 1
        mined_block.flag = int(kwargs['password'])
        # tx length
        storage_flag = int.from_bytes(block_bin[80:81], 'little')
        if storage_flag < 0xfd:
            tx_len = storage_flag
            pos = 81
        elif storage_flag == 0xfd:
            tx_len = int.from_bytes(block_bin[81:83], 'little')
            pos = 83
        elif storage_flag == 0xfe:
            tx_len = int.from_bytes(block_bin[81:85], 'little')
            pos = 85
        else:  # == 0xff
            tx_len = int.from_bytes(block_bin[81:89], 'little')
            pos = 89
        log.debug("RpcSubmit block: pos={}, tx_len={}".format(pos, tx_len))
        # correct txs
        while len(block_bin) > pos:
            tx = TX()
            tx.b = block_bin
            tx.deserialize(first_pos=pos, f_raise=False)
            if tx.version != __chain_version__:
                return 'tx_ver do not match [{}!={}]'.format(tx.version, __chain_version__)
            pos += len(tx.b)
            mined_block.txs.append(tx_builder.get_tx(txhash=tx.hash, default=tx))
        # check format
        if tx_len != len(mined_block.txs):
            return 'Do not match txlen [{}!={}]'.format(tx_len, len(mined_block.txs))
        if pos != len(block_bin):
            return 'Do not match pos [{}!={}]'.format(pos, len(block_bin))
    elif isinstance(block_hex_or_obj, Block):
        mined_block = block_hex_or_obj
        previous_block = chain_builder.get_block(mined_block.previous_hash)
        mined_block.height = previous_block.height + 1
        mined_block.flag = int(kwargs['password'])
    else:
        return 'Unknown input? -> {}'.format(block_hex_or_obj)
    mined_block.update_pow()
    if mined_block.pow_check():
        confirmed_generating_block(mined_block)
        return None  # accepted
    else:
        return 'not satisfied work'