예제 #1
0
파일: blockchain.py 프로젝트: tsifrer/ark
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.database = load_plugin("chain.plugins.database")
        self.process_queue = load_plugin("chain.plugins.process_queue")
        self.peers = load_plugin("chain.plugins.peers")
        self.peers.setup()
        self.transaction_pool = load_plugin("chain.plugins.transaction_pool")
예제 #2
0
def _verify_peer_blocks(peer, start_height, peer_height):
    database = load_plugin("chain.plugins.database")

    current_round, _, max_delegates = calculate_round(start_height)
    last_height_in_round = current_round * max_delegates
    first_height_in_round = (current_round - 1) * max_delegates + 1
    delegates = database.get_active_delegates(first_height_in_round)
    delegate_keys = [d.public_key for d in delegates]

    end_height = min(peer_height, last_height_in_round)

    height_block_map = {}
    for height in range(start_height, end_height + 1):
        if height not in height_block_map:
            # Height does not exist in our map yet, so get it from the peer
            # To get the block with current height, we need to fetch for blocks from
            # previous height
            blocks = peer.fetch_blocks_from_height(height - 1)
            for block in blocks:
                height_block_map[block.height] = block

        if height in height_block_map:
            is_valid = _is_valid_block(height_block_map[height], height,
                                       current_round, delegate_keys)
            if not is_valid:
                return False
        else:
            logger.info(
                "Could not find block with height %s in mapping %s",
                height,
                height_block_map,
            )
            return False

    return True
예제 #3
0
def discover_peers():
    """
    Fetch peers of your existing peers to increase the number of peers.
    """
    # TODO: Disable this function if peer discoverability is disabled in config

    peer_manager = load_plugin("chain.plugins.peers")
    peers = peer_manager.peers()
    # Shuffle peers so we always get the peers from the different peers at the start
    random.shuffle(peers)
    for index, peer in enumerate(peers):
        his_peers = peer.fetch_peers()
        for his_peer in his_peers:
            add_peer(
                ip=his_peer.ip,
                port=his_peer.port,
                chain_version=his_peer.chain_version,
                nethash=his_peer.nethash,
                os=his_peer.os,
            )

        # Always get peers from at least 4 sources. As add_peer is async,
        # `has_minimum_peers` might actually return wrong result, but that will only
        # increase the number of peers we have.
        if index >= 4 and peer_manager.has_minimum_peers():
            break

    reverify_all_peers()
예제 #4
0
def migrate():
    database = load_plugin("chain.plugins.database")

    migrate_dir = os.path.join(os.getcwd(), "chain", "plugins", "database",
                               "migrations")
    router = Router(database.db, migrate_dir=migrate_dir)

    router.run()
예제 #5
0
def create_migrations(name):

    database = load_plugin("chain.plugins.database")
    router = Router(database.db,
                    migrate_dir="chain/plugins/database/migrations")

    # Create migration
    router.create(name, auto="chain.plugins.database.models")
예제 #6
0
    def __init__(self):
        super().__init__()

        self.database = load_plugin("chain.plugins.database")

        self.redis = Redis(
            host=os.environ.get("REDIS_HOST", "localhost"),
            port=os.environ.get("REDIS_PORT", 6379),
            db=os.environ.get("REDIS_DB", 0),
        )
예제 #7
0
파일: manager.py 프로젝트: tsifrer/ark
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.database = load_plugin("chain.plugins.database")

        # TODO: check DNS and NTP connectivitiy?

        self.redis = Redis(
            host=os.environ.get("REDIS_HOST", "localhost"),
            port=os.environ.get("REDIS_PORT", 6379),
            db=os.environ.get("REDIS_DB", 0),
        )
예제 #8
0
    def __init__(self):
        super().__init__()

        # Load the database plugin to correctly setup PoolTransaction model and to
        # use it for getting the last block
        self.database = load_plugin("chain.plugins.database")

        self.redis = Redis(
            host=os.environ.get("REDIS_HOST", "localhost"),
            port=os.environ.get("REDIS_PORT", 6379),
            db=os.environ.get("REDIS_DB", 0),
        )

        self.wallets = PoolWalletManager()
예제 #9
0
def reverify_peer(ip):
    peer_manager = load_plugin("chain.plugins.peers")
    peer = peer_manager.get_peer_by_ip(ip)
    logger.info("Reverifying peer %s:%s", peer.ip, peer.port)
    if peer:
        try:
            peer.verify_peer()
        except Exception as e:  # TODO: be more specific
            logger.error("Peer %s:%s failed verification: %s", peer.ip,
                         peer.port, e)
            peer_manager.suspend_peer(peer)
        else:
            logger.info("Peer %s:%s successfully reverified", peer.ip,
                        peer.port)
            peer_manager.redis.set(peer_manager.key_active.format(peer.ip),
                                   peer.to_json())
    else:
        logger.warning("Couldn't find a peer to reverify")
예제 #10
0
def add_peer(ip, port, chain_version, nethash, os):
    # TODO: Disable this function if peer discoverability is disabled in config

    peer_manager = load_plugin("chain.plugins.peers")

    peer = Peer(ip=ip,
                port=port,
                chain_version=chain_version,
                nethash=nethash,
                os=os)

    if not peer.is_valid() or peer_manager.is_peer_suspended(peer):
        logger.warning("Peer %s:%s is invalid or suspended.", peer.ip,
                       peer.port)
        return

    if peer_manager.peer_with_ip_exists(peer.ip):
        logger.warning("Peer %s:%s already exists.", peer.ip, peer.port)
        return

    # Wait for a bit more than 3s for response in case the node you're pinging is
    # the official node and is trying to add you as their peer and your p2p is not
    # responding. When they ping you, they wait for 3 seconds for response and if they
    # don't get any, they reject your response even though the request to them was
    # valid
    # try:
    try:
        peer.verify_peer()
    except Exception as e:  # TODO: Be more specific
        logger.exception("Suspended peer because %s", str(e))
        peer_manager.suspend_peer(peer)
    else:
        logger.info(
            "Accepting peer %s:%s. Vefification: %s",
            peer.ip,
            peer.port,
            peer.verification,
        )
        peer_manager.redis.set(peer_manager.key_active.format(peer.ip),
                               peer.to_json())
예제 #11
0
def _find_highest_common_between_heights(peer, heights):
    database = load_plugin("chain.plugins.database")
    logger.info(
        ("Checking for the highest common block height. Currently checking for "
         "heights %s"),
        heights,
    )
    our_blocks = database.get_blocks_by_heights(heights)
    if len(our_blocks) != len(heights):
        raise Exception(
            "Did not fetch all blocks with heights {} from the db".format(
                heights))

    # TODO: something something deadline

    heights_by_id = {}
    for block in our_blocks:
        heights_by_id[block.id] = block.height

    common = peer.fetch_common_block_by_ids(list(heights_by_id.keys()))
    if not common:
        logger.info(
            "Couldn't find a common block for peer %s:%s for block heights %s",
            peer.ip,
            peer.port,
            heights,
        )
        return None

    if heights_by_id.get(common["id"]) != common["height"]:
        logger.info(
            ("Our block height %s does not match with peer height %s for block with "
             "id %s"),
            heights_by_id.get(common["id"]),
            common["height"],
            common["id"],
        )
        return None
    return common["height"]
예제 #12
0
def verify_peer_status(peer, state):
    """
    Verify the peer's blockchain (claimed state).
    Confirm that the peer's chain is either:
    - the same as ours or
    - different than ours but legit.
    Legit chain would have blocks signed/forged by the appropriate delegate(s).

    We distinguish 6 different cases with respect to our chain and peer's chain:

    Case1. Peer height > our height and our highest block is part of the peer's chain.
      This means the peer is ahead of us, on the same chain. No fork.
      We verify: his blocks that have height > our height (up to the round end).

    Case2. Peer height > our height and our highest block is not part of the peer's
      chain
      This means that the peer is on a different, higher chain. It has forked before our
      latest block.
      We verify: the first few of the peer's blocks after the fork (up to the round end)

    Case3. Peer height == our height and our latest blocks are the same.
      This means that our chains are the same.
      We verify: nothing.

    Case4. Peer height == our height and our latest blocks differ.
      This means that we are on a different chains with equal height. A fork has
      occurred
      We verify: the first few of the peer's blocks after the fork (up to the round end)

    Case5. Peer height < our height and peer's latest block is part of our chain.
      This means that the peer is on the same chain as us, just lagging behind.

    Case6. Peer height < our height and peer's latest block is not part of our chain.
      This means that we have forked and the peer's chain is lower.
      We verify: the first few of the peer's blocks after the fork (up to the round end)
    """
    database = load_plugin("chain.plugins.database")
    last_block = database.get_last_block()
    peer_height = int(state["header"]["height"])
    peer_id = state["header"]["id"]
    """
    Case3. Peer height == our height and our latest blocks are the same.
      This means that our chains are the same.
    """
    if last_block.height == peer_height and last_block.id == peer_id:
        logger.info(
            ("Peer's latest block is the same as our latest block "
             "(height=%s, id=%s). Identical chains."),
            last_block.height,
            last_block.id,
        )
        return {
            "my_height": last_block.height,
            "his_height": peer_height,
            "highest_common_height": peer_height,
        }
    """
    Case5. Peer height < our height and peer's latest block is part of our chain.
      This means that the peer is on the same chain as us, just lagging behind.
    """
    if peer_height < last_block.height:
        blocks = database.get_blocks_by_heights([peer_height])
        block = blocks[0]
        if block.id == peer_id:
            return {
                "my_height": last_block.height,
                "his_height": peer_height,
                "highest_common_height": peer_height,
            }

    highest_common_height = _find_highest_common_block_height(
        peer, 1, min(peer_height, last_block.height))
    if not highest_common_height:
        return None

    valid_peer_blocks = _verify_peer_blocks(peer, highest_common_height + 1,
                                            peer_height)
    if not valid_peer_blocks:
        return None

    return {
        "my_height": last_block.height,
        "his_height": peer_height,
        "highest_common_height": highest_common_height,
    }
예제 #13
0
def reverify_all_peers():
    peer_manager = load_plugin("chain.plugins.peers")
    peers = peer_manager.peers()
    logger.info("Reverifying all %s peers", len(peers))
    for peer in peers:
        reverify_peer(peer.ip)
예제 #14
0
def rollback_to_round(round):
    database = load_plugin("chain.plugins.database")
    database.rollback_to_round(int(round))
예제 #15
0
 def __init__(self):
     super().__init__()
     self.db = load_plugin("chain.plugins.database")
     self.process_queue = load_plugin("chain.plugins.process_queue")
     self.transaction_pool = load_plugin("chain.plugins.transaction_pool")