private static async Task AddKnownBitcoinFullNodeAsHiddenServiceAsync(AddressManager addressManager) { if (Network == Network.RegTest) { return; } // curl -s https://bitnodes.21.co/api/v1/snapshots/latest/ | egrep -o '[a-z0-9]{16}\.onion:?[0-9]*' | sort -ru // Then filtered to include only /Satoshi:0.17.x var fullBaseDirectory = Path.GetFullPath(AppContext.BaseDirectory); if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { if (!fullBaseDirectory.StartsWith('/')) { fullBaseDirectory.Insert(0, "/"); } } var onions = await File.ReadAllLinesAsync(Path.Combine(fullBaseDirectory, "OnionSeeds", $"{Network}OnionSeeds.txt")); onions.Shuffle(); foreach (var onion in onions.Take(60)) { if (NBitcoin.Utils.TryParseEndpoint(onion, Network.DefaultPort, out var endpoint)) { await addressManager.AddAsync(endpoint); } } }
private async Task LoadGroup() { AddressManager manager = new AddressManager(); await manager.AddAsync(_ChainConfiguration.NodeEndpoint); NodesGroup group = new NodesGroup(_Network.NBitcoinNetwork, new NodeConnectionParameters() { Services = NodeServices.Nothing, IsRelay = true, TemplateBehaviors = { new AddressManagerBehavior(manager) { PeersToDiscover = 1, Mode = AddressManagerBehaviorMode.None }, new ExplorerBehavior(_Repository, _Chain, _AddressPoolService, _EventAggregator) { StartHeight = _ChainConfiguration.StartHeight }, new SlimChainBehavior(_Chain), new PingPongBehavior() } }); group.AllowSameGroup = true; group.MaximumNodeConnection = 1; var task = WaitConnected(group); group.Connect(); try { await task; } catch (Exception ex) { Logs.Configuration.LogError(ex, $"{_Network.CryptoCode}: Failure to connect to the bitcoin node (P2P)"); throw; } _Group = group; group.ConnectedNodes.Added += ConnectedNodes_Changed; group.ConnectedNodes.Removed += ConnectedNodes_Changed; // !Hack. ExplorerBehavior.AttachCore is async and calling the repository. // Because the repository is single thread, calling a ping make sure that AttachCore // has fully ran. // Without this hack, NBXplorer takes sometimes 1 min to go from Synching to Ready state await _Repository.Ping(); }
private async Task AddKnownBitcoinFullNodeAsHiddenServiceAsync(AddressManager addressManager) { if (Network == Network.RegTest) { return; } // curl -s https://bitnodes.21.co/api/v1/snapshots/latest/ | egrep -o '[a-z0-9]{16}\.onion:?[0-9]*' | sort -ru // Then filtered to include only /Satoshi:0.17.x var fullBaseDirectory = EnvironmentHelpers.GetFullBaseDirectory(); var onions = await File.ReadAllLinesAsync(Path.Combine(fullBaseDirectory, "OnionSeeds", $"{Network}OnionSeeds.txt")); onions.Shuffle(); foreach (var onion in onions.Take(60)) { if (Utils.TryParseEndpoint(onion, Network.DefaultPort, out var endpoint)) { await addressManager.AddAsync(endpoint); } } }
public void Listen(ConcurrentChain chain = null) { ListenerTrace.Info($"Connecting to node {_Configuration.Indexer.Node}"); var ip = Utils.ParseEndpoint(string.IsNullOrWhiteSpace(_Configuration.Indexer.Node) ? "127.0.0.1" : _Configuration.Indexer.Node, Configuration.Indexer.Network.DefaultPort); ListenerTrace.Info($"Connecting to node ip {ip.ToEndpointString()}"); var node = Node.Connect(Configuration.Indexer.Network, ip); ListenerTrace.Info($"Connected, trying handshake..."); node.VersionHandshake(); ListenerTrace.Info($"Hanshaked"); node.Disconnect(); _Chain = new ConcurrentChain(_Configuration.Indexer.Network); _Indexer = Configuration.Indexer.CreateIndexer(); if (chain == null) { chain = new ConcurrentChain(_Configuration.Indexer.Network); } _Chain = chain; ListenerTrace.Info("Fetching headers from " + _Chain.Tip.Height + " (from azure)"); var client = Configuration.Indexer.CreateIndexerClient(); client.SynchronizeChain(chain, default(CancellationToken)).GetAwaiter().GetResult(); ListenerTrace.Info("Headers fetched tip " + _Chain.Tip.Height); _Disposables.Add(_IndexerScheduler = new CustomThreadPoolTaskScheduler(50, 100, "Indexing Threads")); _Indexer.TaskScheduler = _IndexerScheduler; _Group = new NodesGroup(Configuration.Indexer.Network); _Disposables.Add(_Group); _Group.AllowSameGroup = true; _Group.MaximumNodeConnection = 2; AddressManager addrman = new AddressManager(); addrman.AddAsync(ip, IPAddress.Parse("127.0.0.1")).GetAwaiter().GetResult(); _Group.NodeConnectionParameters.TemplateBehaviors.Add(new AddressManagerBehavior(addrman) { Mode = AddressManagerBehaviorMode.None }); _Group.NodeConnectionParameters.TemplateBehaviors.Add(new ChainBehavior(_Chain) { SkipPoWCheck = true }); _Group.NodeConnectionParameters.TemplateBehaviors.Add(new Behavior(this)); ListenerTrace.Info("Fetching wallet rules..."); _Wallets = _Configuration.Indexer.CreateIndexerClient().GetAllWalletRules(); ListenerTrace.Info("Wallet rules fetched"); ListenerTrace.Info("Fetching wallet subscriptions..."); _Subscriptions = new SubscriptionCollection(_Configuration.GetSubscriptionsTable().Read()); ListenerTrace.Info("Subscriptions fetched"); _Group.Connect(); ListenerTrace.Info("Fetching transactions to broadcast..."); _Disposables.Add( Configuration .Topics .BroadcastedTransactions .CreateConsumer("listener", true) .EnsureSubscriptionExists() .OnMessage((tx, ctl) => { uint256 hash = null; var repo = Configuration.Indexer.CreateIndexerClient(); var rejects = Configuration.GetRejectTable(); try { hash = tx.Transaction.GetHash(); var indexedTx = repo.GetTransaction(hash); ListenerTrace.Info("Broadcasting " + hash); var reject = rejects.ReadOne(hash.ToString()); if (reject != null) { ListenerTrace.Info("Abort broadcasting of rejected"); return; } if (_Broadcasting.Count > 1000) { _Broadcasting.Clear(); } _Broadcasting.TryAdd(hash, tx.Transaction); if (indexedTx == null || !indexedTx.BlockIds.Any(id => Chain.Contains(id))) { var unused = SendMessageAsync(tx.Transaction); } var reschedule = new[] { TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(10), TimeSpan.FromHours(1), TimeSpan.FromHours(6), TimeSpan.FromHours(24), }; if (tx.Tried <= reschedule.Length - 1) { ctl.RescheduleIn(reschedule[tx.Tried]); tx.Tried++; } } catch (Exception ex) { if (!_Disposed) { LastException = ex; ListenerTrace.Error("Error for new broadcasted transaction " + hash, ex); throw; } } })); ListenerTrace.Info("Transactions to broadcast fetched"); _Disposables.Add(_Configuration .Topics .SubscriptionChanges .EnsureSubscriptionExists() .AddUnhandledExceptionHandler(ExceptionOnMessagePump) .OnMessage(c => { using (_SubscriptionSlimLock.LockWrite()) { if (c.Added) { _Subscriptions.Add(c.Subscription); } else { _Subscriptions.Remove(c.Subscription.Id); } } })); _Disposables.Add(_Configuration .Topics .SendNotifications .AddUnhandledExceptionHandler(ExceptionOnMessagePump) .OnMessageAsync((n, act) => { return(SendAsync(n, act).ContinueWith(t => { if (!_Disposed) { if (t.Exception != null) { LastException = t.Exception; } } })); }, new OnMessageOptions() { MaxConcurrentCalls = 1000, AutoComplete = true, AutoRenewTimeout = TimeSpan.Zero })); _Disposables.Add(Configuration .Topics .AddedAddresses .CreateConsumer("updater", true) .EnsureSubscriptionExists() .AddUnhandledExceptionHandler(ExceptionOnMessagePump) .OnMessage(evt => { if (evt == null) { return; } ListenerTrace.Info("New wallet rule"); using (_WalletsSlimLock.LockWrite()) { foreach (var address in evt) { _Wallets.Add(address.CreateWalletRuleEntry(Network)); } } })); }