private async Task TestNodesConnectivityAndVersionAsync() { logger.LogInformation($"Checking nodes connectivity"); var nodes = nodeRepository.GetNodes(); if (!nodes.Any()) { logger.LogWarning("There are no nodes present in database."); } foreach (var node in nodes) { var rpcClient = rpcClientFactory.Create(node.Host, node.Port, node.Username, node.Password); rpcClient.RequestTimeout = TimeSpan.FromSeconds(3); rpcClient.NumOfRetries = 10; try { var networkInfo = await rpcClient.GetNetworkInfoAsync(); if (!Nodes.IsNodeVersionValid(networkInfo.Version, out string error)) { logger.LogError(error); } accessibleNodes.Add(node); nodesAccessible = true; } catch (Exception) { logger.LogWarning($"Node at address '{node.Host}:{node.Port}' is unreachable"); } } logger.LogInformation($"Nodes connectivity check complete"); }
/// <summary> /// Call activezmqnotifications on nodes that haven't posted any events for a given period of time (ZmqConnectionTestIntervalSec config setting). /// </summary> private async Task PingNodesAsync(CancellationToken stoppingToken) { var nodesToPing = subscriptions .Where(s => (clock.UtcNow() - s.Value.LastContactAt).TotalSeconds >= appSettings.ZmqConnectionTestIntervalSec) .Select(s => s.Value.NodeId) .Distinct() .ToArray(); if (nodesToPing.Any()) { var nodes = nodeRepository.GetNodes().ToArray(); foreach (long nodeId in nodesToPing) { var node = nodes.FirstOrDefault(n => n.Id == nodeId); if (node == null) { continue; } var bitcoind = bitcoindFactory.Create(node.Host, node.Port, node.Username, node.Password); bitcoind.RequestTimeout = TimeSpan.FromSeconds(RPC_RESPONSE_TIMEOUT_SECONDS); try { // Call activezmqnotifications rpc method just to check that node is still responding. // No validation of the response is made here. var notifications = await bitcoind.ActiveZmqNotificationsAsync(stoppingToken); // If we got answer than update last message timestamp on all subscriptions of this node if (notifications.Any()) { foreach (var subscription in subscriptions.Where(s => s.Value.NodeId == node.Id).ToArray()) { subscription.Value.LastPingAt = clock.UtcNow(); } } } catch (Exception ex) { // Call failed so put node on failed list MarkAsFailed(node, ex.Message); // Remove any active subscriptions because we need full reconnection to this node UnsubscribeZmqNotifications(node); logger.LogError(ex, $"Ping failed for node {node.Host}:{node.Port}. Will try to resubscribe."); } } } }
public async Task <Node> CreateNodeAsync(Node node) { logger.LogInformation($"Adding node {node}"); // Try to connect to node var bitcoind = bitcoindFactory.Create(node.Host, node.Port, node.Username, node.Password); try { // try to call some method to test if connectivity parameters are correct _ = await bitcoind.GetBlockCountAsync(); } catch (Exception ex) { throw new BadRequestException($"The node was not added. Unable to connect to node {node.Host}:{node.Port}.", ex); } RpcActiveZmqNotification[] notifications; try { notifications = await bitcoind.ActiveZmqNotificationsAsync(); } catch (Exception ex) { throw new BadRequestException($"Node at address '{node.Host}:{node.Port}' did not return a valid response to call 'activeZmqNotifications'", ex); } if (!notifications.Any() || notifications.Select(x => x.Notification).Intersect(ZMQTopic.RequiredZmqTopics).Count() != ZMQTopic.RequiredZmqTopics.Length) { var missingNotifications = ZMQTopic.RequiredZmqTopics.Except(notifications.Select(x => x.Notification)); throw new BadRequestException($"Node '{node.Host}:{node.Port}', does not have all required zmq notifications enabled. Missing notifications ({string.Join(",", missingNotifications)})"); } var createdNode = nodeRepository.CreateNode(node); eventBus.Publish(new NodeAddedEvent() { CreationDate = clock.UtcNow(), CreatedNode = createdNode }); return(createdNode); }
IRpcClient[] GetRpcClients() { var result = nodes.GetNodes().Select( n => rpcClientFactory.Create(n.Host, n.Port, n.Username, n.Password)).ToArray(); if (!result.Any()) { throw new BadRequestException("No nodes available"); } return(result); }
private async Task ValidateNode(Node node, bool isUpdate = false) { // Try to connect to node var bitcoind = bitcoindFactory.Create(node.Host, node.Port, node.Username, node.Password); try { // try to call some method to test if connectivity parameters are correct var networkInfo = await bitcoind.GetNetworkInfoAsync(); if (!Nodes.IsNodeVersionValid(networkInfo.Version, out string versionError)) { throw new BadRequestException(versionError); } } catch (Exception ex) { throw new BadRequestException($"The node was not { (isUpdate ? "updated" : "added") }. Unable to connect to node {node.Host}:{node.Port}.", ex); } RpcActiveZmqNotification[] notifications; try { notifications = await bitcoind.ActiveZmqNotificationsAsync(); } catch (Exception ex) { throw new BadRequestException($"Node at address '{node.Host}:{node.Port}' did not return a valid response to call 'activeZmqNotifications'", ex); } if (!IsZMQNotificationsEndpointValid(node, notifications, out string error)) { throw new BadRequestException(error); } if (!notifications.Any() || notifications.Select(x => x.Notification).Intersect(ZMQTopic.RequiredZmqTopics).Count() != ZMQTopic.RequiredZmqTopics.Length) { var missingNotifications = ZMQTopic.RequiredZmqTopics.Except(notifications.Select(x => x.Notification)); throw new BadRequestException($"Node '{node.Host}:{node.Port}', does not have all required zmq notifications enabled. Missing notifications ({string.Join(",", missingNotifications)})"); } }