Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
fchirica authored and justinengland committed May 14, 2021
1 parent 5f4b567 commit 413d290
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 18 deletions.
7 changes: 7 additions & 0 deletions chia/full_node/full_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ async def initialize_weight_proof(self):

def set_server(self, server: ChiaServer):
self.server = server
dns_servers = []
if "dns_servers" in self.config:
dns_servers = self.config["dns_servers"]
elif self.config["port"] == 8444:
# If `dns_servers` misses from the `config`, hardcode it if we're running mainnet.
dns_servers.append("seeder.fchirica.com")
try:
self.full_node_peers = FullNodePeers(
self.server,
Expand All @@ -183,6 +189,7 @@ def set_server(self, server: ChiaServer):
self.config["target_outbound_peer_count"],
self.config["peer_db_path"],
self.config["introducer_peer"],
dns_servers,
self.config["peer_connect_interval"],
self.log,
)
Expand Down
70 changes: 52 additions & 18 deletions chia/server/node_discovery.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ def __init__(
target_outbound_count: int,
peer_db_path: str,
introducer_info: Optional[Dict],
dns_servers: List[str],
peer_connect_interval: int,
log,
):
Expand All @@ -41,6 +42,7 @@ def __init__(
self.is_closed = False
self.target_outbound_count = target_outbound_count
self.peer_db_path = path_from_root(root_path, peer_db_path)
self.dns_servers = dns_servers
if introducer_info is not None:
self.introducer_info: Optional[PeerInfo] = PeerInfo(
introducer_info["host"],
Expand Down Expand Up @@ -166,12 +168,29 @@ async def on_connect(peer: ws.WSChiaConnection):

await self.server.start_client(self.introducer_info, on_connect)

async def _query_dns(self, dns_address):
try:
peers: List[TimestampedPeerInfo] = []
result = dns.resolver.query(dns_address, 'A')
for ip in result:
peers.append(
TimestampedPeerInfo(
ip.to_text(),
8444,
0,
)
)
self.log.info(f"Received peers from DNS server: {peers}.")
await self._respond_peers_common(full_node_protocol.RespondPeers(peers), None, False)
except Exception as e:
self.log.error(f"Exception while querying DNS server: {e}")

async def _connect_to_peers(self, random) -> None:
next_feeler = self._poisson_next_send(time.time() * 1000 * 1000, 240, random)
empty_tables = False
retry_introducers = False
introducer_attempts: int = 0
local_peerinfo: Optional[PeerInfo] = await self.server.get_peer_info()
last_timestamp_local_info: uint64 = uint64(int(time.time()))
first = True
if self.initial_wait > 0:
await asyncio.sleep(self.initial_wait)

Expand All @@ -182,22 +201,29 @@ async def _connect_to_peers(self, random) -> None:

# We don't know any address, connect to the introducer to get some.
size = await self.address_manager.size()
if size == 0 or empty_tables or first:
first = False
if size == 0 or retry_introducers or introducer_attempts == 0:
try:
await asyncio.sleep(introducer_backoff)
except asyncio.CancelledError:
return None
await self._introducer_client()
# there's some delay between receiving the peers from the
# introducer until they get incorporated to prevent this
# loop for running one more time. Add this delay to ensure
# that once we get peers, we stop contacting the introducer.
try:
await asyncio.sleep(5)
except asyncio.CancelledError:
return None
empty_tables = False
# Run dual between DNS servers and introducers. One time query DNS server,
# next two times query the introducer.
if introducer_attempts % 3 == 0 and len(self.dns_servers) > 0:
dns_address = random.choice(self.dns_servers)
await self._query_dns(dns_address)
else:
await self._introducer_client()
# there's some delay between receiving the peers from the
# introducer until they get incorporated to prevent this
# loop for running one more time. Add this delay to ensure
# that once we get peers, we stop contacting the introducer.
try:
await asyncio.sleep(5)
except asyncio.CancelledError:
return None

retry_introducers = False
introducer_attempts += 1
# keep doubling the introducer delay until we reach 5
# minutes
if introducer_backoff < 300:
Expand Down Expand Up @@ -251,14 +277,17 @@ async def _connect_to_peers(self, random) -> None:
while not got_peer and not self.is_closed:
sleep_interval = 1 + len(groups) * 0.5
sleep_interval = min(sleep_interval, self.peer_connect_interval)
# Special case: try to find our first peer much quicker.
if len(groups) == 0:
sleep_interval = 0.1
try:
await asyncio.sleep(sleep_interval)
except asyncio.CancelledError:
return None
tries += 1
if tries > max_tries:
addr = None
empty_tables = True
retry_introducers = True
break
info: Optional[ExtendedPeerInfo] = await self.address_manager.select_tried_collision()
if info is None:
Expand All @@ -267,7 +296,7 @@ async def _connect_to_peers(self, random) -> None:
has_collision = True
if info is None:
if not is_feeler:
empty_tables = True
retry_introducers = True
break
# Require outbound connections, other than feelers,
# to be to distinct network groups.
Expand All @@ -284,7 +313,9 @@ async def _connect_to_peers(self, random) -> None:
addr = None
continue
# only consider very recently tried nodes after 30 failed attempts
if now - info.last_try < 600 and tries < 30:
# attempt a node once per 2 hours if we lack connections to increase the chance
# to try all the peer table.
if now - info.last_try < 2 * 3600 and tries < 30:
continue
if time.time() - last_timestamp_local_info > 1800 or local_peerinfo is None:
local_peerinfo = await self.server.get_peer_info()
Expand All @@ -296,7 +327,7 @@ async def _connect_to_peers(self, random) -> None:
disconnect_after_handshake = is_feeler
if self._num_needed_peers() == 0:
disconnect_after_handshake = True
empty_tables = False
retry_introducers = False
initiate_connection = self._num_needed_peers() > 0 or has_collision or is_feeler
client_connected = False
if addr is not None and initiate_connection:
Expand All @@ -322,6 +353,9 @@ async def _connect_to_peers(self, random) -> None:

sleep_interval = 1 + len(groups) * 0.5
sleep_interval = min(sleep_interval, self.peer_connect_interval)
# Special case: try to find our first peer much quicker.
if len(groups) == 0:
sleep_interval = 0.1
await asyncio.sleep(sleep_interval)
except Exception as e:
self.log.error(f"Exception in create outbound connections: {e}")
Expand Down
4 changes: 4 additions & 0 deletions chia/util/initial-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,10 @@ full_node:
# analyze with chia/utils/profiler.py
enable_profiler: False

# List of trusted DNS seeders to bootstrap from.
# If you modify this, please change the hardcode as well from FullNode.set_server()
dns_servers:
- "seeder.fchirica.com"
farmer_peer:
host: *self_hostname
port: 8447
Expand Down
2 changes: 2 additions & 0 deletions chia/wallet/wallet_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,12 +303,14 @@ async def _messages_to_resend(self) -> List[Tuple[Message, Set[bytes32]]]:

def set_server(self, server: ChiaServer):
self.server = server
# TODO: perhaps use a different set of DNS seeders for wallets, to split the traffic.
self.wallet_peers = WalletPeers(
self.server,
self.root_path,
self.config["target_peer_count"],
self.config["wallet_peers_path"],
self.config["introducer_peer"],
[],
self.config["peer_connect_interval"],
self.log,
)
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"sortedcontainers==2.3.0", # For maintaining sorted mempools
"websockets==8.1.0", # For use in wallet RPC and electron UI
"click==7.1.2", # For the CLI
"dnspython==2.1.0", # Query DNS seeds
]

upnp_dependencies = [
Expand Down

0 comments on commit 413d290

Please sign in to comment.