예제 #1
0
파일: jsonrpc.py 프로젝트: yoosofan/bc4py
async def submitblock(block_hex_or_obj, **kwargs):
    if isinstance(block_hex_or_obj, str):
        block_bin = unhexlify(block_hex_or_obj.encode())
        # Block
        mined_block = Block(binary=block_bin[:80])
        if mined_block.previous_hash != builder.best_block.hash:
            return 'PreviousHash don\'t match.'
        previous_block = 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
        if F_HEAVY_DEBUG:
            logging.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
            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 = 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.'
예제 #2
0
def get_bias_by_hash(previous_hash, consensus):
    N = 30  # target blocks

    if consensus == V.BLOCK_BASE_CONSENSUS:
        return 1.0
    elif consensus == C.BLOCK_GENESIS:
        return 1.0
    elif (consensus, previous_hash) in cashe:
        return cashe[(consensus, previous_hash)]
    elif previous_hash == GENESIS_PREVIOUS_HASH:
        return 1.0

    base_diffs = list()
    target_diffs = list()
    target_hash = previous_hash
    while True:
        target_block = builder.get_block(target_hash)
        if target_block is None:
            return 1.0
        target_hash = target_block.previous_hash
        if target_hash == GENESIS_PREVIOUS_HASH:
            return 1.0
        elif target_block.flag == V.BLOCK_BASE_CONSENSUS and N > len(base_diffs):
            base_diffs.append(bits2target(target_block.bits) * (N-len(base_diffs)))
        elif target_block.flag == consensus and N > len(target_diffs):
            target_diffs.append(bits2target(target_block.bits) * (N-len(target_diffs)))
        if len(base_diffs) >= N and len(target_diffs) >= N:
            break

    bias = sum(base_diffs) / sum(target_diffs)
    cashe[(consensus, previous_hash)] = bias
    if Debug.F_SHOW_DIFFICULTY:
        print("bias", bias, hexlify(previous_hash).decode())
    return bias
예제 #3
0
파일: contract.py 프로젝트: kmn/bc4py
def start_tx2index(start_hash=None, start_tx=None):
    if start_hash:
        start_tx = tx_builder.get_tx(txhash=start_hash)
    block = builder.get_block(blockhash=builder.get_block_hash(height=start_tx.height))
    if block is None:
        raise BlockChainError('Not found block of start_tx included? {}'.format(start_tx))
    if start_tx not in block.txs:
        raise BlockChainError('Not found start_tx in block? {}'.format(block))
    return start_tx.height * 0xffffffff + block.txs.index(start_tx)
예제 #4
0
파일: broadcast.py 프로젝트: kmn/bc4py
def fill_newblock_info(data):
    new_block = Block(binary=data['block'])
    logging.debug("Fill newblock={}".format(hexlify(new_block.hash).decode()))
    proof = TX(binary=data['proof'])
    new_block.txs.append(proof)
    new_block.flag = data['block_flag']
    proof.signature = data['sign']
    # Check the block is correct info
    if not new_block.pow_check():
        raise BlockChainError('Proof of work is not satisfied.')
    my_block = builder.get_block(new_block.hash)
    if my_block:
        raise BlockChainError('Already inserted block {}'.format(my_block))
    before_block = builder.get_block(new_block.previous_hash)
    if before_block is None:
        logging.debug("Cannot find beforeBlock {}, try to ask outside node."
                      .format(hexlify(new_block.previous_hash).decode()))
        # 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)
        if not new_insert_block(before_block, time_check=True):
            # require time_check, it was generated only a few seconds ago
            # print([block for block in builder.chain.values()])
            raise BlockChainError('Failed insert beforeBlock {}'.format(before_block))
    new_height = before_block.height + 1
    proof.height = new_height
    new_block.height = new_height
    # 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
            logging.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(binary=r['tx'])
            tx.signature = r['sign']
            check_tx(tx, include_block=None)
            tx_builder.put_unconfirmed(tx)
            logging.debug("Success unknown tx download {}".format(tx))
        tx.height = new_height
        new_block.txs.append(tx)
    return new_block
예제 #5
0
async def get_block_by_height(request):
    height = int(request.query.get('height', 0))
    blockhash = builder.get_block_hash(height)
    if blockhash is None:
        return web.Response(text="Not found height.", status=400)
    block = builder.get_block(blockhash)
    data = block.getinfo()
    data['size'] = block.getsize()
    data['hex'] = hexlify(block.b).decode()
    return web_base.json_res(data)
예제 #6
0
def get_bits_by_hash(previous_hash, consensus):
    if Debug.F_CONSTANT_DIFF:
        return MAX_BITS, MAX_TARGET
    elif previous_hash == GENESIS_PREVIOUS_HASH:
        return MAX_BITS, MAX_TARGET
    elif (previous_hash, consensus) in cashe:
        return cashe[(previous_hash, consensus)]

    # Get best block time
    block_time = round(V.BLOCK_TIME_SPAN / V.BLOCK_CONSENSUS[consensus] * 100)
    # Get N, K params
    N, K = params(block_time)

    # Loop through N most recent blocks.  "< height", not "<=".
    # height-1 = most recently solved rblock
    target_hash = previous_hash
    timestamp = list()
    target = list()
    j = 0
    while True:
        target_block = builder.get_block(target_hash)
        if target_block is None:
            return MAX_BITS, MAX_TARGET
        if target_block.flag != consensus:
            target_hash = target_block.previous_hash
            continue
        if j == N + 1:
            break
        j += 1
        timestamp.insert(0, target_block.time)
        target.insert(0, bits2target(target_block.bits))
        target_hash = target_block.previous_hash
        if target_hash == GENESIS_PREVIOUS_HASH:
            return MAX_BITS, MAX_TARGET

    sum_target = t = j = 0
    for i in range(N):
        solve_time = timestamp[i + 1] - timestamp[i]
        j += 1
        t += solve_time * j
        sum_target += target[i + 1]

    # Keep t reasonable in case strange solvetimes occurred.
    if t < N * K // 3:
        t = N * K // 3

    new_target = t * sum_target // K // N // N

    # convert new target to bits
    new_bits = target2bits(new_target)
    if Debug.F_SHOW_DIFFICULTY:
        print("ratio", C.consensus2name[consensus], new_bits,
              hexlify(previous_hash).decode())
    cashe[(previous_hash, consensus)] = (new_bits, new_target)
    return new_bits, new_target
예제 #7
0
def _big_blocks(height):
    data = list()
    for i in range(20):
        blockhash = builder.get_block_hash(height + i)
        if blockhash is None:
            break
        block = builder.get_block(blockhash)
        if block is None:
            break
        txs = [(tx.b, tx.signature) for tx in block.txs]
        data.append((block.b, block.height, block.flag, txs))
    # TODO:一度に送信できるBytesにチェック
    return data
예제 #8
0
async def get_block_by_hash(request):
    try:
        blockhash = request.query.get('hash')
        blockhash = unhexlify(blockhash.encode())
        block = builder.get_block(blockhash)
        if block is None:
            return web.Response(text="Not found block.", status=400)
        data = block.getinfo()
        data['size'] = block.getsize()
        data['hex'] = hexlify(block.b).decode()
        return web_base.json_res(data)
    except Exception as e:
        return web_base.error_res()
예제 #9
0
def fill_newblock_info(data):
    new_block = Block(binary=data['block'])
    proof = TX(binary=data['proof'])
    new_block.txs.append(proof)
    new_block.flag = data['block_flag']
    proof.signature = data['sign']
    # Check the block is correct info
    if not new_block.pow_check():
        raise BlockChainError('Proof of work is not satisfied.')
    if builder.get_block(new_block.hash):
        raise BlockChainError('Already inserted block.')
    before_block = builder.get_block(new_block.previous_hash)
    if before_block is None:
        raise BlockChainError('Not found beforeBlock {}.'.format(
            hexlify(new_block.previous_hash).decode()))
    new_height = before_block.height + 1
    proof.height = new_height
    new_block.height = new_height
    # 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
            logging.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(binary=r['tx'])
            tx.signature = r['sign']
            check_tx(tx, include_block=None)
            tx_builder.put_unconfirmed(tx)
            logging.debug("Success unknown tx download {}".format(tx))
        tx.height = new_height
        new_block.txs.append(tx)
    return new_block
예제 #10
0
파일: chaininfo.py 프로젝트: kmn/bc4py
async def get_block_by_height(request):
    f_pickled = request.query.get('pickle', False)
    height = int(request.query['height'])
    blockhash = builder.get_block_hash(height)
    if blockhash is None:
        return web.Response(text="Not found height.", status=400)
    block = builder.get_block(blockhash)
    if f_pickled:
        block = pickle.dumps(block)
        return web_base.json_res(b64encode(block).decode())
    data = block.getinfo()
    data['size'] = block.getsize()
    data['hex'] = hexlify(block.b).decode()
    return web_base.json_res(data)
예제 #11
0
def _block_by_hash(blockhash):
    block = builder.get_block(blockhash)
    if block is None:
        return 'Not found blockhash {}.'.format(hexlify(blockhash).decode())
    txs = [{'tx': tx.b, 'sign': tx.signature} for tx in block.txs]
    send_data = {
        'block': block.b,
        'height': block.height,
        'flag': block.flag,
        'orphan': bool(block.f_orphan is True),
        'next_hash': block.next_hash,
        'difficulty': block.difficulty,
        'txs': txs
    }
    return send_data
예제 #12
0
파일: broadcast.py 프로젝트: kmn/bc4py
def make_block_by_node(blockhash):
    """ create Block by outside node """
    r = ask_node(cmd=DirectCmd.BLOCK_BY_HASH, data={'blockhash': blockhash})
    if isinstance(r, str):
        raise BlockChainError('make_block_by_node() failed, by "{}"'.format(hexlify(blockhash).decode(), r))
    block = Block(binary=r['block'])
    block.flag = r['flag']
    before_block = builder.get_block(blockhash=block.previous_hash)
    if before_block is None:
        raise BlockChainError('Not found BeforeBeforeBlock {}'.format(hexlify(block.previous_hash).decode()))
    block.height = before_block.height + 1
    for tx in r['txs']:
        _tx = TX(binary=tx['tx'])
        _tx.height = block.height
        _tx.signature = tx['sign']
        block.txs.append(_tx)
    return block
예제 #13
0
def try_emulate(start_tx, gas_limit=None, out=None):
    start_time = time.time()
    cxt = get_context('spawn')
    que = cxt.Queue()
    # out = out or io.StringIO()
    c_address, c_method, c_args, c_redeem = bjson.loads(start_tx.message)
    c_bin = get_contract_binary(c_address)
    assert c_bin, 'Not found c_bin of {}'.format(c_address)
    params = {
        'sub_dir': V.SUB_DIR, 'genesis_block': builder.get_block(builder.get_block_hash(0)),
        'c_bin': c_bin, 'c_address': c_address, 'c_method': c_method, 'args': c_args}
    p = cxt.Process(target=_work, args=(params, start_tx, que))
    p.start()
    que_cmd, port = que.get(timeout=10)
    if que_cmd != CMD_PORT:
        raise TypeError('Not correct command="{}" data="{}"'.format(que_cmd, port))
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect(("127.0.0.1", port))
    sock.settimeout(10)
    # Start emulation
    line = fee = 0
    cmd = EMU_STEP
    error = None
    logging.debug("Start contract emulate port={}".format(port))
    while True:
        try:
            msg_list = sock.recv(8192).decode(errors='ignore').replace("\r", "").split("\n")
            if len(msg_list) <= 1:
                sock.close()
                break
            elif len(msg_list) < 3:
                pass
            elif gas_limit and fee > gas_limit:
                error = 'reached gas limit. [{}>{}]'.format(fee, gas_limit)
                break
            elif cmd in (EMU_STEP, EMU_NEXT, EMU_UNTIL, EMU_RETURN):
                msg_list, path, words = msg_list[:-3], msg_list[-3][2:], msg_list[-2][3:]
                file = os.path.split(path)[1]
                print(file, words, path)
                if file.startswith('exe.py') and file.endswith('work_field()'):
                    cmd = EMU_STEP  # Start!
                    print("start contract {}", file=out)
                elif file.startswith('contract('):
                    line += 1
                    fee += 1
                    cmd = EMU_STEP
                else:
                    cmd = EMU_NEXT
                # Add fee
                for func, gas in __price__.items():
                    if func in words and words.startswith('def ' + func + '('):
                        fee += gas
                print("{}:read [{}] {} >> {}".format(line, fee, cmd, words), file=out)
            else:
                msg = ', '.join(msg_list)
                print("msg [{}] >>".format(cmd), msg, file=out)
            # response to work field
            sock.send((cmd + "\n").encode())
        except ConnectionResetError:
            break
        except BaseException:
            error = str(traceback.format_exc())
            break
    logging.debug("Finish contract {}Sec error:{}".format(round(time.time() - start_time, 3), error))
    # Close emulation
    try:
        que_cmd, result = que.get_nowait()
        print(result)
        try: que.close()
        except: pass
        try: p.terminate()
        except: pass
        if que_cmd == CMD_ERROR:
            return False, result, fee, line
        elif que_cmd == CMD_SUCCESS:
            return True, result, fee, line
    except BaseException:
        return False, error, fee, line
예제 #14
0
def fast_sync_chain():
    assert V.PC_OBJ is not None, "Need PeerClient start before."
    global f_changed_status
    back_thread = Thread(target=background_sync_chain,
                         name='BackSync',
                         daemon=True)
    back_thread.start()
    start = time()

    # 外部Nodeに次のBlockを逐一尋ねる
    failed_num = 0
    before_block = builder.best_block
    index_height = before_block.height + 1
    logging.debug("Start sync by {}".format(before_block))
    while failed_num < 5:
        if index_height in block_stack:
            new_block = block_stack[index_height]
            with write_protect_lock:
                del block_stack[index_height]
        elif backend_processing_lock.locked():
            sleep(0.1)
            continue
        else:
            with backend_processing_lock:
                logging.debug(
                    "Stack blocks on front form {}".format(index_height))
                r = ask_node(cmd=DirectCmd.BIG_BLOCKS,
                             data={'height': index_height})
                if isinstance(r, str):
                    logging.debug("NewBLockGetError:{}".format(r))
                    before_block = builder.get_block(
                        before_block.previous_hash)
                    index_height = before_block.height + 1
                    failed_num += 1
                    continue
                elif isinstance(r, list):
                    waiter = Waiter(0)
                    waiter.set()
                    waiter = put_to_block_stack(r, waiter)
                    if waiter is None or len(block_stack) == 0:
                        break
                    else:
                        waiter.wait()
                        continue
                else:
                    failed_num += 1
                    logging.debug("Not correct format BIG_BLOCKS.")
                    continue
        # Base check
        base_check_failed_msg = None
        if before_block.hash != new_block.previous_hash:
            base_check_failed_msg = "Not correct previous hash {}".format(
                new_block)
        # proof of work check
        if not new_block.pow_check():
            base_check_failed_msg = "Not correct work hash {}".format(
                new_block)
        # rollback
        if base_check_failed_msg is not None:
            before_block = builder.get_block(before_block.previous_hash)
            index_height = before_block.height + 1
            failed_num += 1
            for height in tuple(block_stack.keys()):
                if height >= index_height:
                    del block_stack[height]
            logging.debug(base_check_failed_msg)
            continue
        # TX check
        if len(new_block.txs) > 1:
            with closing(create_db(V.DB_ACCOUNT_PATH)) as db:
                cur = db.cursor()
                for tx in new_block.txs:
                    if tx.type in (C.TX_POS_REWARD, C.TX_POW_REWARD):
                        continue
                    check_tx(tx=tx, include_block=None)
                    tx_builder.put_unconfirmed(tx=tx, outer_cur=cur)
                db.commit()
        # Block check
        check_block(new_block)
        for tx in new_block.txs:
            tx.height = new_block.height
            check_tx(tx=tx, include_block=new_block)
        # Chainに挿入
        builder.new_block(new_block)
        for tx in new_block.txs:
            user_account.affect_new_tx(tx)
        builder.batch_apply()
        f_changed_status = True
        # 次のBlock
        failed_num = 0
        before_block = new_block
        index_height = before_block.height + 1
        # ロギング
        if index_height % 100 == 0:
            logging.debug("Update block {} now...".format(index_height + 1))
    # Unconfirmed txを取得
    logging.info("Finish get block, next get unconfirmed.")
    r = None
    while not isinstance(r, dict):
        r = ask_node(cmd=DirectCmd.UNCONFIRMED_TX, f_continue_asking=True)
    for txhash in r['txs']:
        if txhash in tx_builder.unconfirmed:
            continue
        try:
            r = ask_node(cmd=DirectCmd.TX_BY_HASH,
                         data={'txhash': txhash},
                         f_continue_asking=True)
            tx = TX(binary=r['tx'])
            tx.signature = r['sign']
            check_tx_time(tx)
            check_tx(tx, include_block=None)
            tx_builder.put_unconfirmed(tx)
        except BlockChainError:
            logging.debug("Failed get unconfirmed {}".format(
                hexlify(txhash).decode()))
    # 最終判断
    reset_good_node()
    set_good_node()
    my_best_height = builder.best_block.height
    best_height_on_network, best_hash_on_network = get_best_conn_info()
    if best_height_on_network <= my_best_height:
        logging.info(
            "Finish update chain data by network. {}Sec [{}<={}]".format(
                round(time() - start, 1), best_height_on_network,
                my_best_height))
        return True
    else:
        logging.debug("Continue update chain, {}<={}".format(
            best_height_on_network, my_best_height))
        return False