1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
|
# Copyright 2014-2022 Vincent Texier <vit@free.fr>
#
# DuniterPy is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# DuniterPy is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from itertools import groupby
from typing import Any, Dict, List
from duniterpy.api import bma
from duniterpy.api.client import Client
from duniterpy.documents.peer import MalformedDocumentError, Peer
from duniterpy.documents.ws2p.heads import HeadV2
def get_available_nodes(client: Client) -> List[List[Dict[str, Any]]]:
"""
Get available nodes grouped and sorted by descending block_id
Each entry is a list of nodes (HeadV2 instance, inline endpoint list) sharing the same block_id:
[
[{"head": HeadV2, "endpoints": [str, ...]}, ...],
[{"head": HeadV2, "endpoints": [str, ...]}, ...],
...
]
You can just select the first endpoint of the first node of the first group to quickly get an available node.
groups = get_available_nodes(client)
first_node_first_endpoint = groups[0][0]["endpoints"][0]
If node is down, you can select another node.
Warning: only nodes with BMAS, BASIC_MERKLED_API, and GVA endpoint are selected
and only those endpoints are available in the endpoint list
:param client: Client instance
:return:
"""
# capture heads and peers
heads_response = client(bma.network.ws2p_heads)
peers_response = client(bma.network.peers)
# get heads instances from WS2P messages
heads = []
for entry in heads_response["heads"]:
head, _ = HeadV2.from_inline(entry["messageV2"], entry["sigV2"])
heads.append(head)
# sort by block_id by descending order
heads = sorted(heads, key=lambda x: x.block_id, reverse=True)
# group heads by block_id
groups = []
for _, group in groupby(heads, key=lambda x: x.block_id):
nodes = []
for head in list(group):
# if head signature not valid...
if head.check_signature(head.pubkey) is False:
# skip this node
continue
bma_peers = [
bma_peer
for bma_peer in peers_response["peers"]
if bma_peer["pubkey"] == head.pubkey
]
# if no peer found...
if len(bma_peers) == 0:
# skip this node
continue
bma_peer = bma_peers[0]
try:
peer = Peer.from_bma(bma_peer)
# if bad peer... (mostly bad formatted endpoints)
except MalformedDocumentError:
# skip this node
continue
# set signature in Document
peer.signature = bma_peer["signature"]
# if peer signature not valid
if peer.check_signature(head.pubkey) is False:
# skip this node
continue
# filter endpoints to get only BMAS, BASIC_MERKLED_API or GVA
endpoints = [
endpoint
for endpoint in bma_peers[0]["endpoints"]
if endpoint.startswith("BMAS")
or endpoint.startswith("BASIC_MERKLED_API")
or endpoint.startswith("GVA")
]
if len(endpoints) == 0:
# skip this node
continue
# add node to group nodes
nodes.append({"head": head, "endpoints": endpoints})
# if nodes in group...
if len(nodes) > 0:
# add group to groups
groups.append(nodes)
return groups
|