Пример #1
0
    async def test_reorg_from_genesis(self):
        blocks = bt.get_consecutive_blocks(test_constants, 20, [], 9, b"0")
        store = FullNodeStore("fndb_test")
        await store._clear_database()
        b: Blockchain = Blockchain(store, test_constants)
        await b.initialize()
        for block in blocks:
            await b.receive_block(block)
        assert b.get_current_tips()[0].height == 20

        # Reorg from genesis
        blocks_reorg_chain = bt.get_consecutive_blocks(test_constants, 21,
                                                       [blocks[0]], 9, b"1")
        for reorg_block in blocks_reorg_chain:
            result = await b.receive_block(reorg_block)
            if reorg_block.height == 0:
                assert result == ReceiveBlockResult.ALREADY_HAVE_BLOCK
            elif reorg_block.height < 19:
                assert result == ReceiveBlockResult.ADDED_AS_ORPHAN
            else:
                assert result == ReceiveBlockResult.ADDED_TO_HEAD
        assert b.get_current_tips()[0].height == 21

        # Reorg back to original branch
        blocks_reorg_chain_2 = bt.get_consecutive_blocks(
            test_constants, 3, blocks, 9, b"3")
        await b.receive_block(blocks_reorg_chain_2[20]
                              ) == ReceiveBlockResult.ADDED_AS_ORPHAN
        assert (await b.receive_block(blocks_reorg_chain_2[21]
                                      )) == ReceiveBlockResult.ADDED_TO_HEAD
        assert (await b.receive_block(blocks_reorg_chain_2[22]
                                      )) == ReceiveBlockResult.ADDED_TO_HEAD
Пример #2
0
    async def test1(self):
        store = FullNodeStore("fndb_test")
        await store._clear_database()
        blocks = bt.get_consecutive_blocks(test_constants, 10, [], 10)
        b: Blockchain = Blockchain(test_constants)
        await store.add_block(blocks[0])
        await b.initialize({})
        for i in range(1, 9):
            assert (await
                    b.receive_block(blocks[i]
                                    )) == ReceiveBlockResult.ADDED_TO_HEAD
            await store.add_block(blocks[i])

        full_node_1 = FullNode(store, b)
        server_1 = ChiaServer(21234, full_node_1, NodeType.FULL_NODE)
        _ = await server_1.start_server("127.0.0.1", None)
        full_node_1._set_server(server_1)

        full_node_2 = FullNode(store, b)
        server_2 = ChiaServer(21235, full_node_2, NodeType.FULL_NODE)
        full_node_2._set_server(server_2)

        await server_2.start_client(PeerInfo("127.0.0.1", uint16(21234)), None)

        await asyncio.sleep(2)  # Allow connections to get made

        num_unfinished_blocks = 1000
        start_unf = time.time()
        for i in range(num_unfinished_blocks):
            msg = Message("unfinished_block",
                          peer_protocol.UnfinishedBlock(blocks[9]))
            server_1.push_message(
                OutboundMessage(NodeType.FULL_NODE, msg, Delivery.BROADCAST))

        # Send the whole block ast the end so we can detect when the node is done
        block_msg = Message("block", peer_protocol.Block(blocks[9]))
        server_1.push_message(
            OutboundMessage(NodeType.FULL_NODE, block_msg, Delivery.BROADCAST))

        while time.time() - start_unf < 300:
            if max([h.height for h in b.get_current_tips()]) == 9:
                print(
                    f"Time taken to process {num_unfinished_blocks} is {time.time() - start_unf}"
                )
                server_1.close_all()
                server_2.close_all()
                await server_1.await_closed()
                await server_2.await_closed()
                return
            await asyncio.sleep(0.1)

        server_1.close_all()
        server_2.close_all()
        await server_1.await_closed()
        await server_2.await_closed()
        raise Exception("Took too long to process blocks")
Пример #3
0
 async def test_get_header_hashes(self):
     blocks = bt.get_consecutive_blocks(test_constants, 5, [], 9, b"0")
     store = FullNodeStore("fndb_test")
     await store._clear_database()
     b: Blockchain = Blockchain(store, test_constants)
     await b.initialize()
     for block in blocks:
         await b.receive_block(block)
     header_hashes = b.get_header_hashes(blocks[-1].header_hash)
     assert len(header_hashes) == 6
     print(header_hashes)
     print([block.header_hash for block in blocks])
     assert header_hashes == [block.header_hash for block in blocks]
Пример #4
0
 async def initial_blockchain(self):
     """
     Provides a list of 10 valid blocks, as well as a blockchain with 9 blocks added to it.
     """
     store = FullNodeStore("fndb_test")
     await store._clear_database()
     blocks = bt.get_consecutive_blocks(test_constants, 10, [], 10)
     b: Blockchain = Blockchain(store, test_constants)
     await b.initialize()
     for i in range(1, 9):
         assert (await
                 b.receive_block(blocks[i]
                                 )) == ReceiveBlockResult.ADDED_TO_HEAD
     return (blocks, b)
Пример #5
0
 async def test_basic_blockchain(self):
     store = FullNodeStore("fndb_test")
     await store._clear_database()
     bc1: Blockchain = Blockchain(store)
     await bc1.initialize()
     assert len(bc1.get_current_tips()) == 1
     genesis_block = bc1.get_current_tips()[0]
     assert genesis_block.height == 0
     assert genesis_block.challenge
     assert (bc1.get_header_blocks_by_height(
         [uint32(0)], genesis_block.header_hash))[0] == genesis_block
     assert (await bc1.get_next_difficulty(
         genesis_block.header_hash)) == genesis_block.challenge.total_weight
     assert await bc1.get_next_ips(genesis_block.header_hash) > 0
Пример #6
0
    async def test2(self):
        num_blocks = 100
        store = FullNodeStore("fndb_test")
        await store._clear_database()
        blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 10)
        b: Blockchain = Blockchain(test_constants)
        await store.add_block(blocks[0])
        await b.initialize({})

        full_node_1 = FullNode(store, b)
        server_1 = ChiaServer(21236, full_node_1, NodeType.FULL_NODE)
        _ = await server_1.start_server("127.0.0.1", None)
        full_node_1._set_server(server_1)

        full_node_2 = FullNode(store, b)
        server_2 = ChiaServer(21237, full_node_2, NodeType.FULL_NODE)
        full_node_2._set_server(server_2)

        await server_2.start_client(PeerInfo("127.0.0.1", uint16(21236)), None)

        await asyncio.sleep(2)  # Allow connections to get made

        start_unf = time.time()
        for i in range(1, num_blocks):
            msg = Message("block", peer_protocol.Block(blocks[i]))
            server_1.push_message(
                OutboundMessage(NodeType.FULL_NODE, msg, Delivery.BROADCAST))

        while time.time() - start_unf < 300:
            if max([h.height for h in b.get_current_tips()]) == num_blocks - 1:
                print(
                    f"Time taken to process {num_blocks} is {time.time() - start_unf}"
                )
                server_1.close_all()
                server_2.close_all()
                await server_1.await_closed()
                await server_2.await_closed()
                return
            await asyncio.sleep(0.1)

        server_1.close_all()
        server_2.close_all()
        await server_1.await_closed()
        await server_2.await_closed()
        raise Exception("Took too long to process blocks")
Пример #7
0
async def load_header_blocks_from_store(
    store: FullNodeStore, ) -> Dict[str, HeaderBlock]:
    seen_blocks: Dict[str, HeaderBlock] = {}
    tips: List[HeaderBlock] = []
    async for full_block in store.get_blocks():
        if not tips or full_block.weight > tips[0].weight:
            tips = [full_block.header_block]
        seen_blocks[full_block.header_hash] = full_block.header_block

    header_blocks = {}
    if len(tips) > 0:
        curr: HeaderBlock = tips[0]
        reverse_blocks: List[HeaderBlock] = [curr]
        while curr.height > 0:
            curr = seen_blocks[curr.prev_header_hash]
            reverse_blocks.append(curr)

        for block in reversed(reverse_blocks):
            header_blocks[block.header_hash] = block
    return header_blocks
Пример #8
0
    async def test_basic_reorg(self):
        blocks = bt.get_consecutive_blocks(test_constants, 100, [], 9)
        store = FullNodeStore("fndb_test")
        await store._clear_database()
        b: Blockchain = Blockchain(store, test_constants)
        await b.initialize()

        for block in blocks:
            await b.receive_block(block)
        assert b.get_current_tips()[0].height == 100

        blocks_reorg_chain = bt.get_consecutive_blocks(test_constants, 30,
                                                       blocks[:90], 9, b"1")
        for reorg_block in blocks_reorg_chain:
            result = await b.receive_block(reorg_block)
            if reorg_block.height < 90:
                assert result == ReceiveBlockResult.ALREADY_HAVE_BLOCK
            elif reorg_block.height < 99:
                assert result == ReceiveBlockResult.ADDED_AS_ORPHAN
            elif reorg_block.height >= 100:
                assert result == ReceiveBlockResult.ADDED_TO_HEAD
        assert b.get_current_tips()[0].height == 119
Пример #9
0
    async def test_difficulty_change(self):
        num_blocks = 30
        # Make it 5x faster than target time
        blocks = bt.get_consecutive_blocks(test_constants, num_blocks, [], 2)

        store = FullNodeStore("fndb_test")
        await store._clear_database()
        b: Blockchain = Blockchain(store, test_constants)
        await b.initialize()
        for i in range(1, num_blocks):
            assert (await
                    b.receive_block(blocks[i]
                                    )) == ReceiveBlockResult.ADDED_TO_HEAD

        diff_25 = await b.get_next_difficulty(blocks[24].header_hash)
        diff_26 = await b.get_next_difficulty(blocks[25].header_hash)
        diff_27 = await b.get_next_difficulty(blocks[26].header_hash)

        assert diff_26 == diff_25
        assert diff_27 > diff_26
        assert (diff_27 / diff_26) <= test_constants["DIFFICULTY_FACTOR"]

        assert (await b.get_next_ips(blocks[1].header_hash
                                     )) == constants["VDF_IPS_STARTING"]
        assert (await
                b.get_next_ips(blocks[24].header_hash
                               )) == (await
                                      b.get_next_ips(blocks[23].header_hash))
        assert (await
                b.get_next_ips(blocks[25].header_hash
                               )) == (await
                                      b.get_next_ips(blocks[24].header_hash))
        assert (await b.get_next_ips(blocks[26].header_hash
                                     )) > (await b.get_next_ips(
                                         blocks[25].header_hash))
        assert (await
                b.get_next_ips(blocks[27].header_hash
                               )) == (await
                                      b.get_next_ips(blocks[26].header_hash))
Пример #10
0
    async def test_lca(self):
        blocks = bt.get_consecutive_blocks(test_constants, 5, [], 9, b"0")
        store = FullNodeStore("fndb_test")
        await store._clear_database()
        b: Blockchain = Blockchain(store, test_constants)
        await b.initialize()
        for block in blocks:
            await b.receive_block(block)

        assert b.lca_block == blocks[3]
        block_5_2 = bt.get_consecutive_blocks(test_constants, 1, blocks[:5], 9,
                                              b"1")[5]
        block_5_3 = bt.get_consecutive_blocks(test_constants, 1, blocks[:5], 9,
                                              b"2")[5]

        await b.receive_block(block_5_2)
        assert b.lca_block == blocks[4]
        await b.receive_block(block_5_3)
        assert b.lca_block == blocks[4]

        reorg = bt.get_consecutive_blocks(test_constants, 6, [], 9, b"3")
        for block in reorg:
            await b.receive_block(block)
        assert b.lca_block == blocks[0]
Пример #11
0
    async def test_basic_database(self):
        blocks = bt.get_consecutive_blocks(test_constants, 9, [], 9, b"0")

        db = FullNodeStore("fndb_test")
        await db._clear_database()
        genesis = FullBlock.from_bytes(constants["GENESIS_BLOCK"])

        # Save/get block
        for block in blocks:
            await db.add_block(block)
            assert block == await db.get_block(block.header_hash)

        # Save/get sync
        for sync_mode in (False, True):
            await db.set_sync_mode(sync_mode)
            assert sync_mode == await db.get_sync_mode()

        # clear sync info
        await db.clear_sync_info()

        # add/get potential tip, get potential tips num
        await db.add_potential_tip(blocks[6])
        assert blocks[6] == await db.get_potential_tip(blocks[6].header_hash)

        # add/get potential trunk
        header = genesis.header_block
        db.add_potential_header(header)
        assert db.get_potential_header(genesis.height) == header

        # Add potential block
        await db.add_potential_block(genesis)
        assert genesis == await db.get_potential_block(uint32(0))

        # Add/get candidate block
        assert await db.get_candidate_block(0) is None
        partial = (
            blocks[5].body,
            blocks[5].header_block.header.data,
            blocks[5].header_block.proof_of_space,
        )
        await db.add_candidate_block(blocks[5].header_hash, *partial)
        assert await db.get_candidate_block(blocks[5].header_hash) == partial
        await db.clear_candidate_blocks_below(uint32(8))
        assert await db.get_candidate_block(blocks[5].header_hash) is None

        # Add/get unfinished block
        i = 1
        for block in blocks:
            key = (block.header_hash, uint64(1000))
            assert await db.get_unfinished_block(key) is None
            await db.add_unfinished_block(key, block)
            assert await db.get_unfinished_block(key) == block
            assert len(await db.get_unfinished_blocks()) == i
            i += 1
        await db.clear_unfinished_blocks_below(uint32(5))
        assert len(await db.get_unfinished_blocks()) == 5

        # Set/get unf block leader
        assert db.get_unfinished_block_leader() == (0, (1 << 64) - 1)
        db.set_unfinished_block_leader(key)
        assert db.get_unfinished_block_leader() == key

        assert await db.get_disconnected_block(blocks[0].prev_header_hash) is None
        # Disconnected blocks
        for block in blocks:
            await db.add_disconnected_block(block)
            await db.get_disconnected_block(block.prev_header_hash) == block

        await db.clear_disconnected_blocks_below(uint32(5))
        assert await db.get_disconnected_block(blocks[4].prev_header_hash) is None

        h_hash_1 = bytes32(token_bytes(32))
        assert not db.seen_unfinished_block(h_hash_1)
        assert db.seen_unfinished_block(h_hash_1)
        db.clear_seen_unfinished_blocks()
        assert not db.seen_unfinished_block(h_hash_1)

        assert not db.seen_block(h_hash_1)
        assert db.seen_block(h_hash_1)
        db.clear_seen_blocks()
        assert not db.seen_block(h_hash_1)
Пример #12
0
async def main():
    # Create the store (DB) and full node instance
    db_id = 0
    if "-id" in sys.argv:
        db_id = int(sys.argv[sys.argv.index("-id") + 1])
    store = FullNodeStore(f"fndb_{db_id}")

    blockchain = Blockchain(store)
    log.info("Initializing blockchain from disk")
    await blockchain.initialize()

    full_node = FullNode(store, blockchain)
    # Starts the full node server (which full nodes can connect to)
    host, port = parse_host_port(full_node)

    if full_node.config["enable_upnp"]:
        log.info(f"Attempting to enable UPnP (open up port {port})")
        try:
            upnp = miniupnpc.UPnP()
            upnp.discoverdelay = 5
            upnp.discover()
            upnp.selectigd()
            upnp.addportmapping(port, "TCP", upnp.lanaddr, port, "chia", "")
        except Exception as e:
            log.warning(f"UPnP failed: {e}")

    server = ChiaServer(port, full_node, NodeType.FULL_NODE)
    full_node._set_server(server)
    _ = await server.start_server(host, full_node._on_connect)
    wait_for_ui, ui_close_cb = None, None

    def master_close_cb():
        global server_closed
        if not server_closed:
            # Called by the UI, when node is closed, or when a signal is sent
            log.info("Closing all connections, and server...")
            full_node._shutdown()
            server.close_all()
            server_closed = True

    def signal_received():
        if ui_close_cb:
            ui_close_cb()
        master_close_cb()

    asyncio.get_running_loop().add_signal_handler(signal.SIGINT,
                                                  signal_received)
    asyncio.get_running_loop().add_signal_handler(signal.SIGTERM,
                                                  signal_received)

    if "-u" in sys.argv:
        # Starts the UI if -u is provided
        index = sys.argv.index("-u")
        ui_ssh_port = int(sys.argv[index + 1])
        from src.ui.prompt_ui import start_ssh_server

        wait_for_ui, ui_close_cb = await start_ssh_server(
            store,
            blockchain,
            server,
            port,
            ui_ssh_port,
            full_node.config["ssh_filename"],
            master_close_cb,
        )

    connect_to_farmer = "-f" in sys.argv
    connect_to_timelord = "-t" in sys.argv

    full_node._start_bg_tasks()

    log.info("Waiting to connect to some peers...")
    await asyncio.sleep(3)
    log.info(
        f"Connected to {len(server.global_connections.get_connections())} peers."
    )

    if connect_to_farmer and not server_closed:
        peer_info = PeerInfo(
            full_node.config["farmer_peer"]["host"],
            full_node.config["farmer_peer"]["port"],
        )
        _ = await server.start_client(peer_info, None)

    if connect_to_timelord and not server_closed:
        peer_info = PeerInfo(
            full_node.config["timelord_peer"]["host"],
            full_node.config["timelord_peer"]["port"],
        )
        _ = await server.start_client(peer_info, None)

    # Awaits for server and all connections to close
    await server.await_closed()

    # Awaits for all ui instances to close
    if wait_for_ui is not None:
        await wait_for_ui()
    await asyncio.get_running_loop().shutdown_asyncgens()